Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / devtools / v8plus / lib / python2.4 / site-packages / Pmw / Pmw_1_2 / lib / PmwComboBox.py
CommitLineData
920dae64
AT
1# Based on iwidgets2.2.0/combobox.itk code.
2
3import os
4import string
5import types
6import Tkinter
7import Pmw
8
9class ComboBox(Pmw.MegaWidget):
10 def __init__(self, parent = None, **kw):
11
12 # Define the megawidget options.
13 INITOPT = Pmw.INITOPT
14 optiondefs = (
15 ('autoclear', 0, INITOPT),
16 ('buttonaspect', 1.0, INITOPT),
17 ('dropdown', 1, INITOPT),
18 ('fliparrow', 0, INITOPT),
19 ('history', 1, INITOPT),
20 ('labelmargin', 0, INITOPT),
21 ('labelpos', None, INITOPT),
22 ('listheight', 200, INITOPT),
23 ('selectioncommand', None, None),
24 ('sticky', 'ew', INITOPT),
25 ('unique', 1, INITOPT),
26 )
27 self.defineoptions(kw, optiondefs)
28
29 # Initialise the base class (after defining the options).
30 Pmw.MegaWidget.__init__(self, parent)
31
32 # Create the components.
33 interior = self.interior()
34
35 self._entryfield = self.createcomponent('entryfield',
36 (('entry', 'entryfield_entry'),), None,
37 Pmw.EntryField, (interior,))
38 self._entryfield.grid(column=2, row=2, sticky=self['sticky'])
39 interior.grid_columnconfigure(2, weight = 1)
40 self._entryWidget = self._entryfield.component('entry')
41
42 if self['dropdown']:
43 self._isPosted = 0
44 interior.grid_rowconfigure(2, weight = 1)
45
46 # Create the arrow button.
47 self._arrowBtn = self.createcomponent('arrowbutton',
48 (), None,
49 Tkinter.Canvas, (interior,), borderwidth = 2,
50 relief = 'raised',
51 width = 16, height = 16)
52 if 'n' in self['sticky']:
53 sticky = 'n'
54 else:
55 sticky = ''
56 if 's' in self['sticky']:
57 sticky = sticky + 's'
58 self._arrowBtn.grid(column=3, row=2, sticky = sticky)
59 self._arrowRelief = self._arrowBtn.cget('relief')
60
61 # Create the label.
62 self.createlabel(interior, childCols=2)
63
64 # Create the dropdown window.
65 self._popup = self.createcomponent('popup',
66 (), None,
67 Tkinter.Toplevel, (interior,))
68 self._popup.withdraw()
69 self._popup.overrideredirect(1)
70
71 # Create the scrolled listbox inside the dropdown window.
72 self._list = self.createcomponent('scrolledlist',
73 (('listbox', 'scrolledlist_listbox'),), None,
74 Pmw.ScrolledListBox, (self._popup,),
75 hull_borderwidth = 2,
76 hull_relief = 'raised',
77 hull_height = self['listheight'],
78 usehullsize = 1,
79 listbox_exportselection = 0)
80 self._list.pack(expand=1, fill='both')
81 self.__listbox = self._list.component('listbox')
82
83 # Bind events to the arrow button.
84 self._arrowBtn.bind('<1>', self._postList)
85 self._arrowBtn.bind('<Configure>', self._drawArrow)
86 self._arrowBtn.bind('<3>', self._next)
87 self._arrowBtn.bind('<Shift-3>', self._previous)
88 self._arrowBtn.bind('<Down>', self._next)
89 self._arrowBtn.bind('<Up>', self._previous)
90 self._arrowBtn.bind('<Control-n>', self._next)
91 self._arrowBtn.bind('<Control-p>', self._previous)
92 self._arrowBtn.bind('<Shift-Down>', self._postList)
93 self._arrowBtn.bind('<Shift-Up>', self._postList)
94 self._arrowBtn.bind('<F34>', self._postList)
95 self._arrowBtn.bind('<F28>', self._postList)
96 self._arrowBtn.bind('<space>', self._postList)
97
98 # Bind events to the dropdown window.
99 self._popup.bind('<Escape>', self._unpostList)
100 self._popup.bind('<space>', self._selectUnpost)
101 self._popup.bind('<Return>', self._selectUnpost)
102 self._popup.bind('<ButtonRelease-1>', self._dropdownBtnRelease)
103 self._popup.bind('<ButtonPress-1>', self._unpostOnNextRelease)
104
105 # Bind events to the Tk listbox.
106 self.__listbox.bind('<Enter>', self._unpostOnNextRelease)
107
108 # Bind events to the Tk entry widget.
109 self._entryWidget.bind('<Configure>', self._resizeArrow)
110 self._entryWidget.bind('<Shift-Down>', self._postList)
111 self._entryWidget.bind('<Shift-Up>', self._postList)
112 self._entryWidget.bind('<F34>', self._postList)
113 self._entryWidget.bind('<F28>', self._postList)
114
115 # Need to unpost the popup if the entryfield is unmapped (eg:
116 # its toplevel window is withdrawn) while the popup list is
117 # displayed.
118 self._entryWidget.bind('<Unmap>', self._unpostList)
119
120 else:
121 # Create the scrolled listbox below the entry field.
122 self._list = self.createcomponent('scrolledlist',
123 (('listbox', 'scrolledlist_listbox'),), None,
124 Pmw.ScrolledListBox, (interior,),
125 selectioncommand = self._selectCmd)
126 self._list.grid(column=2, row=3, sticky='nsew')
127 self.__listbox = self._list.component('listbox')
128
129 # The scrolled listbox should expand vertically.
130 interior.grid_rowconfigure(3, weight = 1)
131
132 # Create the label.
133 self.createlabel(interior, childRows=2)
134
135 self._entryWidget.bind('<Down>', self._next)
136 self._entryWidget.bind('<Up>', self._previous)
137 self._entryWidget.bind('<Control-n>', self._next)
138 self._entryWidget.bind('<Control-p>', self._previous)
139 self.__listbox.bind('<Control-n>', self._next)
140 self.__listbox.bind('<Control-p>', self._previous)
141
142 if self['history']:
143 self._entryfield.configure(command=self._addHistory)
144
145 # Check keywords and initialise options.
146 self.initialiseoptions()
147
148 def destroy(self):
149 if self['dropdown'] and self._isPosted:
150 Pmw.popgrab(self._popup)
151 Pmw.MegaWidget.destroy(self)
152
153 #======================================================================
154
155 # Public methods
156
157 def get(self, first = None, last=None):
158 if first is None:
159 return self._entryWidget.get()
160 else:
161 return self._list.get(first, last)
162
163 def invoke(self):
164 if self['dropdown']:
165 self._postList()
166 else:
167 return self._selectCmd()
168
169 def selectitem(self, index, setentry=1):
170 if type(index) == types.StringType:
171 text = index
172 items = self._list.get(0, 'end')
173 if text in items:
174 index = list(items).index(text)
175 else:
176 raise IndexError, 'index "%s" not found' % text
177 elif setentry:
178 text = self._list.get(0, 'end')[index]
179
180 self._list.select_clear(0, 'end')
181 self._list.select_set(index, index)
182 self._list.activate(index)
183 self.see(index)
184 if setentry:
185 self._entryfield.setentry(text)
186
187 # Need to explicitly forward this to override the stupid
188 # (grid_)size method inherited from Tkinter.Frame.Grid.
189 def size(self):
190 return self._list.size()
191
192 # Need to explicitly forward this to override the stupid
193 # (grid_)bbox method inherited from Tkinter.Frame.Grid.
194 def bbox(self, index):
195 return self._list.bbox(index)
196
197 def clear(self):
198 self._entryfield.clear()
199 self._list.clear()
200
201 #======================================================================
202
203 # Private methods for both dropdown and simple comboboxes.
204
205 def _addHistory(self):
206 input = self._entryWidget.get()
207
208 if input != '':
209 index = None
210 if self['unique']:
211 # If item is already in list, select it and return.
212 items = self._list.get(0, 'end')
213 if input in items:
214 index = list(items).index(input)
215
216 if index is None:
217 index = self._list.index('end')
218 self._list.insert('end', input)
219
220 self.selectitem(index)
221 if self['autoclear']:
222 self._entryWidget.delete(0, 'end')
223
224 # Execute the selectioncommand on the new entry.
225 self._selectCmd()
226
227 def _next(self, event):
228 size = self.size()
229 if size <= 1:
230 return
231
232 cursels = self.curselection()
233
234 if len(cursels) == 0:
235 index = 0
236 else:
237 index = string.atoi(cursels[0])
238 if index == size - 1:
239 index = 0
240 else:
241 index = index + 1
242
243 self.selectitem(index)
244
245 def _previous(self, event):
246 size = self.size()
247 if size <= 1:
248 return
249
250 cursels = self.curselection()
251
252 if len(cursels) == 0:
253 index = size - 1
254 else:
255 index = string.atoi(cursels[0])
256 if index == 0:
257 index = size - 1
258 else:
259 index = index - 1
260
261 self.selectitem(index)
262
263 def _selectCmd(self, event=None):
264
265 sels = self.getcurselection()
266 if len(sels) == 0:
267 item = None
268 else:
269 item = sels[0]
270 self._entryfield.setentry(item)
271
272 cmd = self['selectioncommand']
273 if callable(cmd):
274 if event is None:
275 # Return result of selectioncommand for invoke() method.
276 return cmd(item)
277 else:
278 cmd(item)
279
280 #======================================================================
281
282 # Private methods for dropdown combobox.
283
284 def _drawArrow(self, event=None, sunken=0):
285 arrow = self._arrowBtn
286 if sunken:
287 self._arrowRelief = arrow.cget('relief')
288 arrow.configure(relief = 'sunken')
289 else:
290 arrow.configure(relief = self._arrowRelief)
291
292 if self._isPosted and self['fliparrow']:
293 direction = 'up'
294 else:
295 direction = 'down'
296 Pmw.drawarrow(arrow, self['entry_foreground'], direction, 'arrow')
297
298 def _postList(self, event = None):
299 self._isPosted = 1
300 self._drawArrow(sunken=1)
301
302 # Make sure that the arrow is displayed sunken.
303 self.update_idletasks()
304
305 x = self._entryfield.winfo_rootx()
306 y = self._entryfield.winfo_rooty() + \
307 self._entryfield.winfo_height()
308 w = self._entryfield.winfo_width() + self._arrowBtn.winfo_width()
309 h = self.__listbox.winfo_height()
310 sh = self.winfo_screenheight()
311
312 if y + h > sh and y > sh / 2:
313 y = self._entryfield.winfo_rooty() - h
314
315 self._list.configure(hull_width=w)
316
317 Pmw.setgeometryanddeiconify(self._popup, '+%d+%d' % (x, y))
318
319 # Grab the popup, so that all events are delivered to it, and
320 # set focus to the listbox, to make keyboard navigation
321 # easier.
322 Pmw.pushgrab(self._popup, 1, self._unpostList)
323 self.__listbox.focus_set()
324
325 self._drawArrow()
326
327 # Ignore the first release of the mouse button after posting the
328 # dropdown list, unless the mouse enters the dropdown list.
329 self._ignoreRelease = 1
330
331 def _dropdownBtnRelease(self, event):
332 if (event.widget == self._list.component('vertscrollbar') or
333 event.widget == self._list.component('horizscrollbar')):
334 return
335
336 if self._ignoreRelease:
337 self._unpostOnNextRelease()
338 return
339
340 self._unpostList()
341
342 if (event.x >= 0 and event.x < self.__listbox.winfo_width() and
343 event.y >= 0 and event.y < self.__listbox.winfo_height()):
344 self._selectCmd()
345
346 def _unpostOnNextRelease(self, event = None):
347 self._ignoreRelease = 0
348
349 def _resizeArrow(self, event):
350 bw = (string.atoi(self._arrowBtn['borderwidth']) +
351 string.atoi(self._arrowBtn['highlightthickness']))
352 newHeight = self._entryfield.winfo_reqheight() - 2 * bw
353 newWidth = int(newHeight * self['buttonaspect'])
354 self._arrowBtn.configure(width=newWidth, height=newHeight)
355 self._drawArrow()
356
357 def _unpostList(self, event=None):
358 if not self._isPosted:
359 # It is possible to get events on an unposted popup. For
360 # example, by repeatedly pressing the space key to post
361 # and unpost the popup. The <space> event may be
362 # delivered to the popup window even though
363 # Pmw.popgrab() has set the focus away from the
364 # popup window. (Bug in Tk?)
365 return
366
367 # Restore the focus before withdrawing the window, since
368 # otherwise the window manager may take the focus away so we
369 # can't redirect it. Also, return the grab to the next active
370 # window in the stack, if any.
371 Pmw.popgrab(self._popup)
372 self._popup.withdraw()
373
374 self._isPosted = 0
375 self._drawArrow()
376
377 def _selectUnpost(self, event):
378 self._unpostList()
379 self._selectCmd()
380
381Pmw.forwardmethods(ComboBox, Pmw.ScrolledListBox, '_list')
382Pmw.forwardmethods(ComboBox, Pmw.EntryField, '_entryfield')