1#
2# tk/menu.rb : treat menu and menubutton
3#
4require 'tk'
5require 'tk/itemconfig'
6require 'tk/menuspec'
7
8module TkMenuEntryConfig
9  include TkItemConfigMethod
10
11  def __item_cget_cmd(id)
12    [self.path, 'entrycget', id]
13  end
14  private :__item_cget_cmd
15
16  def __item_config_cmd(id)
17    [self.path, 'entryconfigure', id]
18  end
19  private :__item_config_cmd
20
21  def __item_strval_optkeys(id)
22    super(id) << 'selectcolor'
23  end
24  private :__item_strval_optkeys
25
26  def __item_listval_optkeys(id)
27    []
28  end
29  private :__item_listval_optkeys
30
31  def __item_val2ruby_optkeys(id)  # { key=>proc, ... }
32    super(id).update('menu'=>proc{|i, v| window(v)})
33  end
34  private :__item_val2ruby_optkeys
35
36  alias entrycget_tkstring itemcget_tkstring
37  alias entrycget itemcget
38  alias entrycget_strict itemcget_strict
39  alias entryconfigure itemconfigure
40  alias entryconfiginfo itemconfiginfo
41  alias current_entryconfiginfo current_itemconfiginfo
42
43  private :itemcget_tkstring, :itemcget, :itemcget_strict
44  private :itemconfigure, :itemconfiginfo, :current_itemconfiginfo
45end
46
47class Tk::Menu<TkWindow
48  include Wm
49  include TkMenuEntryConfig
50  extend TkMenuSpec
51
52  TkCommandNames = ['menu'.freeze].freeze
53  WidgetClassName = 'Menu'.freeze
54  WidgetClassNames[WidgetClassName] ||= self
55
56  #def create_self(keys)
57  #  if keys and keys != None
58  #    tk_call_without_enc('menu', @path, *hash_kv(keys, true))
59  #  else
60  #    tk_call_without_enc('menu', @path)
61  #  end
62  #end
63  #private :create_self
64
65  def __strval_optkeys
66    super() << 'selectcolor' << 'title'
67  end
68  private :__strval_optkeys
69
70  def __boolval_optkeys
71    super() << 'tearoff'
72  end
73  private :__boolval_optkeys
74
75  def self.new_menuspec(menu_spec, parent = nil, tearoff = false, keys = nil)
76    if parent.kind_of?(Hash)
77      keys = _symbolkey2str(parent)
78      parent = keys.delete('parent')
79      tearoff = keys.delete('tearoff')
80    elsif tearoff.kind_of?(Hash)
81      keys = _symbolkey2str(tearoff)
82      tearoff = keys.delete('tearoff')
83    elsif keys
84      keys = _symbolkey2str(keys)
85    else
86      keys = {}
87    end
88
89    widgetname = keys.delete('widgetname')
90    _create_menu(parent, menu_spec, widgetname, tearoff, keys)
91  end
92
93  def tagid(id)
94    #id.to_s
95    _get_eval_string(id)
96  end
97
98  def activate(index)
99    tk_send_without_enc('activate', _get_eval_enc_str(index))
100    self
101  end
102  def add(type, keys=nil)
103    tk_send_without_enc('add', type, *hash_kv(keys, true))
104    self
105  end
106  def add_cascade(keys=nil)
107    add('cascade', keys)
108  end
109  def add_checkbutton(keys=nil)
110    add('checkbutton', keys)
111  end
112  def add_command(keys=nil)
113    add('command', keys)
114  end
115  def add_radiobutton(keys=nil)
116    add('radiobutton', keys)
117  end
118  def add_separator(keys=nil)
119    add('separator', keys)
120  end
121
122  def clone_menu(*args)
123    if args[0].kind_of?(TkWindow)
124      parent = args.shift
125    else
126      parent = self
127    end
128
129    if args[0].kind_of?(String) || args[0].kind_of?(Symbol) # menu type
130      type = args.shift
131    else
132      type = None # 'normal'
133    end
134
135    if args[0].kind_of?(Hash)
136      keys = _symbolkey2str(args.shift)
137    else
138      keys = {}
139    end
140
141    parent = keys.delete('parent') if keys.has_key?('parent')
142    type = keys.delete('type') if keys.has_key?('type')
143
144    if keys.empty?
145      Tk::MenuClone.new(self, parent, type)
146    else
147      Tk::MenuClone.new(self, parent, type, keys)
148    end
149  end
150
151  def index(idx)
152    ret = tk_send_without_enc('index', _get_eval_enc_str(idx))
153    (ret == 'none')? nil: number(ret)
154  end
155  def invoke(index)
156    _fromUTF8(tk_send_without_enc('invoke', _get_eval_enc_str(index)))
157  end
158  def insert(index, type, keys=nil)
159    tk_send_without_enc('insert', _get_eval_enc_str(index),
160                        type, *hash_kv(keys, true))
161    self
162  end
163  def delete(first, last=nil)
164    if last
165      tk_send_without_enc('delete', _get_eval_enc_str(first),
166                          _get_eval_enc_str(last))
167    else
168      tk_send_without_enc('delete', _get_eval_enc_str(first))
169    end
170    self
171  end
172  def popup(x, y, index=nil)
173    if index
174      tk_call_without_enc('tk_popup', path, x, y,
175                          _get_eval_enc_str(index))
176    else
177      tk_call_without_enc('tk_popup', path, x, y)
178    end
179    self
180  end
181  def post(x, y)
182    _fromUTF8(tk_send_without_enc('post', x, y))
183  end
184  def postcascade(index)
185    tk_send_without_enc('postcascade', _get_eval_enc_str(index))
186    self
187  end
188  def postcommand(cmd=Proc.new)
189    configure_cmd 'postcommand', cmd
190    self
191  end
192  def set_focus
193    tk_call_without_enc('tk_menuSetFocus', path)
194    self
195  end
196  def tearoffcommand(cmd=Proc.new)
197    configure_cmd 'tearoffcommand', cmd
198    self
199  end
200  def menutype(index)
201    tk_send_without_enc('type', _get_eval_enc_str(index))
202  end
203  def unpost
204    tk_send_without_enc('unpost')
205    self
206  end
207  def xposition(index)
208    number(tk_send_without_enc('xposition', _get_eval_enc_str(index)))
209  end
210  def yposition(index)
211    number(tk_send_without_enc('yposition', _get_eval_enc_str(index)))
212  end
213
214=begin
215  def entrycget(index, key)
216    case key.to_s
217    when 'text', 'label', 'show'
218      _fromUTF8(tk_send_without_enc('entrycget',
219                                    _get_eval_enc_str(index), "-#{key}"))
220    when 'font', 'kanjifont'
221      #fnt = tk_tcl2ruby(tk_send('entrycget', index, "-#{key}"))
222      fnt = tk_tcl2ruby(_fromUTF8(tk_send_without_enc('entrycget', _get_eval_enc_str(index), '-font')))
223      unless fnt.kind_of?(TkFont)
224        fnt = tagfontobj(index, fnt)
225      end
226      if key.to_s == 'kanjifont' && JAPANIZED_TK && TK_VERSION =~ /^4\.*/
227        # obsolete; just for compatibility
228        fnt.kanji_font
229      else
230        fnt
231      end
232    else
233      tk_tcl2ruby(_fromUTF8(tk_send_without_enc('entrycget', _get_eval_enc_str(index), "-#{key}")))
234    end
235  end
236  def entryconfigure(index, key, val=None)
237    if key.kind_of? Hash
238      if (key['font'] || key[:font] ||
239          key['kanjifont'] || key[:kanjifont] ||
240          key['latinfont'] || key[:latinfont] ||
241          key['asciifont'] || key[:asciifont])
242        tagfont_configure(index, _symbolkey2str(key))
243      else
244        tk_send_without_enc('entryconfigure', _get_eval_enc_str(index),
245                            *hash_kv(key, true))
246      end
247
248    else
249      if (key == 'font' || key == :font ||
250          key == 'kanjifont' || key == :kanjifont ||
251          key == 'latinfont' || key == :latinfont ||
252          key == 'asciifont' || key == :asciifont )
253        if val == None
254          tagfontobj(index)
255        else
256          tagfont_configure(index, {key=>val})
257        end
258      else
259        tk_call('entryconfigure', index, "-#{key}", val)
260      end
261    end
262    self
263  end
264
265  def entryconfiginfo(index, key=nil)
266    if TkComm::GET_CONFIGINFO_AS_ARRAY
267      if key
268        case key.to_s
269        when 'text', 'label', 'show'
270          conf = tk_split_simplelist(_fromUTF8(tk_send_without_enc('entryconfigure',_get_eval_enc_str(index),"-#{key}")))
271        when 'font', 'kanjifont'
272          conf = tk_split_simplelist(_fromUTF8(tk_send_without_enc('entryconfigure',_get_eval_enc_str(index),"-#{key}")))
273          conf[4] = tagfont_configinfo(index, conf[4])
274        else
275          conf = tk_split_list(_fromUTF8(tk_send_without_enc('entryconfigure',_get_eval_enc_str(index),"-#{key}")))
276        end
277        conf[0] = conf[0][1..-1]
278        conf
279      else
280        ret = tk_split_simplelist(_fromUTF8(tk_send_without_enc('entryconfigure', _get_eval_enc_str(index)))).collect{|conflist|
281          conf = tk_split_simplelist(conflist)
282          conf[0] = conf[0][1..-1]
283          case conf[0]
284          when 'text', 'label', 'show'
285          else
286            if conf[3]
287              if conf[3].index('{')
288                conf[3] = tk_split_list(conf[3])
289              else
290                conf[3] = tk_tcl2ruby(conf[3])
291              end
292            end
293            if conf[4]
294              if conf[4].index('{')
295                conf[4] = tk_split_list(conf[4])
296              else
297                conf[4] = tk_tcl2ruby(conf[4])
298              end
299            end
300          end
301          conf[1] = conf[1][1..-1] if conf.size == 2 # alias info
302          conf
303        }
304        if fontconf
305          ret.delete_if{|item| item[0] == 'font' || item[0] == 'kanjifont'}
306          fontconf[4] = tagfont_configinfo(index, fontconf[4])
307          ret.push(fontconf)
308        else
309          ret
310        end
311      end
312    else # ! TkComm::GET_CONFIGINFO_AS_ARRAY
313      if key
314        case key.to_s
315        when 'text', 'label', 'show'
316          conf = tk_split_simplelist(_fromUTF8(tk_send_without_enc('entryconfigure',_get_eval_enc_str(index),"-#{key}")))
317        when 'font', 'kanjifont'
318          conf = tk_split_simplelist(_fromUTF8(tk_send_without_enc('entryconfigure',_get_eval_enc_str(index),"-#{key}")))
319          conf[4] = tagfont_configinfo(index, conf[4])
320        else
321          conf = tk_split_list(_fromUTF8(tk_send_without_enc('entryconfigure',_get_eval_enc_str(index),"-#{key}")))
322        end
323        key = conf.shift[1..-1]
324        { key => conf }
325      else
326        ret = {}
327        tk_split_simplelist(_fromUTF8(tk_send_without_enc('entryconfigure', _get_eval_enc_str(index)))).each{|conflist|
328          conf = tk_split_simplelist(conflist)
329          key = conf.shift[1..-1]
330          case key
331          when 'text', 'label', 'show'
332          else
333            if conf[2]
334              if conf[2].index('{')
335                conf[2] = tk_split_list(conf[2])
336              else
337                conf[2] = tk_tcl2ruby(conf[2])
338              end
339            end
340            if conf[3]
341              if conf[3].index('{')
342                conf[3] = tk_split_list(conf[3])
343              else
344                conf[3] = tk_tcl2ruby(conf[3])
345              end
346            end
347          end
348          if conf.size == 1
349            ret[key] = conf[0][1..-1]  # alias info
350          else
351            ret[key] = conf
352          end
353        }
354        fontconf = ret['font']
355        if fontconf
356          ret.delete('font')
357          ret.delete('kanjifont')
358          fontconf[3] = tagfont_configinfo(index, fontconf[3])
359          ret['font'] = fontconf
360        end
361        ret
362      end
363    end
364  end
365
366  def current_entryconfiginfo(index, key=nil)
367    if TkComm::GET_CONFIGINFO_AS_ARRAY
368      if key
369        conf = entryconfiginfo(index, key)
370        {conf[0] => conf[4]}
371      else
372        ret = {}
373        entryconfiginfo(index).each{|conf|
374          ret[conf[0]] = conf[4] if conf.size > 2
375        }
376        ret
377      end
378    else # ! TkComm::GET_CONFIGINFO_AS_ARRAY
379      ret = {}
380      entryconfiginfo(index, key).each{|k, conf|
381        ret[k] = conf[-1] if conf.kind_of?(Array)
382      }
383      ret
384    end
385  end
386=end
387end
388
389#TkMenu = Tk::Menu unless Object.const_defined? :TkMenu
390#Tk.__set_toplevel_aliases__(:Tk, Tk::Menu, :TkMenu)
391Tk.__set_loaded_toplevel_aliases__('tk/menu.rb', :Tk, Tk::Menu, :TkMenu)
392
393
394module Tk::Menu::TkInternalFunction; end
395class << Tk::Menu::TkInternalFunction
396  # These methods calls internal functions of Tcl/Tk.
397  # So, They may not work on your Tcl/Tk.
398  def next_menu(menu, dir='next')
399    dir = dir.to_s
400    case dir
401    when 'next', 'forward', 'down'
402      dir = 'right'
403    when 'previous', 'backward', 'up'
404      dir = 'left'
405    end
406
407    Tk.tk_call('::tk::MenuNextMenu', menu, dir)
408  end
409
410  def next_entry(menu, delta)
411    # delta is increment value of entry index.
412    # For example, +1 denotes 'next entry' and -1 denotes 'previous entry'.
413    Tk.tk_call('::tk::MenuNextEntry', menu, delta)
414  end
415end
416
417class Tk::MenuClone<Tk::Menu
418=begin
419  def initialize(parent, type=None)
420    widgetname = nil
421    if parent.kind_of? Hash
422      keys = _symbolkey2str(parent)
423      parent = keys.delete('parent')
424      widgetname = keys.delete('widgetname')
425      type = keys.delete('type'); type = None unless type
426    end
427    #unless parent.kind_of?(TkMenu)
428    #  fail ArgumentError, "parent must be TkMenu"
429    #end
430    @parent = parent
431    install_win(@parent.path, widgetname)
432    tk_call_without_enc(@parent.path, 'clone', @path, type)
433  end
434=end
435  def initialize(src_menu, *args)
436    widgetname = nil
437
438    if args[0].kind_of?(TkWindow)  # parent window
439      parent = args.shift
440    else
441      parent = src_menu
442    end
443
444    if args[0].kind_of?(String) || args[0].kind_of?(Symbol)  # menu type
445      type = args.shift
446    else
447      type = None  # 'normal'
448    end
449
450    if args[0].kind_of?(Hash)
451      keys = _symbolkey2str(args.shift)
452      parent = keys.delete('parent') if keys.has_key?('parent')
453      widgetname = keys.delete('widgetname')
454      type = keys.delete('type') if keys.has_key?('type')
455    else
456      keys = nil
457    end
458
459    @src_menu = src_menu
460    @parent = parent
461    @type = type
462    install_win(@parent.path, widgetname)
463    tk_call_without_enc(@src_menu.path, 'clone', @path, @type)
464    configure(keys) if keys && !keys.empty?
465  end
466
467  def source_menu
468    @src_menu
469  end
470end
471Tk::CloneMenu = Tk::MenuClone
472#TkMenuClone = Tk::MenuClone unless Object.const_defined? :TkMenuClone
473#TkCloneMenu = Tk::CloneMenu unless Object.const_defined? :TkCloneMenu
474#Tk.__set_toplevel_aliases__(:Tk, Tk::MenuClone, :TkMenuClone, :TkCloneMenu)
475Tk.__set_loaded_toplevel_aliases__('tk/menu.rb', :Tk, Tk::MenuClone,
476                                   :TkMenuClone, :TkCloneMenu)
477
478module Tk::SystemMenu
479  def initialize(parent, keys=nil)
480    if parent.kind_of? Hash
481      keys = _symbolkey2str(parent)
482      parent = keys.delete('parent')
483    end
484    #unless parent.kind_of? TkMenu
485    #  fail ArgumentError, "parent must be a TkMenu object"
486    #end
487    # @path = Kernel.format("%s.%s", parent.path, self.class::SYSMENU_NAME)
488    @path = parent.path + '.' + self.class::SYSMENU_NAME
489    #TkComm::Tk_WINDOWS[@path] = self
490    TkCore::INTERP.tk_windows[@path] = self
491    if self.method(:create_self).arity == 0
492      p 'create_self has no arg' if $DEBUG
493      create_self
494      configure(keys) if keys
495    else
496      p 'create_self has an arg' if $DEBUG
497      create_self(keys)
498    end
499  end
500end
501TkSystemMenu = Tk::SystemMenu
502
503
504class Tk::SysMenu_Help<Tk::Menu
505  # for all platform
506  include Tk::SystemMenu
507  SYSMENU_NAME = 'help'
508end
509#TkSysMenu_Help = Tk::SysMenu_Help unless Object.const_defined? :TkSysMenu_Help
510#Tk.__set_toplevel_aliases__(:Tk, Tk::SysMenu_Help, :TkSysMenu_Help)
511Tk.__set_loaded_toplevel_aliases__('tk/menu.rb', :Tk, Tk::SysMenu_Help,
512                                   :TkSysMenu_Help)
513
514
515class Tk::SysMenu_System<Tk::Menu
516  # for Windows
517  include Tk::SystemMenu
518  SYSMENU_NAME = 'system'
519end
520#TkSysMenu_System = Tk::SysMenu_System unless Object.const_defined? :TkSysMenu_System
521#Tk.__set_toplevel_aliases__(:Tk, Tk::SysMenu_System, :TkSysMenu_System)
522Tk.__set_loaded_toplevel_aliases__('tk/menu.rb', :Tk, Tk::SysMenu_System,
523                                   :TkSysMenu_System)
524
525
526class Tk::SysMenu_Apple<Tk::Menu
527  # for Machintosh
528  include Tk::SystemMenu
529  SYSMENU_NAME = 'apple'
530end
531#TkSysMenu_Apple = Tk::SysMenu_Apple unless Object.const_defined? :TkSysMenu_Apple
532#Tk.__set_toplevel_aliases__(:Tk, Tk::SysMenu_Apple, :TkSysMenu_Apple)
533Tk.__set_loaded_toplevel_aliases__('tk/menu.rb', :Tk, Tk::SysMenu_Apple,
534                                   :TkSysMenu_Apple)
535
536
537class Tk::Menubutton<Tk::Label
538  TkCommandNames = ['menubutton'.freeze].freeze
539  WidgetClassName = 'Menubutton'.freeze
540  WidgetClassNames[WidgetClassName] ||= self
541  def create_self(keys)
542    if keys and keys != None
543      unless TkConfigMethod.__IGNORE_UNKNOWN_CONFIGURE_OPTION__
544        # tk_call_without_enc('menubutton', @path, *hash_kv(keys, true))
545        tk_call_without_enc(self.class::TkCommandNames[0], @path,
546                            *hash_kv(keys, true))
547      else
548        begin
549          tk_call_without_enc(self.class::TkCommandNames[0], @path,
550                              *hash_kv(keys, true))
551        rescue
552          tk_call_without_enc(self.class::TkCommandNames[0], @path)
553          keys = __check_available_configure_options(keys)
554          unless keys.empty?
555            tk_call_without_enc('destroy', @path) rescue nil
556            tk_call_without_enc(self.class::TkCommandNames[0], @path,
557                                *hash_kv(keys, true))
558          end
559        end
560      end
561    else
562      # tk_call_without_enc('menubutton', @path)
563      tk_call_without_enc(self.class::TkCommandNames[0], @path)
564    end
565  end
566  private :create_self
567
568  def __boolval_optkeys
569    super() << 'indicatoron'
570  end
571  private :__boolval_optkeys
572
573end
574Tk::MenuButton = Tk::Menubutton
575#TkMenubutton = Tk::Menubutton unless Object.const_defined? :TkMenubutton
576#TkMenuButton = Tk::MenuButton unless Object.const_defined? :TkMenuButton
577#Tk.__set_toplevel_aliases__(:Tk, Tk::Menubutton, :TkMenubutton, :TkMenuButton)
578Tk.__set_loaded_toplevel_aliases__('tk/menu.rb', :Tk, Tk::Menubutton,
579                                   :TkMenubutton, :TkMenuButton)
580
581
582class Tk::OptionMenubutton<Tk::Menubutton
583  TkCommandNames = ['tk_optionMenu'.freeze].freeze
584
585  class OptionMenu<TkMenu
586    def initialize(path)  #==> return value of tk_optionMenu
587      @path = path
588      #TkComm::Tk_WINDOWS[@path] = self
589      TkCore::INTERP.tk_windows[@path] = self
590    end
591  end
592
593  def initialize(*args)
594    # args :: [parent,] [var,] [value[, ...],] [keys]
595    #    parent --> TkWindow or nil
596    #    var    --> TkVariable or nil
597    #    keys   --> Hash
598    #       keys[:parent] or keys['parent']     --> parent
599    #       keys[:variable] or keys['variable'] --> var
600    #       keys[:values] or keys['values']     --> value, ...
601    #       other Hash keys are menubutton options
602    keys = {}
603    keys = args.pop if args[-1].kind_of?(Hash)
604    keys = _symbolkey2str(keys)
605
606    parent = nil
607    if !args.empty? && (args[0].kind_of?(TkWindow) || args[0] == nil)
608      keys.delete('parent') # ignore
609      parent = args.shift
610    else
611      parent = keys.delete('parent')
612    end
613
614    @variable = nil
615    if !args.empty? && (args[0].kind_of?(TkVariable) || args[0] == nil)
616      keys.delete('variable') # ignore
617      @variable = args.shift
618    else
619      @variable = keys.delete('variable')
620    end
621    @variable = TkVariable.new unless @variable
622
623    (args = keys.delete('values') || []) if args.empty?
624    if args.empty?
625      args << @variable.value
626    else
627      @variable.value = args[0]
628    end
629
630    install_win(if parent then parent.path end)
631    @menu = OptionMenu.new(tk_call('tk_optionMenu',
632                                   @path, @variable.id, *args))
633
634    configure(keys) if keys
635  end
636
637  def value
638    @variable.value
639  end
640
641  def value=(val)
642    @variable.value = val
643  end
644
645  def activate(index)
646    @menu.activate(index)
647    self
648  end
649  def add(value)
650    @menu.add('radiobutton', 'variable'=>@variable,
651              'label'=>value, 'value'=>value)
652    self
653  end
654  def index(index)
655    @menu.index(index)
656  end
657  def invoke(index)
658    @menu.invoke(index)
659  end
660  def insert(index, value)
661    @menu.insert(index, 'radiobutton', 'variable'=>@variable,
662              'label'=>value, 'value'=>value)
663    self
664  end
665  def delete(index, last=None)
666    @menu.delete(index, last)
667    self
668  end
669  def xposition(index)
670    @menu.xposition(index)
671  end
672  def yposition(index)
673    @menu.yposition(index)
674  end
675  def menu
676    @menu
677  end
678  def menucget(key)
679    @menu.cget(key)
680  end
681  def menucget_strict(key)
682    @menu.cget_strict(key)
683  end
684  def menuconfigure(key, val=None)
685    @menu.configure(key, val)
686    self
687  end
688  def menuconfiginfo(key=nil)
689    @menu.configinfo(key)
690  end
691  def current_menuconfiginfo(key=nil)
692    @menu.current_configinfo(key)
693  end
694  def entrycget(index, key)
695    @menu.entrycget(index, key)
696  end
697  def entrycget_strict(index, key)
698    @menu.entrycget_strict(index, key)
699  end
700  def entryconfigure(index, key, val=None)
701    @menu.entryconfigure(index, key, val)
702    self
703  end
704  def entryconfiginfo(index, key=nil)
705    @menu.entryconfiginfo(index, key)
706  end
707  def current_entryconfiginfo(index, key=nil)
708    @menu.current_entryconfiginfo(index, key)
709  end
710end
711
712Tk::OptionMenuButton = Tk::OptionMenubutton
713#TkOptionMenubutton = Tk::OptionMenubutton unless Object.const_defined? :TkOptionMenubutton
714#TkOptionMenuButton = Tk::OptionMenuButton unless Object.const_defined? :TkOptionMenuButton
715#Tk.__set_toplevel_aliases__(:Tk, Tk::OptionMenubutton,
716#                            :TkOptionMenubutton, :TkOptionMenuButton)
717Tk.__set_loaded_toplevel_aliases__('tk/menu.rb', :Tk, Tk::OptionMenubutton,
718                                   :TkOptionMenubutton, :TkOptionMenuButton)
719