Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / devtools / amd64 / lib / python2.4 / site-packages / Pmw / Pmw_1_2 / lib / PmwBalloon.py
CommitLineData
920dae64
AT
1import os
2import string
3import Tkinter
4import Pmw
5
6class Balloon(Pmw.MegaToplevel):
7 def __init__(self, parent = None, **kw):
8
9 # Define the megawidget options.
10 optiondefs = (
11 ('initwait', 500, None), # milliseconds
12 ('label_background', 'lightyellow', None),
13 ('label_foreground', 'black', None),
14 ('label_justify', 'left', None),
15 ('master', 'parent', None),
16 ('relmouse', 'none', self._relmouse),
17 ('state', 'both', self._state),
18 ('statuscommand', None, None),
19 ('xoffset', 20, None), # pixels
20 ('yoffset', 1, None), # pixels
21 ('hull_highlightthickness', 1, None),
22 ('hull_highlightbackground', 'black', None),
23 )
24 self.defineoptions(kw, optiondefs)
25
26 # Initialise the base class (after defining the options).
27 Pmw.MegaToplevel.__init__(self, parent)
28
29 self.withdraw()
30 self.overrideredirect(1)
31
32 # Create the components.
33 interior = self.interior()
34 self._label = self.createcomponent('label',
35 (), None,
36 Tkinter.Label, (interior,))
37 self._label.pack()
38
39 # The default hull configuration options give a black border
40 # around the balloon, but avoids a black 'flash' when the
41 # balloon is deiconified, before the text appears.
42 if not kw.has_key('hull_background'):
43 self.configure(hull_background = \
44 str(self._label.cget('background')))
45
46 # Initialise instance variables.
47 self._timer = None
48
49 # The widget or item that is currently triggering the balloon.
50 # It is None if the balloon is not being displayed. It is a
51 # one-tuple if the balloon is being displayed in response to a
52 # widget binding (value is the widget). It is a two-tuple if
53 # the balloon is being displayed in response to a canvas or
54 # text item binding (value is the widget and the item).
55 self._currentTrigger = None
56
57 # Check keywords and initialise options.
58 self.initialiseoptions()
59
60 def destroy(self):
61 if self._timer is not None:
62 self.after_cancel(self._timer)
63 self._timer = None
64 Pmw.MegaToplevel.destroy(self)
65
66 def bind(self, widget, balloonHelp, statusHelp = None):
67
68 # If a previous bind for this widget exists, remove it.
69 self.unbind(widget)
70
71 if balloonHelp is None and statusHelp is None:
72 return
73
74 if statusHelp is None:
75 statusHelp = balloonHelp
76 enterId = widget.bind('<Enter>',
77 lambda event, self = self, w = widget,
78 sHelp = statusHelp, bHelp = balloonHelp:
79 self._enter(event, w, sHelp, bHelp, 0))
80
81 # Set Motion binding so that if the pointer remains at rest
82 # within the widget until the status line removes the help and
83 # then the pointer moves again, then redisplay the help in the
84 # status line.
85 # Note: The Motion binding only works for basic widgets, and
86 # the hull of megawidgets but not for other megawidget components.
87 motionId = widget.bind('<Motion>',
88 lambda event = None, self = self, statusHelp = statusHelp:
89 self.showstatus(statusHelp))
90
91 leaveId = widget.bind('<Leave>', self._leave)
92 buttonId = widget.bind('<ButtonPress>', self._buttonpress)
93
94 # Set Destroy binding so that the balloon can be withdrawn and
95 # the timer can be cancelled if the widget is destroyed.
96 destroyId = widget.bind('<Destroy>', self._destroy)
97
98 # Use the None item in the widget's private Pmw dictionary to
99 # store the widget's bind callbacks, for later clean up.
100 if not hasattr(widget, '_Pmw_BalloonBindIds'):
101 widget._Pmw_BalloonBindIds = {}
102 widget._Pmw_BalloonBindIds[None] = \
103 (enterId, motionId, leaveId, buttonId, destroyId)
104
105 def unbind(self, widget):
106 if hasattr(widget, '_Pmw_BalloonBindIds'):
107 if widget._Pmw_BalloonBindIds.has_key(None):
108 (enterId, motionId, leaveId, buttonId, destroyId) = \
109 widget._Pmw_BalloonBindIds[None]
110 # Need to pass in old bindings, so that Tkinter can
111 # delete the commands. Otherwise, memory is leaked.
112 widget.unbind('<Enter>', enterId)
113 widget.unbind('<Motion>', motionId)
114 widget.unbind('<Leave>', leaveId)
115 widget.unbind('<ButtonPress>', buttonId)
116 widget.unbind('<Destroy>', destroyId)
117 del widget._Pmw_BalloonBindIds[None]
118
119 if self._currentTrigger is not None and len(self._currentTrigger) == 1:
120 # The balloon is currently being displayed and the current
121 # trigger is a widget.
122 triggerWidget = self._currentTrigger[0]
123 if triggerWidget == widget:
124 if self._timer is not None:
125 self.after_cancel(self._timer)
126 self._timer = None
127 self.withdraw()
128 self.clearstatus()
129 self._currentTrigger = None
130
131 def tagbind(self, widget, tagOrItem, balloonHelp, statusHelp = None):
132
133 # If a previous bind for this widget's tagOrItem exists, remove it.
134 self.tagunbind(widget, tagOrItem)
135
136 if balloonHelp is None and statusHelp is None:
137 return
138
139 if statusHelp is None:
140 statusHelp = balloonHelp
141 enterId = widget.tag_bind(tagOrItem, '<Enter>',
142 lambda event, self = self, w = widget,
143 sHelp = statusHelp, bHelp = balloonHelp:
144 self._enter(event, w, sHelp, bHelp, 1))
145 motionId = widget.tag_bind(tagOrItem, '<Motion>',
146 lambda event = None, self = self, statusHelp = statusHelp:
147 self.showstatus(statusHelp))
148 leaveId = widget.tag_bind(tagOrItem, '<Leave>', self._leave)
149 buttonId = widget.tag_bind(tagOrItem, '<ButtonPress>', self._buttonpress)
150
151 # Use the tagOrItem item in the widget's private Pmw dictionary to
152 # store the tagOrItem's bind callbacks, for later clean up.
153 if not hasattr(widget, '_Pmw_BalloonBindIds'):
154 widget._Pmw_BalloonBindIds = {}
155 widget._Pmw_BalloonBindIds[tagOrItem] = \
156 (enterId, motionId, leaveId, buttonId)
157
158 def tagunbind(self, widget, tagOrItem):
159 if hasattr(widget, '_Pmw_BalloonBindIds'):
160 if widget._Pmw_BalloonBindIds.has_key(tagOrItem):
161 (enterId, motionId, leaveId, buttonId) = \
162 widget._Pmw_BalloonBindIds[tagOrItem]
163 widget.tag_unbind(tagOrItem, '<Enter>', enterId)
164 widget.tag_unbind(tagOrItem, '<Motion>', motionId)
165 widget.tag_unbind(tagOrItem, '<Leave>', leaveId)
166 widget.tag_unbind(tagOrItem, '<ButtonPress>', buttonId)
167 del widget._Pmw_BalloonBindIds[tagOrItem]
168
169 if self._currentTrigger is None:
170 # The balloon is not currently being displayed.
171 return
172
173 if len(self._currentTrigger) == 1:
174 # The current trigger is a widget.
175 return
176
177 if len(self._currentTrigger) == 2:
178 # The current trigger is a canvas item.
179 (triggerWidget, triggerItem) = self._currentTrigger
180 if triggerWidget == widget and triggerItem == tagOrItem:
181 if self._timer is not None:
182 self.after_cancel(self._timer)
183 self._timer = None
184 self.withdraw()
185 self.clearstatus()
186 self._currentTrigger = None
187 else: # The current trigger is a text item.
188 (triggerWidget, x, y) = self._currentTrigger
189 if triggerWidget == widget:
190 currentPos = widget.index('@%d,%d' % (x, y))
191 currentTags = widget.tag_names(currentPos)
192 if tagOrItem in currentTags:
193 if self._timer is not None:
194 self.after_cancel(self._timer)
195 self._timer = None
196 self.withdraw()
197 self.clearstatus()
198 self._currentTrigger = None
199
200 def showstatus(self, statusHelp):
201 if self['state'] in ('status', 'both'):
202 cmd = self['statuscommand']
203 if callable(cmd):
204 cmd(statusHelp)
205
206 def clearstatus(self):
207 self.showstatus(None)
208
209 def _state(self):
210 if self['state'] not in ('both', 'balloon', 'status', 'none'):
211 raise ValueError, 'bad state option ' + repr(self['state']) + \
212 ': should be one of \'both\', \'balloon\', ' + \
213 '\'status\' or \'none\''
214
215 def _relmouse(self):
216 if self['relmouse'] not in ('both', 'x', 'y', 'none'):
217 raise ValueError, 'bad relmouse option ' + repr(self['relmouse'])+ \
218 ': should be one of \'both\', \'x\', ' + '\'y\' or \'none\''
219
220 def _enter(self, event, widget, statusHelp, balloonHelp, isItem):
221
222 # Do not display balloon if mouse button is pressed. This
223 # will only occur if the button was pressed inside a widget,
224 # then the mouse moved out of and then back into the widget,
225 # with the button still held down. The number 0x1f00 is the
226 # button mask for the 5 possible buttons in X.
227 buttonPressed = (event.state & 0x1f00) != 0
228
229 if not buttonPressed and balloonHelp is not None and \
230 self['state'] in ('balloon', 'both'):
231 if self._timer is not None:
232 self.after_cancel(self._timer)
233 self._timer = None
234
235 self._timer = self.after(self['initwait'],
236 lambda self = self, widget = widget, help = balloonHelp,
237 isItem = isItem:
238 self._showBalloon(widget, help, isItem))
239
240 if isItem:
241 if hasattr(widget, 'canvasx'):
242 # The widget is a canvas.
243 item = widget.find_withtag('current')
244 if len(item) > 0:
245 item = item[0]
246 else:
247 item = None
248 self._currentTrigger = (widget, item)
249 else:
250 # The widget is a text widget.
251 self._currentTrigger = (widget, event.x, event.y)
252 else:
253 self._currentTrigger = (widget,)
254
255 self.showstatus(statusHelp)
256
257 def _leave(self, event):
258 if self._timer is not None:
259 self.after_cancel(self._timer)
260 self._timer = None
261 self.withdraw()
262 self.clearstatus()
263 self._currentTrigger = None
264
265 def _destroy(self, event):
266
267 # Only withdraw the balloon and cancel the timer if the widget
268 # being destroyed is the widget that triggered the balloon.
269 # Note that in a Tkinter Destroy event, the widget field is a
270 # string and not a widget as usual.
271
272 if self._currentTrigger is None:
273 # The balloon is not currently being displayed
274 return
275
276 if len(self._currentTrigger) == 1:
277 # The current trigger is a widget (not an item)
278 triggerWidget = self._currentTrigger[0]
279 if str(triggerWidget) == event.widget:
280 if self._timer is not None:
281 self.after_cancel(self._timer)
282 self._timer = None
283 self.withdraw()
284 self.clearstatus()
285 self._currentTrigger = None
286
287 def _buttonpress(self, event):
288 if self._timer is not None:
289 self.after_cancel(self._timer)
290 self._timer = None
291 self.withdraw()
292 self._currentTrigger = None
293
294 def _showBalloon(self, widget, balloonHelp, isItem):
295
296 self._label.configure(text = balloonHelp)
297
298 # First, display the balloon offscreen to get dimensions.
299 screenWidth = self.winfo_screenwidth()
300 screenHeight = self.winfo_screenheight()
301 self.geometry('+%d+0' % (screenWidth + 1))
302 self.update_idletasks()
303
304 if isItem:
305 # Get the bounding box of the current item.
306 bbox = widget.bbox('current')
307 if bbox is None:
308 # The item that triggered the balloon has disappeared,
309 # perhaps by a user's timer event that occured between
310 # the <Enter> event and the 'initwait' timer calling
311 # this method.
312 return
313
314 # The widget is either a text or canvas. The meaning of
315 # the values returned by the bbox method is different for
316 # each, so use the existence of the 'canvasx' method to
317 # distinguish between them.
318 if hasattr(widget, 'canvasx'):
319 # The widget is a canvas. Place balloon under canvas
320 # item. The positions returned by bbox are relative
321 # to the entire canvas, not just the visible part, so
322 # need to convert to window coordinates.
323 leftrel = bbox[0] - widget.canvasx(0)
324 toprel = bbox[1] - widget.canvasy(0)
325 bottomrel = bbox[3] - widget.canvasy(0)
326 else:
327 # The widget is a text widget. Place balloon under
328 # the character closest to the mouse. The positions
329 # returned by bbox are relative to the text widget
330 # window (ie the visible part of the text only).
331 leftrel = bbox[0]
332 toprel = bbox[1]
333 bottomrel = bbox[1] + bbox[3]
334 else:
335 leftrel = 0
336 toprel = 0
337 bottomrel = widget.winfo_height()
338
339 xpointer, ypointer = widget.winfo_pointerxy() # -1 if off screen
340
341 if xpointer >= 0 and self['relmouse'] in ('both', 'x'):
342 x = xpointer
343 else:
344 x = leftrel + widget.winfo_rootx()
345 x = x + self['xoffset']
346
347 if ypointer >= 0 and self['relmouse'] in ('both', 'y'):
348 y = ypointer
349 else:
350 y = bottomrel + widget.winfo_rooty()
351 y = y + self['yoffset']
352
353 edges = (string.atoi(str(self.cget('hull_highlightthickness'))) +
354 string.atoi(str(self.cget('hull_borderwidth')))) * 2
355 if x + self._label.winfo_reqwidth() + edges > screenWidth:
356 x = screenWidth - self._label.winfo_reqwidth() - edges
357
358 if y + self._label.winfo_reqheight() + edges > screenHeight:
359 if ypointer >= 0 and self['relmouse'] in ('both', 'y'):
360 y = ypointer
361 else:
362 y = toprel + widget.winfo_rooty()
363 y = y - self._label.winfo_reqheight() - self['yoffset'] - edges
364
365 Pmw.setgeometryanddeiconify(self, '+%d+%d' % (x, y))