3 # Hidethoshi NAGAI (nagai@ai.kyutech.ac.jp)
5 # based on tkmenubar.rb :
6 # Copyright (C) 1998 maeda shugo. All rights reserved.
7 # This file can be distributed under the terms of the Ruby.
9 # The format of the menu_spec is:
10 # [ menu_info, menu_info, ... ]
12 # And the format of the menu_info is:
14 # [text, underline, configs], # menu button/entry (*1)
15 # [label, command, underline, accelerator, configs], # command entry
16 # [label, TkVar_obj, underline, accelerator, configs], # checkbutton entry
17 # [label, [TkVar_obj, value],
18 # underline, accelerator, configs], # radiobutton entry
19 # [label, [[...menu_info...], [...menu_info...], ...],
20 # underline, accelerator, configs], # cascade entry (*2)
25 # underline, accelerator, and configs are optional pearameters.
26 # Hashes are OK instead of Arrays. Then the entry type ('command',
27 # 'checkbutton', 'radiobutton' or 'cascade') is given by 'type' key
28 # (e.g. :type=>'cascade'). When type is 'cascade', an array of menu_info
29 # is acceptable for 'menu' key (then, create sub-menu).
32 # If you want to make special menus (*.help for UNIX, *.system for Win,
33 # and *.apple for Mac), append 'menu_name'=>name (name is 'help' for UNIX,
34 # 'system' for Win, and 'apple' for Mac) option to the configs hash of
35 # menu button/entry information.
38 # If you want to configure a cascade menu, add :menu_config=>{...configs..}
39 # to the configs of the cascade entry.
42 def _create_menu(parent, menu_info, menu_name = nil,
43 tearoff = false, default_opts = nil)
44 if tearoff.kind_of?(Hash)
45 default_opts = tearoff
49 if menu_name.kind_of?(Hash)
50 default_opts = menu_name
55 if default_opts.kind_of?(Hash)
56 orig_opts = _symbolkey2str(default_opts)
61 tearoff = orig_opts.delete('tearoff') if orig_opts.key?('tearoff')
64 #menu = Tk::Menu.new(parent, :widgetname=>menu_name, :tearoff=>tearoff)
65 # --> use current TkMenu class
66 menu = TkMenu.new(parent, :widgetname=>menu_name, :tearoff=>tearoff)
68 #menu = Tk::Menu.new(parent, :tearoff=>tearoff)
69 # --> use current TkMenu class
70 menu = TkMenu.new(parent, :tearoff=>tearoff)
73 for item_info in menu_info
74 if item_info.kind_of?(Hash)
75 options = orig_opts.dup
76 options.update(_symbolkey2str(item_info))
77 item_type = (options.delete('type') || 'command').to_s
78 menu_name = options.delete('menu_name')
79 menu_opts = orig_opts.dup
80 menu_opts.update(_symbolkey2str(options.delete('menu_config') || {}))
81 if item_type == 'cascade' && options['menu'].kind_of?(Array)
83 submenu = _create_menu(menu, options['menu'], menu_name,
85 options['menu'] = submenu
87 menu.add(item_type, options)
89 elsif item_info.kind_of?(Array)
90 options = orig_opts.dup
92 options['label'] = item_info[0] if item_info[0]
97 item_type = 'checkbutton'
98 options['variable'] = item_info[1]
99 options['onvalue'] = true
100 options['offvalue'] = false
103 # radiobutton or cascade
104 if item_info[1][0].kind_of?(TkVariable)
106 item_type = 'radiobutton'
107 options['variable'] = item_info[1][0]
108 options['value'] = item_info[1][1] if item_info[1][1]
112 item_type = 'cascade'
113 menu_opts = orig_opts.dup
114 if item_info[4] && item_info[4].kind_of?(Hash)
115 opts = _symbolkey2str(item_info[4])
116 menu_name = opts.delete('menu_name')
117 menu_config = opts.delete('menu_config') || {}
118 menu_opts.update(_symbolkey2str(menu_config))
120 submenu = _create_menu(menu, item_info[1], menu_name,
122 options['menu'] = submenu
127 item_type = 'command'
128 options['command'] = item_info[1] if item_info[1]
131 options['underline'] = item_info[2] if item_info[2]
132 options['accelerator'] = item_info[3] if item_info[3]
133 if item_info[4] && item_info[4].kind_of?(Hash)
134 opts = _symbolkey2str(item_info[4])
135 if item_type == 'cascade'
136 opts.delete('menu_name')
137 opts.delete('menu_config')
141 menu.add(item_type, options)
143 elsif /^-+$/ =~ item_info
144 menu.add('separator')
147 menu.add('command', 'label' => item_info)
153 private :_create_menu
155 def _use_menubar?(parent)
157 if parent.kind_of?(Tk::Root) || parent.kind_of?(Tk::Toplevel)
159 elsif parent.current_configinfo.has_key?('menu')
165 private :_use_menubar?
167 def _create_menu_for_menubar(parent)
168 #unless (mbar = parent.menu).kind_of?(TkMenu)
169 # --> use current TkMenu class
171 unless mbar.kind_of?(Tk::Menu) || mbar.kind_of?(TkMenu)
172 #mbar = Tk::Menu.new(parent, :tearoff=>false)
173 mbar = TkMenu.new(parent, :tearoff=>false)
178 private :_create_menu_for_menubar
180 def _create_menubutton(parent, menu_info, tearoff=false, default_opts = nil)
181 btn_info = menu_info[0]
183 if tearoff.kind_of?(Hash)
184 default_opts = tearoff
188 if default_opts.kind_of?(Hash)
189 keys = _symbolkey2str(default_opts)
194 tearoff = keys.delete('tearoff') if keys.key?('tearoff')
196 if _use_menubar?(parent)
197 # menubar by menu entries
198 mbar = _create_menu_for_menubar(parent)
202 if btn_info.kind_of?(Hash)
203 keys.update(_symbolkey2str(btn_info))
204 menu_name = keys.delete('menu_name')
205 keys['label'] = keys.delete('text') if keys.key?('text')
206 elsif btn_info.kind_of?(Array)
207 keys['label'] = btn_info[0] if btn_info[0]
208 keys['underline'] = btn_info[1] if btn_info[1]
209 if btn_info[2]&&btn_info[2].kind_of?(Hash)
210 keys.update(_symbolkey2str(btn_info[2]))
211 menu_name = keys.delete('menu_name')
214 keys = {:label=>btn_info}
217 menu = _create_menu(mbar, menu_info[1..-1], menu_name,
218 tearoff, default_opts)
219 menu.tearoff(tearoff)
222 mbar.add('cascade', keys)
227 # menubar by menubuttons
228 #mbtn = Tk::Menubutton.new(parent)
229 # --> use current TkMenubutton class
230 mbtn = TkMenubutton.new(parent)
234 if btn_info.kind_of?(Hash)
235 keys.update(_symbolkey2str(btn_info))
236 menu_name = keys.delete('menu_name')
237 keys['text'] = keys.delete('label') if keys.key?('label')
239 elsif btn_info.kind_of?(Array)
240 mbtn.configure('text', btn_info[0]) if btn_info[0]
241 mbtn.configure('underline', btn_info[1]) if btn_info[1]
242 # mbtn.configure('accelerator', btn_info[2]) if btn_info[2]
243 if btn_info[2]&&btn_info[2].kind_of?(Hash)
244 keys.update(_symbolkey2str(btn_info[2]))
245 menu_name = keys.delete('menu_name')
249 mbtn.configure('text', btn_info)
252 mbtn.pack('side' => 'left')
254 menu = _create_menu(mbtn, menu_info[1..-1], menu_name,
255 tearoff, default_opts)
262 private :_create_menubutton
264 def _get_cascade_menus(menu)
266 (0..(menu.index('last'))).each{|idx|
267 if menu.menutype(idx) == 'cascade'
268 submenu = menu.entrycget(idx, 'menu')
269 menus << [submenu, _get_cascade_menus(submenu)]
274 private :_get_cascade_menus