Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | # Main menubar |
2 | ||
3 | import string | |
4 | import types | |
5 | import Tkinter | |
6 | import Pmw | |
7 | ||
8 | class MainMenuBar(Pmw.MegaArchetype): | |
9 | ||
10 | def __init__(self, parent = None, **kw): | |
11 | ||
12 | # Define the megawidget options. | |
13 | INITOPT = Pmw.INITOPT | |
14 | optiondefs = ( | |
15 | ('balloon', None, None), | |
16 | ('hotkeys', 1, INITOPT), | |
17 | ('hull_tearoff', 0, None), | |
18 | ) | |
19 | self.defineoptions(kw, optiondefs, dynamicGroups = ('Menu',)) | |
20 | ||
21 | # Initialise the base class (after defining the options). | |
22 | Pmw.MegaArchetype.__init__(self, parent, Tkinter.Menu) | |
23 | ||
24 | self._menuInfo = {} | |
25 | self._menuInfo[None] = (None, []) | |
26 | # Map from a menu name to a tuple of information about the menu. | |
27 | # The first item in the tuple is the name of the parent menu (for | |
28 | # toplevel menus this is None). The second item in the tuple is | |
29 | # a list of status help messages for each item in the menu. | |
30 | # The key for the information for the main menubar is None. | |
31 | ||
32 | self._menu = self.interior() | |
33 | self._menu.bind('<Leave>', self._resetHelpmessage) | |
34 | self._menu.bind('<Motion>', | |
35 | lambda event=None, self=self: self._menuHelp(event, None)) | |
36 | ||
37 | # Check keywords and initialise options. | |
38 | self.initialiseoptions() | |
39 | ||
40 | def deletemenuitems(self, menuName, start, end = None): | |
41 | self.component(menuName).delete(start, end) | |
42 | if end is None: | |
43 | del self._menuInfo[menuName][1][start] | |
44 | else: | |
45 | self._menuInfo[menuName][1][start:end+1] = [] | |
46 | ||
47 | def deletemenu(self, menuName): | |
48 | """Delete should be called for cascaded menus before main menus. | |
49 | """ | |
50 | ||
51 | parentName = self._menuInfo[menuName][0] | |
52 | del self._menuInfo[menuName] | |
53 | if parentName is None: | |
54 | parentMenu = self._menu | |
55 | else: | |
56 | parentMenu = self.component(parentName) | |
57 | ||
58 | menu = self.component(menuName) | |
59 | menuId = str(menu) | |
60 | for item in range(parentMenu.index('end') + 1): | |
61 | if parentMenu.type(item) == 'cascade': | |
62 | itemMenu = str(parentMenu.entrycget(item, 'menu')) | |
63 | if itemMenu == menuId: | |
64 | parentMenu.delete(item) | |
65 | del self._menuInfo[parentName][1][item] | |
66 | break | |
67 | ||
68 | self.destroycomponent(menuName) | |
69 | ||
70 | def disableall(self): | |
71 | for index in range(len(self._menuInfo[None][1])): | |
72 | self.entryconfigure(index, state = 'disabled') | |
73 | ||
74 | def enableall(self): | |
75 | for index in range(len(self._menuInfo[None][1])): | |
76 | self.entryconfigure(index, state = 'normal') | |
77 | ||
78 | def addmenu(self, menuName, balloonHelp, statusHelp = None, | |
79 | traverseSpec = None, **kw): | |
80 | if statusHelp is None: | |
81 | statusHelp = balloonHelp | |
82 | self._addmenu(None, menuName, balloonHelp, statusHelp, | |
83 | traverseSpec, kw) | |
84 | ||
85 | def addcascademenu(self, parentMenuName, menuName, statusHelp='', | |
86 | traverseSpec = None, **kw): | |
87 | self._addmenu(parentMenuName, menuName, None, statusHelp, | |
88 | traverseSpec, kw) | |
89 | ||
90 | def _addmenu(self, parentMenuName, menuName, balloonHelp, statusHelp, | |
91 | traverseSpec, kw): | |
92 | ||
93 | if (menuName) in self.components(): | |
94 | raise ValueError, 'menu "%s" already exists' % menuName | |
95 | ||
96 | menukw = {} | |
97 | if kw.has_key('tearoff'): | |
98 | menukw['tearoff'] = kw['tearoff'] | |
99 | del kw['tearoff'] | |
100 | else: | |
101 | menukw['tearoff'] = 0 | |
102 | if kw.has_key('name'): | |
103 | menukw['name'] = kw['name'] | |
104 | del kw['name'] | |
105 | ||
106 | if not kw.has_key('label'): | |
107 | kw['label'] = menuName | |
108 | ||
109 | self._addHotkeyToOptions(parentMenuName, kw, traverseSpec) | |
110 | ||
111 | if parentMenuName is None: | |
112 | parentMenu = self._menu | |
113 | balloon = self['balloon'] | |
114 | # Bug in Tk: balloon help not implemented | |
115 | # if balloon is not None: | |
116 | # balloon.mainmenubind(parentMenu, balloonHelp, statusHelp) | |
117 | else: | |
118 | parentMenu = self.component(parentMenuName) | |
119 | ||
120 | apply(parentMenu.add_cascade, (), kw) | |
121 | ||
122 | menu = apply(self.createcomponent, (menuName, | |
123 | (), 'Menu', | |
124 | Tkinter.Menu, (parentMenu,)), menukw) | |
125 | parentMenu.entryconfigure('end', menu = menu) | |
126 | ||
127 | self._menuInfo[parentMenuName][1].append(statusHelp) | |
128 | self._menuInfo[menuName] = (parentMenuName, []) | |
129 | ||
130 | menu.bind('<Leave>', self._resetHelpmessage) | |
131 | menu.bind('<Motion>', | |
132 | lambda event=None, self=self, menuName=menuName: | |
133 | self._menuHelp(event, menuName)) | |
134 | ||
135 | def addmenuitem(self, menuName, itemType, statusHelp = '', | |
136 | traverseSpec = None, **kw): | |
137 | ||
138 | menu = self.component(menuName) | |
139 | if itemType != 'separator': | |
140 | self._addHotkeyToOptions(menuName, kw, traverseSpec) | |
141 | ||
142 | if itemType == 'command': | |
143 | command = menu.add_command | |
144 | elif itemType == 'separator': | |
145 | command = menu.add_separator | |
146 | elif itemType == 'checkbutton': | |
147 | command = menu.add_checkbutton | |
148 | elif itemType == 'radiobutton': | |
149 | command = menu.add_radiobutton | |
150 | elif itemType == 'cascade': | |
151 | command = menu.add_cascade | |
152 | else: | |
153 | raise ValueError, 'unknown menuitem type "%s"' % itemType | |
154 | ||
155 | self._menuInfo[menuName][1].append(statusHelp) | |
156 | apply(command, (), kw) | |
157 | ||
158 | def _addHotkeyToOptions(self, menuName, kw, traverseSpec): | |
159 | ||
160 | if (not self['hotkeys'] or kw.has_key('underline') or | |
161 | not kw.has_key('label')): | |
162 | return | |
163 | ||
164 | if type(traverseSpec) == types.IntType: | |
165 | kw['underline'] = traverseSpec | |
166 | return | |
167 | ||
168 | if menuName is None: | |
169 | menu = self._menu | |
170 | else: | |
171 | menu = self.component(menuName) | |
172 | hotkeyList = [] | |
173 | end = menu.index('end') | |
174 | if end is not None: | |
175 | for item in range(end + 1): | |
176 | if menu.type(item) not in ('separator', 'tearoff'): | |
177 | underline = \ | |
178 | string.atoi(str(menu.entrycget(item, 'underline'))) | |
179 | if underline != -1: | |
180 | label = str(menu.entrycget(item, 'label')) | |
181 | if underline < len(label): | |
182 | hotkey = string.lower(label[underline]) | |
183 | if hotkey not in hotkeyList: | |
184 | hotkeyList.append(hotkey) | |
185 | ||
186 | name = kw['label'] | |
187 | ||
188 | if type(traverseSpec) == types.StringType: | |
189 | lowerLetter = string.lower(traverseSpec) | |
190 | if traverseSpec in name and lowerLetter not in hotkeyList: | |
191 | kw['underline'] = string.index(name, traverseSpec) | |
192 | else: | |
193 | targets = string.digits + string.letters | |
194 | lowerName = string.lower(name) | |
195 | for letter_index in range(len(name)): | |
196 | letter = lowerName[letter_index] | |
197 | if letter in targets and letter not in hotkeyList: | |
198 | kw['underline'] = letter_index | |
199 | break | |
200 | ||
201 | def _menuHelp(self, event, menuName): | |
202 | if menuName is None: | |
203 | menu = self._menu | |
204 | index = menu.index('@%d'% event.x) | |
205 | else: | |
206 | menu = self.component(menuName) | |
207 | index = menu.index('@%d'% event.y) | |
208 | ||
209 | balloon = self['balloon'] | |
210 | if balloon is not None: | |
211 | if index is None: | |
212 | balloon.showstatus('') | |
213 | else: | |
214 | if str(menu.cget('tearoff')) == '1': | |
215 | index = index - 1 | |
216 | if index >= 0: | |
217 | help = self._menuInfo[menuName][1][index] | |
218 | balloon.showstatus(help) | |
219 | ||
220 | def _resetHelpmessage(self, event=None): | |
221 | balloon = self['balloon'] | |
222 | if balloon is not None: | |
223 | balloon.clearstatus() | |
224 | ||
225 | Pmw.forwardmethods(MainMenuBar, Tkinter.Menu, '_hull') |