Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | import string |
2 | import types | |
3 | import Tkinter | |
4 | import Pmw | |
5 | ||
6 | class 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. | |
617 | Pmw.forwardmethods(NoteBook, Tkinter.Canvas, '_hull') |