Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / devtools / v8plus / lib / python2.4 / site-packages / Pmw / Pmw_1_2 / lib / PmwNoteBook.py
CommitLineData
920dae64
AT
1import string
2import types
3import Tkinter
4import Pmw
5
6class NoteBook(Pmw.MegaArchetype):
7
8 def __init__(self, parent = None, **kw):
9
10 # Define the megawidget options.
11 INITOPT = Pmw.INITOPT
12 optiondefs = (
13 ('hull_highlightthickness', 0, None),
14 ('hull_borderwidth', 0, None),
15 ('arrownavigation', 1, INITOPT),
16 ('borderwidth', 2, INITOPT),
17 ('createcommand', None, None),
18 ('lowercommand', None, None),
19 ('pagemargin', 4, INITOPT),
20 ('raisecommand', None, None),
21 ('tabpos', 'n', INITOPT),
22 )
23 self.defineoptions(kw, optiondefs, dynamicGroups = ('Page', 'Tab'))
24
25 # Initialise the base class (after defining the options).
26 Pmw.MegaArchetype.__init__(self, parent, Tkinter.Canvas)
27
28 self.bind('<Map>', self._handleMap)
29 self.bind('<Configure>', self._handleConfigure)
30
31 tabpos = self['tabpos']
32 if tabpos is not None and tabpos != 'n':
33 raise ValueError, \
34 'bad tabpos option %s: should be n or None' % repr(tabpos)
35 self._withTabs = (tabpos is not None)
36 self._pageMargin = self['pagemargin']
37 self._borderWidth = self['borderwidth']
38
39 # Use a dictionary as a set of bits indicating what needs to
40 # be redisplayed the next time _layout() is called. If
41 # dictionary contains 'topPage' key, the value is the new top
42 # page to be displayed. None indicates that all pages have
43 # been deleted and that _layout() should draw a border under where
44 # the tabs should be.
45 self._pending = {}
46 self._pending['size'] = 1
47 self._pending['borderColor'] = 1
48 self._pending['topPage'] = None
49 if self._withTabs:
50 self._pending['tabs'] = 1
51
52 self._canvasSize = None # This gets set by <Configure> events
53
54 # Set initial height of space for tabs
55 if self._withTabs:
56 self.tabBottom = 35
57 else:
58 self.tabBottom = 0
59
60 self._lightBorderColor, self._darkBorderColor = \
61 Pmw.Color.bordercolors(self, self['hull_background'])
62
63 self._pageNames = [] # List of page names
64
65 # Map from page name to page info. Each item is itself a
66 # dictionary containing the following items:
67 # page the Tkinter.Frame widget for the page
68 # created set to true the first time the page is raised
69 # tabbutton the Tkinter.Button widget for the button (if any)
70 # tabreqwidth requested width of the tab
71 # tabreqheight requested height of the tab
72 # tabitems the canvas items for the button: the button
73 # window item, the lightshadow and the darkshadow
74 # left the left and right canvas coordinates of the tab
75 # right
76 self._pageAttrs = {}
77
78 # Name of page currently on top (actually displayed, using
79 # create_window, not pending). Ignored if current top page
80 # has been deleted or new top page is pending. None indicates
81 # no pages in notebook.
82 self._topPageName = None
83
84 # Canvas items used:
85 # Per tab:
86 # top and left shadow
87 # right shadow
88 # button
89 # Per notebook:
90 # page
91 # top page
92 # left shadow
93 # bottom and right shadow
94 # top (one or two items)
95
96 # Canvas tags used:
97 # lighttag - top and left shadows of tabs and page
98 # darktag - bottom and right shadows of tabs and page
99 # (if no tabs then these are reversed)
100 # (used to color the borders by recolorborders)
101
102 # Create page border shadows.
103 if self._withTabs:
104 self._pageLeftBorder = self.create_polygon(0, 0, 0, 0, 0, 0,
105 fill = self._lightBorderColor, tags = 'lighttag')
106 self._pageBottomRightBorder = self.create_polygon(0, 0, 0, 0, 0, 0,
107 fill = self._darkBorderColor, tags = 'darktag')
108 self._pageTop1Border = self.create_polygon(0, 0, 0, 0, 0, 0,
109 fill = self._darkBorderColor, tags = 'lighttag')
110 self._pageTop2Border = self.create_polygon(0, 0, 0, 0, 0, 0,
111 fill = self._darkBorderColor, tags = 'lighttag')
112 else:
113 self._pageLeftBorder = self.create_polygon(0, 0, 0, 0, 0, 0,
114 fill = self._darkBorderColor, tags = 'darktag')
115 self._pageBottomRightBorder = self.create_polygon(0, 0, 0, 0, 0, 0,
116 fill = self._lightBorderColor, tags = 'lighttag')
117 self._pageTopBorder = self.create_polygon(0, 0, 0, 0, 0, 0,
118 fill = self._darkBorderColor, tags = 'darktag')
119
120 # Check keywords and initialise options.
121 self.initialiseoptions()
122
123 def insert(self, pageName, before = 0, **kw):
124 if self._pageAttrs.has_key(pageName):
125 msg = 'Page "%s" already exists.' % pageName
126 raise ValueError, msg
127
128 # Do this early to catch bad <before> spec before creating any items.
129 beforeIndex = self.index(before, 1)
130
131 pageOptions = {}
132 if self._withTabs:
133 # Default tab button options.
134 tabOptions = {
135 'text' : pageName,
136 'borderwidth' : 0,
137 }
138
139 # Divide the keyword options into the 'page_' and 'tab_' options.
140 for key in kw.keys():
141 if key[:5] == 'page_':
142 pageOptions[key[5:]] = kw[key]
143 del kw[key]
144 elif self._withTabs and key[:4] == 'tab_':
145 tabOptions[key[4:]] = kw[key]
146 del kw[key]
147 else:
148 raise KeyError, 'Unknown option "' + key + '"'
149
150 # Create the frame to contain the page.
151 page = apply(self.createcomponent, (pageName,
152 (), 'Page',
153 Tkinter.Frame, self._hull), pageOptions)
154
155 attributes = {}
156 attributes['page'] = page
157 attributes['created'] = 0
158
159 if self._withTabs:
160 # Create the button for the tab.
161 def raiseThisPage(self = self, pageName = pageName):
162 self.selectpage(pageName)
163 tabOptions['command'] = raiseThisPage
164 tab = apply(self.createcomponent, (pageName + '-tab',
165 (), 'Tab',
166 Tkinter.Button, self._hull), tabOptions)
167
168 if self['arrownavigation']:
169 # Allow the use of the arrow keys for Tab navigation:
170 def next(event, self = self, pageName = pageName):
171 self.nextpage(pageName)
172 def prev(event, self = self, pageName = pageName):
173 self.previouspage(pageName)
174 tab.bind('<Left>', prev)
175 tab.bind('<Right>', next)
176
177 attributes['tabbutton'] = tab
178 attributes['tabreqwidth'] = tab.winfo_reqwidth()
179 attributes['tabreqheight'] = tab.winfo_reqheight()
180
181 # Create the canvas item to manage the tab's button and the items
182 # for the tab's shadow.
183 windowitem = self.create_window(0, 0, window = tab, anchor = 'nw')
184 lightshadow = self.create_polygon(0, 0, 0, 0, 0, 0,
185 tags = 'lighttag', fill = self._lightBorderColor)
186 darkshadow = self.create_polygon(0, 0, 0, 0, 0, 0,
187 tags = 'darktag', fill = self._darkBorderColor)
188 attributes['tabitems'] = (windowitem, lightshadow, darkshadow)
189 self._pending['tabs'] = 1
190
191 self._pageAttrs[pageName] = attributes
192 self._pageNames.insert(beforeIndex, pageName)
193
194 # If this is the first page added, make it the new top page
195 # and call the create and raise callbacks.
196 if self.getcurselection() is None:
197 self._pending['topPage'] = pageName
198 self._raiseNewTop(pageName)
199
200 self._layout()
201 return page
202
203 def add(self, pageName, **kw):
204 return apply(self.insert, (pageName, len(self._pageNames)), kw)
205
206 def delete(self, *pageNames):
207 newTopPage = 0
208 for page in pageNames:
209 pageIndex = self.index(page)
210 pageName = self._pageNames[pageIndex]
211 pageInfo = self._pageAttrs[pageName]
212
213 if self.getcurselection() == pageName:
214 if len(self._pageNames) == 1:
215 newTopPage = 0
216 self._pending['topPage'] = None
217 elif pageIndex == len(self._pageNames) - 1:
218 newTopPage = 1
219 self._pending['topPage'] = self._pageNames[pageIndex - 1]
220 else:
221 newTopPage = 1
222 self._pending['topPage'] = self._pageNames[pageIndex + 1]
223
224 if self._topPageName == pageName:
225 self._hull.delete(self._topPageItem)
226 self._topPageName = None
227
228 if self._withTabs:
229 self.destroycomponent(pageName + '-tab')
230 apply(self._hull.delete, pageInfo['tabitems'])
231 self.destroycomponent(pageName)
232 del self._pageAttrs[pageName]
233 del self._pageNames[pageIndex]
234
235 # If the old top page was deleted and there are still pages
236 # left in the notebook, call the create and raise callbacks.
237 if newTopPage:
238 pageName = self._pending['topPage']
239 self._raiseNewTop(pageName)
240
241 if self._withTabs:
242 self._pending['tabs'] = 1
243 self._layout()
244
245 def page(self, pageIndex):
246 pageName = self._pageNames[self.index(pageIndex)]
247 return self._pageAttrs[pageName]['page']
248
249 def pagenames(self):
250 return list(self._pageNames)
251
252 def getcurselection(self):
253 if self._pending.has_key('topPage'):
254 return self._pending['topPage']
255 else:
256 return self._topPageName
257
258 def tab(self, pageIndex):
259 if self._withTabs:
260 pageName = self._pageNames[self.index(pageIndex)]
261 return self._pageAttrs[pageName]['tabbutton']
262 else:
263 return None
264
265 def index(self, index, forInsert = 0):
266 listLength = len(self._pageNames)
267 if type(index) == types.IntType:
268 if forInsert and index <= listLength:
269 return index
270 elif not forInsert and index < listLength:
271 return index
272 else:
273 raise ValueError, 'index "%s" is out of range' % index
274 elif index is Pmw.END:
275 if forInsert:
276 return listLength
277 elif listLength > 0:
278 return listLength - 1
279 else:
280 raise ValueError, 'NoteBook has no pages'
281 elif index is Pmw.SELECT:
282 if listLength == 0:
283 raise ValueError, 'NoteBook has no pages'
284 return self._pageNames.index(self.getcurselection())
285 else:
286 if index in self._pageNames:
287 return self._pageNames.index(index)
288 validValues = 'a name, a number, Pmw.END or Pmw.SELECT'
289 raise ValueError, \
290 'bad index "%s": must be %s' % (index, validValues)
291
292 def selectpage(self, page):
293 pageName = self._pageNames[self.index(page)]
294 oldTopPage = self.getcurselection()
295 if pageName != oldTopPage:
296 self._pending['topPage'] = pageName
297 if oldTopPage == self._topPageName:
298 self._hull.delete(self._topPageItem)
299 cmd = self['lowercommand']
300 if cmd is not None:
301 cmd(oldTopPage)
302 self._raiseNewTop(pageName)
303
304 self._layout()
305
306 # Set focus to the tab of new top page:
307 if self._withTabs and self['arrownavigation']:
308 self._pageAttrs[pageName]['tabbutton'].focus_set()
309
310 def previouspage(self, pageIndex = None):
311 if pageIndex is None:
312 curpage = self.index(Pmw.SELECT)
313 else:
314 curpage = self.index(pageIndex)
315 if curpage > 0:
316 self.selectpage(curpage - 1)
317
318 def nextpage(self, pageIndex = None):
319 if pageIndex is None:
320 curpage = self.index(Pmw.SELECT)
321 else:
322 curpage = self.index(pageIndex)
323 if curpage < len(self._pageNames) - 1:
324 self.selectpage(curpage + 1)
325
326 def setnaturalsize(self, pageNames = None):
327 self.update_idletasks()
328 maxPageWidth = 1
329 maxPageHeight = 1
330 if pageNames is None:
331 pageNames = self.pagenames()
332 for pageName in pageNames:
333 pageInfo = self._pageAttrs[pageName]
334 page = pageInfo['page']
335 w = page.winfo_reqwidth()
336 h = page.winfo_reqheight()
337 if maxPageWidth < w:
338 maxPageWidth = w
339 if maxPageHeight < h:
340 maxPageHeight = h
341 pageBorder = self._borderWidth + self._pageMargin
342 width = maxPageWidth + pageBorder * 2
343 height = maxPageHeight + pageBorder * 2
344
345 if self._withTabs:
346 maxTabHeight = 0
347 for pageInfo in self._pageAttrs.values():
348 if maxTabHeight < pageInfo['tabreqheight']:
349 maxTabHeight = pageInfo['tabreqheight']
350 height = height + maxTabHeight + self._borderWidth * 1.5
351
352 # Note that, since the hull is a canvas, the width and height
353 # options specify the geometry *inside* the borderwidth and
354 # highlightthickness.
355 self.configure(hull_width = width, hull_height = height)
356
357 def recolorborders(self):
358 self._pending['borderColor'] = 1
359 self._layout()
360
361 def _handleMap(self, event):
362 self._layout()
363
364 def _handleConfigure(self, event):
365 self._canvasSize = (event.width, event.height)
366 self._pending['size'] = 1
367 self._layout()
368
369 def _raiseNewTop(self, pageName):
370 if not self._pageAttrs[pageName]['created']:
371 self._pageAttrs[pageName]['created'] = 1
372 cmd = self['createcommand']
373 if cmd is not None:
374 cmd(pageName)
375 cmd = self['raisecommand']
376 if cmd is not None:
377 cmd(pageName)
378
379 # This is the vertical layout of the notebook, from top (assuming
380 # tabpos is 'n'):
381 # hull highlightthickness (top)
382 # hull borderwidth (top)
383 # borderwidth (top border of tabs)
384 # borderwidth * 0.5 (space for bevel)
385 # tab button (maximum of requested height of all tab buttons)
386 # borderwidth (border between tabs and page)
387 # pagemargin (top)
388 # the page itself
389 # pagemargin (bottom)
390 # borderwidth (border below page)
391 # hull borderwidth (bottom)
392 # hull highlightthickness (bottom)
393 #
394 # canvasBorder is sum of top two elements.
395 # tabBottom is sum of top five elements.
396 #
397 # Horizontal layout (and also vertical layout when tabpos is None):
398 # hull highlightthickness
399 # hull borderwidth
400 # borderwidth
401 # pagemargin
402 # the page itself
403 # pagemargin
404 # borderwidth
405 # hull borderwidth
406 # hull highlightthickness
407 #
408 def _layout(self):
409 if not self.winfo_ismapped() or self._canvasSize is None:
410 # Don't layout if the window is not displayed, or we
411 # haven't yet received a <Configure> event.
412 return
413
414 hullWidth, hullHeight = self._canvasSize
415 borderWidth = self._borderWidth
416 canvasBorder = string.atoi(self._hull['borderwidth']) + \
417 string.atoi(self._hull['highlightthickness'])
418 if not self._withTabs:
419 self.tabBottom = canvasBorder
420 oldTabBottom = self.tabBottom
421
422 if self._pending.has_key('borderColor'):
423 self._lightBorderColor, self._darkBorderColor = \
424 Pmw.Color.bordercolors(self, self['hull_background'])
425
426 # Draw all the tabs.
427 if self._withTabs and (self._pending.has_key('tabs') or
428 self._pending.has_key('size')):
429 # Find total requested width and maximum requested height
430 # of tabs.
431 sumTabReqWidth = 0
432 maxTabHeight = 0
433 for pageInfo in self._pageAttrs.values():
434 sumTabReqWidth = sumTabReqWidth + pageInfo['tabreqwidth']
435 if maxTabHeight < pageInfo['tabreqheight']:
436 maxTabHeight = pageInfo['tabreqheight']
437 if maxTabHeight != 0:
438 # Add the top tab border plus a bit for the angled corners
439 self.tabBottom = canvasBorder + maxTabHeight + borderWidth * 1.5
440
441 # Prepare for drawing the border around each tab button.
442 tabTop = canvasBorder
443 tabTop2 = tabTop + borderWidth
444 tabTop3 = tabTop + borderWidth * 1.5
445 tabBottom2 = self.tabBottom
446 tabBottom = self.tabBottom + borderWidth
447
448 numTabs = len(self._pageNames)
449 availableWidth = hullWidth - 2 * canvasBorder - \
450 numTabs * 2 * borderWidth
451 x = canvasBorder
452 cumTabReqWidth = 0
453 cumTabWidth = 0
454
455 # Position all the tabs.
456 for pageName in self._pageNames:
457 pageInfo = self._pageAttrs[pageName]
458 (windowitem, lightshadow, darkshadow) = pageInfo['tabitems']
459 if sumTabReqWidth <= availableWidth:
460 tabwidth = pageInfo['tabreqwidth']
461 else:
462 # This ugly calculation ensures that, when the
463 # notebook is not wide enough for the requested
464 # widths of the tabs, the total width given to
465 # the tabs exactly equals the available width,
466 # without rounding errors.
467 cumTabReqWidth = cumTabReqWidth + pageInfo['tabreqwidth']
468 tmp = (2*cumTabReqWidth*availableWidth + sumTabReqWidth) \
469 / (2 * sumTabReqWidth)
470 tabwidth = tmp - cumTabWidth
471 cumTabWidth = tmp
472
473 # Position the tab's button canvas item.
474 self.coords(windowitem, x + borderWidth, tabTop3)
475 self.itemconfigure(windowitem,
476 width = tabwidth, height = maxTabHeight)
477
478 # Make a beautiful border around the tab.
479 left = x
480 left2 = left + borderWidth
481 left3 = left + borderWidth * 1.5
482 right = left + tabwidth + 2 * borderWidth
483 right2 = left + tabwidth + borderWidth
484 right3 = left + tabwidth + borderWidth * 0.5
485
486 self.coords(lightshadow,
487 left, tabBottom2, left, tabTop2, left2, tabTop,
488 right2, tabTop, right3, tabTop2, left3, tabTop2,
489 left2, tabTop3, left2, tabBottom,
490 )
491 self.coords(darkshadow,
492 right2, tabTop, right, tabTop2, right, tabBottom2,
493 right2, tabBottom, right2, tabTop3, right3, tabTop2,
494 )
495 pageInfo['left'] = left
496 pageInfo['right'] = right
497
498 x = x + tabwidth + 2 * borderWidth
499
500 # Redraw shadow under tabs so that it appears that tab for old
501 # top page is lowered and that tab for new top page is raised.
502 if self._withTabs and (self._pending.has_key('topPage') or
503 self._pending.has_key('tabs') or self._pending.has_key('size')):
504
505 if self.getcurselection() is None:
506 # No pages, so draw line across top of page area.
507 self.coords(self._pageTop1Border,
508 canvasBorder, self.tabBottom,
509 hullWidth - canvasBorder, self.tabBottom,
510 hullWidth - canvasBorder - borderWidth,
511 self.tabBottom + borderWidth,
512 borderWidth + canvasBorder, self.tabBottom + borderWidth,
513 )
514
515 # Ignore second top border.
516 self.coords(self._pageTop2Border, 0, 0, 0, 0, 0, 0)
517 else:
518 # Draw two lines, one on each side of the tab for the
519 # top page, so that the tab appears to be raised.
520 pageInfo = self._pageAttrs[self.getcurselection()]
521 left = pageInfo['left']
522 right = pageInfo['right']
523 self.coords(self._pageTop1Border,
524 canvasBorder, self.tabBottom,
525 left, self.tabBottom,
526 left + borderWidth, self.tabBottom + borderWidth,
527 canvasBorder + borderWidth, self.tabBottom + borderWidth,
528 )
529
530 self.coords(self._pageTop2Border,
531 right, self.tabBottom,
532 hullWidth - canvasBorder, self.tabBottom,
533 hullWidth - canvasBorder - borderWidth,
534 self.tabBottom + borderWidth,
535 right - borderWidth, self.tabBottom + borderWidth,
536 )
537
538 # Prevent bottom of dark border of tabs appearing over
539 # page top border.
540 self.tag_raise(self._pageTop1Border)
541 self.tag_raise(self._pageTop2Border)
542
543 # Position the page border shadows.
544 if self._pending.has_key('size') or oldTabBottom != self.tabBottom:
545
546 self.coords(self._pageLeftBorder,
547 canvasBorder, self.tabBottom,
548 borderWidth + canvasBorder,
549 self.tabBottom + borderWidth,
550 borderWidth + canvasBorder,
551 hullHeight - canvasBorder - borderWidth,
552 canvasBorder, hullHeight - canvasBorder,
553 )
554
555 self.coords(self._pageBottomRightBorder,
556 hullWidth - canvasBorder, self.tabBottom,
557 hullWidth - canvasBorder, hullHeight - canvasBorder,
558 canvasBorder, hullHeight - canvasBorder,
559 borderWidth + canvasBorder,
560 hullHeight - canvasBorder - borderWidth,
561 hullWidth - canvasBorder - borderWidth,
562 hullHeight - canvasBorder - borderWidth,
563 hullWidth - canvasBorder - borderWidth,
564 self.tabBottom + borderWidth,
565 )
566
567 if not self._withTabs:
568 self.coords(self._pageTopBorder,
569 canvasBorder, self.tabBottom,
570 hullWidth - canvasBorder, self.tabBottom,
571 hullWidth - canvasBorder - borderWidth,
572 self.tabBottom + borderWidth,
573 borderWidth + canvasBorder, self.tabBottom + borderWidth,
574 )
575
576 # Color borders.
577 if self._pending.has_key('borderColor'):
578 self.itemconfigure('lighttag', fill = self._lightBorderColor)
579 self.itemconfigure('darktag', fill = self._darkBorderColor)
580
581 newTopPage = self._pending.get('topPage')
582 pageBorder = borderWidth + self._pageMargin
583
584 # Raise new top page.
585 if newTopPage is not None:
586 self._topPageName = newTopPage
587 self._topPageItem = self.create_window(
588 pageBorder + canvasBorder, self.tabBottom + pageBorder,
589 window = self._pageAttrs[newTopPage]['page'],
590 anchor = 'nw',
591 )
592
593 # Change position of top page if tab height has changed.
594 if self._topPageName is not None and oldTabBottom != self.tabBottom:
595 self.coords(self._topPageItem,
596 pageBorder + canvasBorder, self.tabBottom + pageBorder)
597
598 # Change size of top page if,
599 # 1) there is a new top page.
600 # 2) canvas size has changed, but not if there is no top
601 # page (eg: initially or when all pages deleted).
602 # 3) tab height has changed, due to difference in the height of a tab
603 if (newTopPage is not None or \
604 self._pending.has_key('size') and self._topPageName is not None
605 or oldTabBottom != self.tabBottom):
606 self.itemconfigure(self._topPageItem,
607 width = hullWidth - 2 * canvasBorder - pageBorder * 2,
608 height = hullHeight - 2 * canvasBorder - pageBorder * 2 -
609 (self.tabBottom - canvasBorder),
610 )
611
612 self._pending = {}
613
614# Need to do forwarding to get the pack, grid, etc methods.
615# Unfortunately this means that all the other canvas methods are also
616# forwarded.
617Pmw.forwardmethods(NoteBook, Tkinter.Canvas, '_hull')