OSDN Git Service

ruby-1.9.1-rc1
[splhack/AndroidRuby.git] / lib / ruby-1.9.1-rc1 / ext / tk / lib / tk / menuspec.rb
1 #
2 # tk/menuspec.rb
3 #                              Hidethoshi NAGAI (nagai@ai.kyutech.ac.jp)
4 #
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.
8 #
9 # The format of the menu_spec is:
10 #   [ menu_info, menu_info, ... ]
11 #
12 # And the format of the menu_info is:
13 #   [
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)
21 #     '---', # separator
22 #     ...
23 #   ]
24 #
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).
30 #
31 # NOTE: (*1)
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.
36 #
37 # NOTE: (*2)
38 #   If you want to configure a cascade menu, add :menu_config=>{...configs..}
39 #   to the configs of the cascade entry.
40
41 module TkMenuSpec
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
46       tearoff = false
47     end
48
49     if menu_name.kind_of?(Hash)
50       default_opts = menu_name
51       menu_name = nil
52       tearoff = false
53     end
54
55     if default_opts.kind_of?(Hash)
56       orig_opts = _symbolkey2str(default_opts)
57     else
58       orig_opts = {}
59     end
60
61     tearoff = orig_opts.delete('tearoff') if orig_opts.key?('tearoff')
62
63     if menu_name
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)
67     else
68       #menu = Tk::Menu.new(parent, :tearoff=>tearoff)
69       # --> use current TkMenu class
70       menu = TkMenu.new(parent, :tearoff=>tearoff)
71     end
72
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)
82           # create cascade menu
83           submenu = _create_menu(menu, options['menu'], menu_name, 
84                                  tearoff, menu_opts)
85           options['menu'] = submenu
86         end
87         menu.add(item_type, options)
88
89       elsif item_info.kind_of?(Array)
90         options = orig_opts.dup
91
92         options['label'] = item_info[0] if item_info[0]
93
94         case item_info[1]
95         when TkVariable
96           # checkbutton
97           item_type = 'checkbutton'
98           options['variable'] = item_info[1]
99           options['onvalue']  = true
100           options['offvalue'] = false
101
102         when Array
103           # radiobutton or cascade
104           if item_info[1][0].kind_of?(TkVariable)
105             # radiobutton
106             item_type = 'radiobutton'
107             options['variable'] = item_info[1][0]
108             options['value'] = item_info[1][1] if item_info[1][1]
109
110           else
111             # cascade
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))
119             end
120             submenu = _create_menu(menu, item_info[1], menu_name, 
121                                    tearoff, menu_opts)
122             options['menu'] = submenu
123           end
124
125         else
126           # command
127           item_type = 'command'
128           options['command'] = item_info[1] if item_info[1]
129         end
130
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')
138           end
139           options.update(opts)
140         end
141         menu.add(item_type, options)
142
143       elsif /^-+$/ =~ item_info
144         menu.add('separator')
145
146       else
147         menu.add('command', 'label' => item_info)
148       end
149     end
150
151     menu
152   end
153   private :_create_menu
154
155   def _use_menubar?(parent)
156     use_menubar = false
157     if parent.kind_of?(Tk::Root) || parent.kind_of?(Tk::Toplevel)
158       true 
159     elsif parent.current_configinfo.has_key?('menu')
160       true
161     else
162       false
163     end
164   end
165   private :_use_menubar?
166
167   def _create_menu_for_menubar(parent)
168     #unless (mbar = parent.menu).kind_of?(TkMenu)
169     # --> use current TkMenu class
170     mbar = parent.menu
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)
174       parent.menu(mbar)
175     end
176     mbar
177   end
178   private :_create_menu_for_menubar
179
180   def _create_menubutton(parent, menu_info, tearoff=false, default_opts = nil)
181     btn_info = menu_info[0]
182
183     if tearoff.kind_of?(Hash)
184       default_opts = tearoff
185       tearoff = false
186     end
187
188     if default_opts.kind_of?(Hash)
189       keys = _symbolkey2str(default_opts)
190     else
191       keys = {}
192     end
193
194     tearoff = keys.delete('tearoff') if keys.key?('tearoff')
195
196     if _use_menubar?(parent)
197       # menubar by menu entries
198       mbar = _create_menu_for_menubar(parent)
199
200       menu_name = nil
201
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')
212         end
213       else
214         keys = {:label=>btn_info}
215       end
216
217       menu = _create_menu(mbar, menu_info[1..-1], menu_name, 
218                           tearoff, default_opts)
219       menu.tearoff(tearoff)
220
221       keys['menu'] = menu
222       mbar.add('cascade', keys)
223
224       [mbar, menu]
225
226     else
227       # menubar by menubuttons
228       #mbtn = Tk::Menubutton.new(parent)
229       # --> use current TkMenubutton class
230       mbtn = TkMenubutton.new(parent)
231
232       menu_name = nil
233
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')
238         mbtn.configure(keys)
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')
246           mbtn.configure(keys)
247         end
248       else
249         mbtn.configure('text', btn_info)
250       end
251
252       mbtn.pack('side' => 'left')
253
254       menu = _create_menu(mbtn, menu_info[1..-1], menu_name, 
255                           tearoff, default_opts)
256     
257       mbtn.menu(menu)
258
259       [mbtn, menu]
260     end
261   end
262   private :_create_menubutton
263
264   def _get_cascade_menus(menu)
265     menus = []
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)]
270       end
271     }
272     menus
273   end
274   private :_get_cascade_menus
275 end