Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / devtools / v9 / lib / python2.4 / site-packages / Pmw / Pmw_1_2 / lib / PmwScrolledText.py
CommitLineData
920dae64
AT
1# Based on iwidgets2.2.0/scrolledtext.itk code.
2
3import Tkinter
4import Pmw
5
6class ScrolledText(Pmw.MegaWidget):
7 def __init__(self, parent = None, **kw):
8
9 # Define the megawidget options.
10 INITOPT = Pmw.INITOPT
11 optiondefs = (
12 ('borderframe', 0, INITOPT),
13 ('columnheader', 0, INITOPT),
14 ('hscrollmode', 'dynamic', self._hscrollMode),
15 ('labelmargin', 0, INITOPT),
16 ('labelpos', None, INITOPT),
17 ('rowcolumnheader',0, INITOPT),
18 ('rowheader', 0, INITOPT),
19 ('scrollmargin', 2, INITOPT),
20 ('usehullsize', 0, INITOPT),
21 ('vscrollmode', 'dynamic', self._vscrollMode),
22 )
23 self.defineoptions(kw, optiondefs)
24
25 # Initialise the base class (after defining the options).
26 Pmw.MegaWidget.__init__(self, parent)
27
28 # Create the components.
29 interior = self.interior()
30
31 if self['usehullsize']:
32 interior.grid_propagate(0)
33
34 if self['borderframe']:
35 # Create a frame widget to act as the border of the text
36 # widget. Later, pack the text widget so that it fills
37 # the frame. This avoids a problem in Tk, where window
38 # items in a text widget may overlap the border of the
39 # text widget.
40 self._borderframe = self.createcomponent('borderframe',
41 (), None,
42 Tkinter.Frame, (interior,),
43 relief = 'sunken',
44 borderwidth = 2,
45 )
46 self._borderframe.grid(row = 4, column = 4, sticky = 'news')
47
48 # Create the text widget.
49 self._textbox = self.createcomponent('text',
50 (), None,
51 Tkinter.Text, (self._borderframe,),
52 highlightthickness = 0,
53 borderwidth = 0,
54 )
55 self._textbox.pack(fill = 'both', expand = 1)
56
57 bw = self._borderframe.cget('borderwidth'),
58 ht = self._borderframe.cget('highlightthickness'),
59 else:
60 # Create the text widget.
61 self._textbox = self.createcomponent('text',
62 (), None,
63 Tkinter.Text, (interior,),
64 )
65 self._textbox.grid(row = 4, column = 4, sticky = 'news')
66
67 bw = self._textbox.cget('borderwidth'),
68 ht = self._textbox.cget('highlightthickness'),
69
70 # Create the header text widgets
71 if self['columnheader']:
72 self._columnheader = self.createcomponent('columnheader',
73 (), 'Header',
74 Tkinter.Text, (interior,),
75 height=1,
76 wrap='none',
77 borderwidth = bw,
78 highlightthickness = ht,
79 )
80 self._columnheader.grid(row = 2, column = 4, sticky = 'ew')
81 self._columnheader.configure(
82 xscrollcommand = self._columnheaderscrolled)
83
84 if self['rowheader']:
85 self._rowheader = self.createcomponent('rowheader',
86 (), 'Header',
87 Tkinter.Text, (interior,),
88 wrap='none',
89 borderwidth = bw,
90 highlightthickness = ht,
91 )
92 self._rowheader.grid(row = 4, column = 2, sticky = 'ns')
93 self._rowheader.configure(
94 yscrollcommand = self._rowheaderscrolled)
95
96 if self['rowcolumnheader']:
97 self._rowcolumnheader = self.createcomponent('rowcolumnheader',
98 (), 'Header',
99 Tkinter.Text, (interior,),
100 height=1,
101 wrap='none',
102 borderwidth = bw,
103 highlightthickness = ht,
104 )
105 self._rowcolumnheader.grid(row = 2, column = 2, sticky = 'nsew')
106
107 interior.grid_rowconfigure(4, weight = 1, minsize = 0)
108 interior.grid_columnconfigure(4, weight = 1, minsize = 0)
109
110 # Create the horizontal scrollbar
111 self._horizScrollbar = self.createcomponent('horizscrollbar',
112 (), 'Scrollbar',
113 Tkinter.Scrollbar, (interior,),
114 orient='horizontal',
115 command=self._textbox.xview
116 )
117
118 # Create the vertical scrollbar
119 self._vertScrollbar = self.createcomponent('vertscrollbar',
120 (), 'Scrollbar',
121 Tkinter.Scrollbar, (interior,),
122 orient='vertical',
123 command=self._textbox.yview
124 )
125
126 self.createlabel(interior, childCols = 5, childRows = 5)
127
128 # Initialise instance variables.
129 self._horizScrollbarOn = 0
130 self._vertScrollbarOn = 0
131 self.scrollTimer = None
132 self._scrollRecurse = 0
133 self._horizScrollbarNeeded = 0
134 self._vertScrollbarNeeded = 0
135 self._textWidth = None
136
137 # These four variables avoid an infinite loop caused by the
138 # row or column header's scrollcommand causing the main text
139 # widget's scrollcommand to be called and vice versa.
140 self._textboxLastX = None
141 self._textboxLastY = None
142 self._columnheaderLastX = None
143 self._rowheaderLastY = None
144
145 # Check keywords and initialise options.
146 self.initialiseoptions()
147
148 def destroy(self):
149 if self.scrollTimer is not None:
150 self.after_cancel(self.scrollTimer)
151 self.scrollTimer = None
152 Pmw.MegaWidget.destroy(self)
153
154 # ======================================================================
155
156 # Public methods.
157
158 def clear(self):
159 self.settext('')
160
161 def importfile(self, fileName, where = 'end'):
162 file = open(fileName, 'r')
163 self._textbox.insert(where, file.read())
164 file.close()
165
166 def exportfile(self, fileName):
167 file = open(fileName, 'w')
168 file.write(self._textbox.get('1.0', 'end'))
169 file.close()
170
171 def settext(self, text):
172 disabled = (str(self._textbox.cget('state')) == 'disabled')
173 if disabled:
174 self._textbox.configure(state='normal')
175 self._textbox.delete('0.0', 'end')
176 self._textbox.insert('end', text)
177 if disabled:
178 self._textbox.configure(state='disabled')
179
180 # Override Tkinter.Text get method, so that if it is called with
181 # no arguments, return all text (consistent with other widgets).
182 def get(self, first=None, last=None):
183 if first is None:
184 return self._textbox.get('1.0', 'end')
185 else:
186 return self._textbox.get(first, last)
187
188 def getvalue(self):
189 return self.get()
190
191 def setvalue(self, text):
192 return self.settext(text)
193
194 def appendtext(self, text):
195 oldTop, oldBottom = self._textbox.yview()
196
197 disabled = (str(self._textbox.cget('state')) == 'disabled')
198 if disabled:
199 self._textbox.configure(state='normal')
200 self._textbox.insert('end', text)
201 if disabled:
202 self._textbox.configure(state='disabled')
203
204 if oldBottom == 1.0:
205 self._textbox.yview('moveto', 1.0)
206
207 # ======================================================================
208
209 # Configuration methods.
210
211 def _hscrollMode(self):
212 # The horizontal scroll mode has been configured.
213
214 mode = self['hscrollmode']
215
216 if mode == 'static':
217 if not self._horizScrollbarOn:
218 self._toggleHorizScrollbar()
219 elif mode == 'dynamic':
220 if self._horizScrollbarNeeded != self._horizScrollbarOn:
221 self._toggleHorizScrollbar()
222 elif mode == 'none':
223 if self._horizScrollbarOn:
224 self._toggleHorizScrollbar()
225 else:
226 message = 'bad hscrollmode option "%s": should be static, dynamic, or none' % mode
227 raise ValueError, message
228
229 self._configureScrollCommands()
230
231 def _vscrollMode(self):
232 # The vertical scroll mode has been configured.
233
234 mode = self['vscrollmode']
235
236 if mode == 'static':
237 if not self._vertScrollbarOn:
238 self._toggleVertScrollbar()
239 elif mode == 'dynamic':
240 if self._vertScrollbarNeeded != self._vertScrollbarOn:
241 self._toggleVertScrollbar()
242 elif mode == 'none':
243 if self._vertScrollbarOn:
244 self._toggleVertScrollbar()
245 else:
246 message = 'bad vscrollmode option "%s": should be static, dynamic, or none' % mode
247 raise ValueError, message
248
249 self._configureScrollCommands()
250
251 # ======================================================================
252
253 # Private methods.
254
255 def _configureScrollCommands(self):
256 # If both scrollmodes are not dynamic we can save a lot of
257 # time by not having to create an idle job to handle the
258 # scroll commands.
259
260 # Clean up previous scroll commands to prevent memory leak.
261 tclCommandName = str(self._textbox.cget('xscrollcommand'))
262 if tclCommandName != '':
263 self._textbox.deletecommand(tclCommandName)
264 tclCommandName = str(self._textbox.cget('yscrollcommand'))
265 if tclCommandName != '':
266 self._textbox.deletecommand(tclCommandName)
267
268 if self['hscrollmode'] == self['vscrollmode'] == 'dynamic':
269 self._textbox.configure(
270 xscrollcommand=self._scrollBothLater,
271 yscrollcommand=self._scrollBothLater
272 )
273 else:
274 self._textbox.configure(
275 xscrollcommand=self._scrollXNow,
276 yscrollcommand=self._scrollYNow
277 )
278
279 def _scrollXNow(self, first, last):
280 self._horizScrollbar.set(first, last)
281 self._horizScrollbarNeeded = ((first, last) != ('0', '1'))
282
283 # This code is the same as in _scrollBothNow. Keep it that way.
284 if self['hscrollmode'] == 'dynamic':
285 currentWidth = self._textbox.winfo_width()
286 if self._horizScrollbarNeeded != self._horizScrollbarOn:
287 if self._horizScrollbarNeeded or \
288 self._textWidth != currentWidth:
289 self._toggleHorizScrollbar()
290 self._textWidth = currentWidth
291
292 if self['columnheader']:
293 if self._columnheaderLastX != first:
294 self._columnheaderLastX = first
295 self._columnheader.xview('moveto', first)
296
297 def _scrollYNow(self, first, last):
298 if first == '0' and last == '0':
299 return
300 self._vertScrollbar.set(first, last)
301 self._vertScrollbarNeeded = ((first, last) != ('0', '1'))
302
303 if self['vscrollmode'] == 'dynamic':
304 if self._vertScrollbarNeeded != self._vertScrollbarOn:
305 self._toggleVertScrollbar()
306
307 if self['rowheader']:
308 if self._rowheaderLastY != first:
309 self._rowheaderLastY = first
310 self._rowheader.yview('moveto', first)
311
312 def _scrollBothLater(self, first, last):
313 # Called by the text widget to set the horizontal or vertical
314 # scrollbar when it has scrolled or changed size or contents.
315
316 if self.scrollTimer is None:
317 self.scrollTimer = self.after_idle(self._scrollBothNow)
318
319 def _scrollBothNow(self):
320 # This performs the function of _scrollXNow and _scrollYNow.
321 # If one is changed, the other should be updated to match.
322 self.scrollTimer = None
323
324 # Call update_idletasks to make sure that the containing frame
325 # has been resized before we attempt to set the scrollbars.
326 # Otherwise the scrollbars may be mapped/unmapped continuously.
327 self._scrollRecurse = self._scrollRecurse + 1
328 self.update_idletasks()
329 self._scrollRecurse = self._scrollRecurse - 1
330 if self._scrollRecurse != 0:
331 return
332
333 xview = self._textbox.xview()
334 yview = self._textbox.yview()
335
336 # The text widget returns a yview of (0.0, 0.0) just after it
337 # has been created. Ignore this.
338 if yview == (0.0, 0.0):
339 return
340
341 if self['columnheader']:
342 if self._columnheaderLastX != xview[0]:
343 self._columnheaderLastX = xview[0]
344 self._columnheader.xview('moveto', xview[0])
345 if self['rowheader']:
346 if self._rowheaderLastY != yview[0]:
347 self._rowheaderLastY = yview[0]
348 self._rowheader.yview('moveto', yview[0])
349
350 self._horizScrollbar.set(xview[0], xview[1])
351 self._vertScrollbar.set(yview[0], yview[1])
352
353 self._horizScrollbarNeeded = (xview != (0.0, 1.0))
354 self._vertScrollbarNeeded = (yview != (0.0, 1.0))
355
356 # If both horizontal and vertical scrollmodes are dynamic and
357 # currently only one scrollbar is mapped and both should be
358 # toggled, then unmap the mapped scrollbar. This prevents a
359 # continuous mapping and unmapping of the scrollbars.
360 if (self['hscrollmode'] == self['vscrollmode'] == 'dynamic' and
361 self._horizScrollbarNeeded != self._horizScrollbarOn and
362 self._vertScrollbarNeeded != self._vertScrollbarOn and
363 self._vertScrollbarOn != self._horizScrollbarOn):
364 if self._horizScrollbarOn:
365 self._toggleHorizScrollbar()
366 else:
367 self._toggleVertScrollbar()
368 return
369
370 if self['hscrollmode'] == 'dynamic':
371
372 # The following test is done to prevent continuous
373 # mapping and unmapping of the horizontal scrollbar.
374 # This may occur when some event (scrolling, resizing
375 # or text changes) modifies the displayed text such
376 # that the bottom line in the window is the longest
377 # line displayed. If this causes the horizontal
378 # scrollbar to be mapped, the scrollbar may "cover up"
379 # the bottom line, which would mean that the scrollbar
380 # is no longer required. If the scrollbar is then
381 # unmapped, the bottom line will then become visible
382 # again, which would cause the scrollbar to be mapped
383 # again, and so on...
384 #
385 # The idea is that, if the width of the text widget
386 # has not changed and the scrollbar is currently
387 # mapped, then do not unmap the scrollbar even if it
388 # is no longer required. This means that, during
389 # normal scrolling of the text, once the horizontal
390 # scrollbar has been mapped it will not be unmapped
391 # (until the width of the text widget changes).
392
393 currentWidth = self._textbox.winfo_width()
394 if self._horizScrollbarNeeded != self._horizScrollbarOn:
395 if self._horizScrollbarNeeded or \
396 self._textWidth != currentWidth:
397 self._toggleHorizScrollbar()
398 self._textWidth = currentWidth
399
400 if self['vscrollmode'] == 'dynamic':
401 if self._vertScrollbarNeeded != self._vertScrollbarOn:
402 self._toggleVertScrollbar()
403
404 def _columnheaderscrolled(self, first, last):
405 if self._textboxLastX != first:
406 self._textboxLastX = first
407 self._textbox.xview('moveto', first)
408
409 def _rowheaderscrolled(self, first, last):
410 if self._textboxLastY != first:
411 self._textboxLastY = first
412 self._textbox.yview('moveto', first)
413
414 def _toggleHorizScrollbar(self):
415
416 self._horizScrollbarOn = not self._horizScrollbarOn
417
418 interior = self.interior()
419 if self._horizScrollbarOn:
420 self._horizScrollbar.grid(row = 6, column = 4, sticky = 'news')
421 interior.grid_rowconfigure(5, minsize = self['scrollmargin'])
422 else:
423 self._horizScrollbar.grid_forget()
424 interior.grid_rowconfigure(5, minsize = 0)
425
426 def _toggleVertScrollbar(self):
427
428 self._vertScrollbarOn = not self._vertScrollbarOn
429
430 interior = self.interior()
431 if self._vertScrollbarOn:
432 self._vertScrollbar.grid(row = 4, column = 6, sticky = 'news')
433 interior.grid_columnconfigure(5, minsize = self['scrollmargin'])
434 else:
435 self._vertScrollbar.grid_forget()
436 interior.grid_columnconfigure(5, minsize = 0)
437
438 # Need to explicitly forward this to override the stupid
439 # (grid_)bbox method inherited from Tkinter.Frame.Grid.
440 def bbox(self, index):
441 return self._textbox.bbox(index)
442
443Pmw.forwardmethods(ScrolledText, Tkinter.Text, '_textbox')