OSDN Git Service

Please enter the commit message for your changes. Lines starting
[eos/base.git] / util / src / TclTk / tk8.6.12 / generic / ttk / ttkNotebook.c
1 /*
2  * Copyright (c) 2004, Joe English
3  */
4
5 #include "tkInt.h"
6
7 #include "ttkTheme.h"
8 #include "ttkWidget.h"
9 #include "ttkManager.h"
10
11 #define MIN(a,b) ((a) < (b) ? (a) : (b))
12 #define MAX(a,b) ((a) > (b) ? (a) : (b))
13
14 /*------------------------------------------------------------------------
15  * +++ Tab resources.
16  */
17
18 #define DEFAULT_MIN_TAB_WIDTH 24
19
20 static const char *const TabStateStrings[] = { "normal", "disabled", "hidden", 0 };
21 typedef enum {
22     TAB_STATE_NORMAL, TAB_STATE_DISABLED, TAB_STATE_HIDDEN
23 } TAB_STATE;
24
25 typedef struct
26 {
27     /* Internal data:
28      */
29     int         width, height;          /* Requested size of tab */
30     Ttk_Box     parcel;                 /* Tab position */
31
32     /* Tab options:
33      */
34     TAB_STATE   state;
35
36     /* Child window options:
37      */
38     Tcl_Obj     *paddingObj;            /* Padding inside pane */
39     Ttk_Padding padding;
40     Tcl_Obj     *stickyObj;
41     Ttk_Sticky  sticky;
42
43     /* Label options:
44      */
45     Tcl_Obj *textObj;
46     Tcl_Obj *imageObj;
47     Tcl_Obj *compoundObj;
48     Tcl_Obj *underlineObj;
49
50 } Tab;
51
52 /* Two different option tables are used for tabs:
53  * TabOptionSpecs is used to draw the tab, and only includes resources
54  * relevant to the tab.
55  *
56  * PaneOptionSpecs includes additional options for child window placement
57  * and is used to configure the content window.
58  */
59 static Tk_OptionSpec TabOptionSpecs[] =
60 {
61     {TK_OPTION_STRING_TABLE, "-state", "", "",
62         "normal", -1,Tk_Offset(Tab,state),
63         0, (void *)TabStateStrings, 0 },
64     {TK_OPTION_STRING, "-text", "text", "Text", "",
65         Tk_Offset(Tab,textObj), -1, 0,0,GEOMETRY_CHANGED },
66     {TK_OPTION_STRING, "-image", "image", "Image", NULL/*default*/,
67         Tk_Offset(Tab,imageObj), -1, TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
68     {TK_OPTION_STRING_TABLE, "-compound", "compound", "Compound",
69         NULL, Tk_Offset(Tab,compoundObj), -1,
70         TK_OPTION_NULL_OK, (void *)ttkCompoundStrings, GEOMETRY_CHANGED },
71     {TK_OPTION_INT, "-underline", "underline", "Underline", "-1",
72         Tk_Offset(Tab,underlineObj), -1, 0,0,GEOMETRY_CHANGED },
73     {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0 }
74 };
75
76 static Tk_OptionSpec PaneOptionSpecs[] =
77 {
78     {TK_OPTION_STRING, "-padding", "padding", "Padding", "0",
79         Tk_Offset(Tab,paddingObj), -1, 0,0,GEOMETRY_CHANGED },
80     {TK_OPTION_STRING, "-sticky", "sticky", "Sticky", "nsew",
81         Tk_Offset(Tab,stickyObj), -1, 0,0,GEOMETRY_CHANGED },
82
83     WIDGET_INHERIT_OPTIONS(TabOptionSpecs)
84 };
85
86 /*------------------------------------------------------------------------
87  * +++ Notebook resources.
88  */
89 typedef struct
90 {
91     Tcl_Obj *widthObj;          /* Default width */
92     Tcl_Obj *heightObj;         /* Default height */
93     Tcl_Obj *paddingObj;        /* Padding around notebook */
94
95     Ttk_Manager *mgr;           /* Geometry manager */
96     Tk_OptionTable tabOptionTable;      /* Tab options */
97     Tk_OptionTable paneOptionTable;     /* Tab+pane options */
98     int currentIndex;           /* index of currently selected tab */
99     int activeIndex;            /* index of currently active tab */
100     Ttk_Layout tabLayout;       /* Sublayout for tabs */
101
102     Ttk_Box clientArea;         /* Where to pack content widgets */
103 } NotebookPart;
104
105 typedef struct
106 {
107     WidgetCore core;
108     NotebookPart notebook;
109 } Notebook;
110
111 static Tk_OptionSpec NotebookOptionSpecs[] =
112 {
113     {TK_OPTION_INT, "-width", "width", "Width", "0",
114         Tk_Offset(Notebook,notebook.widthObj),-1,
115         0,0,GEOMETRY_CHANGED },
116     {TK_OPTION_INT, "-height", "height", "Height", "0",
117         Tk_Offset(Notebook,notebook.heightObj),-1,
118         0,0,GEOMETRY_CHANGED },
119     {TK_OPTION_STRING, "-padding", "padding", "Padding", NULL,
120         Tk_Offset(Notebook,notebook.paddingObj),-1,
121         TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
122
123     WIDGET_TAKEFOCUS_TRUE,
124     WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs)
125 };
126
127 /* Notebook style options:
128  */
129 typedef struct
130 {
131     Ttk_PositionSpec    tabPosition;    /* Where to place tabs */
132     Ttk_Padding         tabMargins;     /* Margins around tab row */
133     Ttk_PositionSpec    tabPlacement;   /* How to pack tabs within tab row */
134     Ttk_Orient          tabOrient;      /* ... */
135     int                 minTabWidth;    /* Minimum tab width */
136     Ttk_Padding         padding;        /* External padding */
137 } NotebookStyle;
138
139 static void NotebookStyleOptions(Notebook *nb, NotebookStyle *nbstyle)
140 {
141     Tcl_Obj *objPtr;
142
143     nbstyle->tabPosition = TTK_PACK_TOP | TTK_STICK_W;
144     if ((objPtr = Ttk_QueryOption(nb->core.layout, "-tabposition", 0)) != 0) {
145         TtkGetLabelAnchorFromObj(NULL, objPtr, &nbstyle->tabPosition);
146     }
147
148     /* Guess default tabPlacement as function of tabPosition:
149      */
150     if (nbstyle->tabPosition & TTK_PACK_LEFT) {
151         nbstyle->tabPlacement = TTK_PACK_TOP | TTK_STICK_E;
152     } else if (nbstyle->tabPosition & TTK_PACK_RIGHT) {
153         nbstyle->tabPlacement = TTK_PACK_TOP | TTK_STICK_W;
154     } else if (nbstyle->tabPosition & TTK_PACK_BOTTOM) {
155         nbstyle->tabPlacement = TTK_PACK_LEFT | TTK_STICK_N;
156     } else { /* Assume TTK_PACK_TOP */
157         nbstyle->tabPlacement = TTK_PACK_LEFT | TTK_STICK_S;
158     }
159     if ((objPtr = Ttk_QueryOption(nb->core.layout, "-tabplacement", 0)) != 0) {
160         TtkGetLabelAnchorFromObj(NULL, objPtr, &nbstyle->tabPlacement);
161     }
162
163     /* Compute tabOrient as function of tabPlacement:
164      */
165     if (nbstyle->tabPlacement & (TTK_PACK_LEFT|TTK_PACK_RIGHT)) {
166         nbstyle->tabOrient = TTK_ORIENT_HORIZONTAL;
167     } else {
168         nbstyle->tabOrient = TTK_ORIENT_VERTICAL;
169     }
170
171     nbstyle->tabMargins = Ttk_UniformPadding(0);
172     if ((objPtr = Ttk_QueryOption(nb->core.layout, "-tabmargins", 0)) != 0) {
173         Ttk_GetBorderFromObj(NULL, objPtr, &nbstyle->tabMargins);
174     }
175
176     nbstyle->padding = Ttk_UniformPadding(0);
177     if ((objPtr = Ttk_QueryOption(nb->core.layout, "-padding", 0)) != 0) {
178         Ttk_GetPaddingFromObj(NULL,nb->core.tkwin,objPtr,&nbstyle->padding);
179     }
180
181     nbstyle->minTabWidth = DEFAULT_MIN_TAB_WIDTH;
182     if ((objPtr = Ttk_QueryOption(nb->core.layout, "-mintabwidth", 0)) != 0) {
183         Tcl_GetIntFromObj(NULL, objPtr, &nbstyle->minTabWidth);
184     }
185 }
186
187 /*------------------------------------------------------------------------
188  * +++ Tab management.
189  */
190
191 static Tab *CreateTab(Tcl_Interp *interp, Notebook *nb, Tk_Window window)
192 {
193     Tk_OptionTable optionTable = nb->notebook.paneOptionTable;
194     Tab *record = (Tab *)ckalloc(sizeof(Tab));
195     memset(record, 0, sizeof(Tab));
196
197     if (Tk_InitOptions(interp, (char *)record, optionTable, window) != TCL_OK) {
198         ckfree(record);
199         return NULL;
200     }
201
202     return record;
203 }
204
205 static void DestroyTab(Notebook *nb, Tab *tab)
206 {
207     void *record = tab;
208     Tk_FreeConfigOptions(record, nb->notebook.paneOptionTable, nb->core.tkwin);
209     ckfree(record);
210 }
211
212 static int ConfigureTab(
213     Tcl_Interp *interp, Notebook *nb, Tab *tab, Tk_Window window,
214     int objc, Tcl_Obj *const objv[])
215 {
216     Ttk_Sticky sticky = tab->sticky;
217     Ttk_Padding padding = tab->padding;
218     Tk_SavedOptions savedOptions;
219     int mask = 0;
220
221     if (Tk_SetOptions(interp, (void *)tab, nb->notebook.paneOptionTable,
222             objc, objv, window, &savedOptions, &mask) != TCL_OK)
223     {
224         return TCL_ERROR;
225     }
226
227     /* Check options:
228      * @@@ TODO: validate -image option.
229      */
230     if (Ttk_GetStickyFromObj(interp, tab->stickyObj, &sticky) != TCL_OK)
231     {
232         goto error;
233     }
234     if (Ttk_GetPaddingFromObj(interp, window, tab->paddingObj, &padding)
235             != TCL_OK)
236     {
237         goto error;
238     }
239
240     tab->sticky = sticky;
241     tab->padding = padding;
242
243     Tk_FreeSavedOptions(&savedOptions);
244     Ttk_ManagerSizeChanged(nb->notebook.mgr);
245     TtkRedisplayWidget(&nb->core);
246
247     return TCL_OK;
248 error:
249     Tk_RestoreSavedOptions(&savedOptions);
250     return TCL_ERROR;
251 }
252
253 /*
254  * IdentifyTab --
255  *      Return the index of the tab at point x,y,
256  *      or -1 if no tab at that point.
257  */
258 static int IdentifyTab(Notebook *nb, int x, int y)
259 {
260     int index;
261     for (index = 0; index < Ttk_NumberContent(nb->notebook.mgr); ++index) {
262         Tab *tab = (Tab *)Ttk_ContentData(nb->notebook.mgr,index);
263         if (    tab->state != TAB_STATE_HIDDEN
264              && Ttk_BoxContains(tab->parcel, x,y))
265         {
266             return index;
267         }
268     }
269     return -1;
270 }
271
272 /*
273  * ActivateTab --
274  *      Set the active tab index, redisplay if necessary.
275  */
276 static void ActivateTab(Notebook *nb, int index)
277 {
278     if (index != nb->notebook.activeIndex) {
279         nb->notebook.activeIndex = index;
280         TtkRedisplayWidget(&nb->core);
281     }
282 }
283
284 /*
285  * TabState --
286  *      Return the state of the specified tab, based on
287  *      notebook state, currentIndex, activeIndex, and user-specified tab state.
288  *      The USER1 bit is set for the leftmost visible tab, and USER2
289  *      is set for the rightmost visible tab.
290  */
291 static Ttk_State TabState(Notebook *nb, int index)
292 {
293     Ttk_State state = nb->core.state;
294     Tab *itab = (Tab *)Ttk_ContentData(nb->notebook.mgr, index);
295     int i = 0;
296
297     if (index == nb->notebook.currentIndex) {
298         state |= TTK_STATE_SELECTED;
299     } else {
300         state &= ~TTK_STATE_FOCUS;
301     }
302
303     if (index == nb->notebook.activeIndex) {
304         state |= TTK_STATE_ACTIVE;
305     }
306     for (i = 0; i < Ttk_NumberContent(nb->notebook.mgr); ++i) {
307         Tab *tab = (Tab *)Ttk_ContentData(nb->notebook.mgr, i);
308         if (tab->state == TAB_STATE_HIDDEN) {
309             continue;
310         }
311         if (index == i) {
312             state |= TTK_STATE_USER1;
313         }
314         break;
315     }
316     for (i = Ttk_NumberContent(nb->notebook.mgr) - 1; i != -1; --i) {
317         Tab *tab = (Tab *)Ttk_ContentData(nb->notebook.mgr, i);
318         if (tab->state == TAB_STATE_HIDDEN) {
319             continue;
320         }
321         if (index == i) {
322             state |= TTK_STATE_USER2;
323         }
324         break;
325     }
326     if (itab->state == TAB_STATE_DISABLED) {
327         state |= TTK_STATE_DISABLED;
328     }
329
330     return state;
331 }
332
333 /*------------------------------------------------------------------------
334  * +++ Geometry management - size computation.
335  */
336
337 /* TabrowSize --
338  *      Compute max height and total width of all tabs (horizontal layouts)
339  *      or total height and max width (vertical layouts).
340  *      The -mintabwidth style option is taken into account (for the width
341  *      only).
342  *
343  * Side effects:
344  *      Sets width and height fields for all tabs.
345  *
346  * Notes:
347  *      Hidden tabs are included in the perpendicular computation
348  *      (max height/width) but not parallel (total width/height).
349  */
350 static void TabrowSize(
351     Notebook *nb, Ttk_Orient orient, int minTabWidth, int *widthPtr, int *heightPtr)
352 {
353     Ttk_Layout tabLayout = nb->notebook.tabLayout;
354     int tabrowWidth = 0, tabrowHeight = 0;
355     int i;
356
357     for (i = 0; i < Ttk_NumberContent(nb->notebook.mgr); ++i) {
358         Tab *tab = (Tab *)Ttk_ContentData(nb->notebook.mgr, i);
359         Ttk_State tabState = TabState(nb,i);
360
361         Ttk_RebindSublayout(tabLayout, tab);
362         Ttk_LayoutSize(tabLayout,tabState,&tab->width,&tab->height);
363         tab->width = MAX(tab->width, minTabWidth);
364
365         if (orient == TTK_ORIENT_HORIZONTAL) {
366             tabrowHeight = MAX(tabrowHeight, tab->height);
367             if (tab->state != TAB_STATE_HIDDEN) { tabrowWidth += tab->width; }
368         } else {
369             tabrowWidth = MAX(tabrowWidth, tab->width);
370             if (tab->state != TAB_STATE_HIDDEN) { tabrowHeight += tab->height; }
371         }
372     }
373
374     *widthPtr = tabrowWidth;
375     *heightPtr = tabrowHeight;
376 }
377
378 /* NotebookSize -- GM and widget size hook.
379  *
380  * Total height is tab height + client area height + pane internal padding
381  * Total width is max(client width, tab width) + pane internal padding
382  * Client area size determined by max size of content windows,
383  * overridden by -width and/or -height if nonzero.
384  */
385
386 static int NotebookSize(void *clientData, int *widthPtr, int *heightPtr)
387 {
388     Notebook *nb = (Notebook *)clientData;
389     NotebookStyle nbstyle;
390     Ttk_Padding padding;
391     Ttk_Element clientNode = Ttk_FindElement(nb->core.layout, "client");
392     int clientWidth = 0, clientHeight = 0,
393         reqWidth = 0, reqHeight = 0,
394         tabrowWidth = 0, tabrowHeight = 0;
395     int i;
396
397     NotebookStyleOptions(nb, &nbstyle);
398
399     /* Compute max requested size of all content windows:
400      */
401     for (i = 0; i < Ttk_NumberContent(nb->notebook.mgr); ++i) {
402         Tk_Window window = Ttk_ContentWindow(nb->notebook.mgr, i);
403         Tab *tab = (Tab *)Ttk_ContentData(nb->notebook.mgr, i);
404         int width
405             = Tk_ReqWidth(window) + Ttk_PaddingWidth(tab->padding);
406         int height
407             = Tk_ReqHeight(window) + Ttk_PaddingHeight(tab->padding);
408
409         clientWidth = MAX(clientWidth, width);
410         clientHeight = MAX(clientHeight, height);
411     }
412
413     /* Client width/height overridable by widget options:
414      */
415     Tcl_GetIntFromObj(NULL, nb->notebook.widthObj,&reqWidth);
416     Tcl_GetIntFromObj(NULL, nb->notebook.heightObj,&reqHeight);
417     if (reqWidth > 0)
418         clientWidth = reqWidth;
419     if (reqHeight > 0)
420         clientHeight = reqHeight;
421
422     /* Tab row:
423      */
424     TabrowSize(nb, nbstyle.tabOrient, nbstyle.minTabWidth, &tabrowWidth, &tabrowHeight);
425     tabrowHeight += Ttk_PaddingHeight(nbstyle.tabMargins);
426     tabrowWidth += Ttk_PaddingWidth(nbstyle.tabMargins);
427
428     /* Account for exterior and interior padding:
429      */
430     padding = nbstyle.padding;
431     if (clientNode) {
432         Ttk_Padding ipad =
433             Ttk_LayoutNodeInternalPadding(nb->core.layout, clientNode);
434         padding = Ttk_AddPadding(padding, ipad);
435     }
436
437     if (nbstyle.tabPosition & (TTK_PACK_TOP|TTK_PACK_BOTTOM)) {
438         *widthPtr = MAX(tabrowWidth, clientWidth) + Ttk_PaddingWidth(padding);
439         *heightPtr = tabrowHeight + clientHeight + Ttk_PaddingHeight(padding);
440     } else {
441         *widthPtr = tabrowWidth + clientWidth + Ttk_PaddingWidth(padding);
442         *heightPtr = MAX(tabrowHeight,clientHeight) + Ttk_PaddingHeight(padding);
443     }
444
445     return 1;
446 }
447
448 /*------------------------------------------------------------------------
449  * +++ Geometry management - layout.
450  */
451
452 /* SqueezeTabs --
453  *      Squeeze or stretch tabs to fit within the tab area parcel.
454  *      This happens independently of the -mintabwidth style option.
455  *
456  *      All tabs are adjusted by an equal amount.
457  *
458  * @@@ <<NOTE-TABPOSITION>> bug: only works for horizontal orientations
459  * @@@ <<NOTE-SQUEEZE-HIDDEN>> does not account for hidden tabs.
460  */
461
462 static void SqueezeTabs(
463     Notebook *nb, int needed, int available)
464 {
465     int nTabs = Ttk_NumberContent(nb->notebook.mgr);
466
467     if (nTabs > 0) {
468         int difference = available - needed;
469         double delta = (double)difference / needed;
470         double slack = 0;
471         int i;
472
473         for (i = 0; i < nTabs; ++i) {
474             Tab *tab = (Tab *)Ttk_ContentData(nb->notebook.mgr,i);
475             double ad = slack + tab->width * delta;
476             tab->width += (int)ad;
477             slack = ad - (int)ad;
478         }
479     }
480 }
481
482 /* PlaceTabs --
483  *      Compute all tab parcels.
484  */
485 static void PlaceTabs(
486     Notebook *nb, Ttk_Box tabrowBox, Ttk_PositionSpec tabPlacement)
487 {
488     Ttk_Layout tabLayout = nb->notebook.tabLayout;
489     int nTabs = Ttk_NumberContent(nb->notebook.mgr);
490     int i;
491
492     for (i = 0; i < nTabs; ++i) {
493         Tab *tab = (Tab *)Ttk_ContentData(nb->notebook.mgr, i);
494         Ttk_State tabState = TabState(nb, i);
495
496         if (tab->state != TAB_STATE_HIDDEN) {
497             Ttk_Padding expand = Ttk_UniformPadding(0);
498             Tcl_Obj *expandObj = Ttk_QueryOption(tabLayout,"-expand",tabState);
499
500             if (expandObj) {
501                 Ttk_GetBorderFromObj(NULL, expandObj, &expand);
502             }
503
504             tab->parcel =
505                 Ttk_ExpandBox(
506                     Ttk_PositionBox(&tabrowBox,
507                         tab->width, tab->height, tabPlacement),
508                     expand);
509         }
510     }
511 }
512
513 /* NotebookDoLayout --
514  *      Computes notebook layout and places tabs.
515  *
516  * Side effects:
517  *      Sets clientArea, used to place panes.
518  */
519 static void NotebookDoLayout(void *recordPtr)
520 {
521     Notebook *nb = (Notebook *)recordPtr;
522     Tk_Window nbwin = nb->core.tkwin;
523     Ttk_Box cavity = Ttk_WinBox(nbwin);
524     int tabrowWidth = 0, tabrowHeight = 0;
525     Ttk_Element clientNode = Ttk_FindElement(nb->core.layout, "client");
526     Ttk_Box tabrowBox;
527     NotebookStyle nbstyle;
528
529     NotebookStyleOptions(nb, &nbstyle);
530
531     /* Notebook internal padding:
532      */
533     cavity = Ttk_PadBox(cavity, nbstyle.padding);
534
535     /* Layout for notebook background (base layout):
536      */
537     Ttk_PlaceLayout(nb->core.layout, nb->core.state, Ttk_WinBox(nbwin));
538
539     /* Place tabs:
540      * Note: TabrowSize() takes into account -mintabwidth, but the tabs will
541      * actually have this minimum size when displayed only if there is enough
542      * space to draw the tabs with this width. Otherwise some of the tabs can
543      * be squeezed to a size smaller than -mintabwidth because we prefer
544      * displaying all tabs than than honoring -mintabwidth for all of them.
545      */
546     TabrowSize(nb, nbstyle.tabOrient, nbstyle.minTabWidth, &tabrowWidth, &tabrowHeight);
547     tabrowBox = Ttk_PadBox(
548                     Ttk_PositionBox(&cavity,
549                         tabrowWidth + Ttk_PaddingWidth(nbstyle.tabMargins),
550                         tabrowHeight + Ttk_PaddingHeight(nbstyle.tabMargins),
551                         nbstyle.tabPosition),
552                     nbstyle.tabMargins);
553
554     SqueezeTabs(nb, tabrowWidth, tabrowBox.width);
555     PlaceTabs(nb, tabrowBox, nbstyle.tabPlacement);
556
557     /* Layout for client area frame:
558      */
559     if (clientNode) {
560         Ttk_PlaceElement(nb->core.layout, clientNode, cavity);
561         cavity = Ttk_LayoutNodeInternalParcel(nb->core.layout, clientNode);
562     }
563
564     if (cavity.height <= 0) cavity.height = 1;
565     if (cavity.width <= 0) cavity.width = 1;
566
567     nb->notebook.clientArea = cavity;
568 }
569
570 /*
571  * NotebookPlaceContent --
572  *      Set the position and size of a child widget
573  *      based on the current client area and content window options:
574  */
575 static void NotebookPlaceContent(Notebook *nb, int index)
576 {
577     Tab *tab = (Tab *)Ttk_ContentData(nb->notebook.mgr, index);
578     Tk_Window window = Ttk_ContentWindow(nb->notebook.mgr, index);
579     Ttk_Box box =
580         Ttk_StickBox(Ttk_PadBox(nb->notebook.clientArea, tab->padding),
581             Tk_ReqWidth(window), Tk_ReqHeight(window),tab->sticky);
582
583     Ttk_PlaceContent(nb->notebook.mgr, index,
584         box.x, box.y, box.width, box.height);
585 }
586
587 /* NotebookPlaceContents --
588  *      Geometry manager hook.
589  */
590 static void NotebookPlaceContents(void *recordPtr)
591 {
592     Notebook *nb = (Notebook *)recordPtr;
593     int currentIndex = nb->notebook.currentIndex;
594     if (currentIndex >= 0) {
595         NotebookDoLayout(nb);
596         NotebookPlaceContent(nb, currentIndex);
597     }
598 }
599
600 /*
601  * SelectTab(nb, index) --
602  *      Change the currently-selected tab.
603  */
604 static void SelectTab(Notebook *nb, int index)
605 {
606     Tab *tab = (Tab *)Ttk_ContentData(nb->notebook.mgr, index);
607     int currentIndex = nb->notebook.currentIndex;
608
609     if (index == currentIndex) {
610         return;
611     }
612
613     if (TabState(nb, index) & TTK_STATE_DISABLED) {
614         return;
615     }
616
617     /* Unhide the tab if it is currently hidden and being selected.
618      */
619     if (tab->state == TAB_STATE_HIDDEN) {
620         tab->state = TAB_STATE_NORMAL;
621     }
622
623     if (currentIndex >= 0) {
624         Ttk_UnmapContent(nb->notebook.mgr, currentIndex);
625     }
626
627     /* Must be set before calling NotebookPlaceContent(), otherwise it may
628      * happen that NotebookPlaceContents(), triggered by an interveaning
629      * geometry request, will swap to old index. */
630     nb->notebook.currentIndex = index;
631
632     NotebookPlaceContent(nb, index);
633     TtkRedisplayWidget(&nb->core);
634
635     TtkSendVirtualEvent(nb->core.tkwin, "NotebookTabChanged");
636 }
637
638 /* NextTab --
639  *      Returns the index of the next tab after the specified tab
640  *      in the normal state (e.g., not hidden or disabled),
641  *      or -1 if all tabs are disabled or hidden.
642  */
643 static int NextTab(Notebook *nb, int index)
644 {
645     int nTabs = Ttk_NumberContent(nb->notebook.mgr);
646     int nextIndex;
647
648     /* Scan forward for following usable tab:
649      */
650     for (nextIndex = index + 1; nextIndex < nTabs; ++nextIndex) {
651         Tab *tab = (Tab *)Ttk_ContentData(nb->notebook.mgr, nextIndex);
652         if (tab->state == TAB_STATE_NORMAL) {
653             return nextIndex;
654         }
655     }
656
657     /* Not found -- scan backwards.
658      */
659     for (nextIndex = index - 1; nextIndex >= 0; --nextIndex) {
660         Tab *tab = (Tab *)Ttk_ContentData(nb->notebook.mgr, nextIndex);
661         if (tab->state == TAB_STATE_NORMAL) {
662             return nextIndex;
663         }
664     }
665
666     /* Still nothing.  Give up.
667      */
668     return -1;
669 }
670
671 /* SelectNearestTab --
672  *      Handles the case where the current tab is forgotten, hidden,
673  *      or destroyed.
674  *
675  *      Unmap the current tab and schedule the next available one
676  *      to be mapped at the next GM update.
677  */
678 static void SelectNearestTab(Notebook *nb)
679 {
680     int currentIndex = nb->notebook.currentIndex;
681     int nextIndex = NextTab(nb, currentIndex);
682
683     if (currentIndex >= 0) {
684         Ttk_UnmapContent(nb->notebook.mgr, currentIndex);
685     }
686     if (currentIndex != nextIndex) {
687         TtkSendVirtualEvent(nb->core.tkwin, "NotebookTabChanged");
688     }
689
690     nb->notebook.currentIndex = nextIndex;
691     Ttk_ManagerLayoutChanged(nb->notebook.mgr);
692     TtkRedisplayWidget(&nb->core);
693 }
694
695 /* TabRemoved -- GM TabRemoved hook.
696  *      Select the next tab if the current one is being removed.
697  *      Adjust currentIndex to account for removed content window.
698  */
699 static void TabRemoved(void *managerData, int index)
700 {
701     Notebook *nb = (Notebook *)managerData;
702     Tab *tab = (Tab *)Ttk_ContentData(nb->notebook.mgr, index);
703
704     if (index == nb->notebook.currentIndex) {
705         SelectNearestTab(nb);
706     }
707
708     if (index < nb->notebook.currentIndex) {
709         --nb->notebook.currentIndex;
710     }
711
712     DestroyTab(nb, tab);
713
714     TtkRedisplayWidget(&nb->core);
715 }
716
717 static int TabRequest(
718     TCL_UNUSED(void *),
719     TCL_UNUSED(int),
720     TCL_UNUSED(int),
721     TCL_UNUSED(int))
722 {
723     return 1;
724 }
725
726 /* AddTab --
727  *      Add new tab at specified index.
728  */
729 static int AddTab(
730     Tcl_Interp *interp, Notebook *nb,
731     int destIndex, Tk_Window window,
732     int objc, Tcl_Obj *const objv[])
733 {
734     Tab *tab;
735     if (!Ttk_Maintainable(interp, window, nb->core.tkwin)) {
736         return TCL_ERROR;
737     }
738 #if 0 /* can't happen */
739     if (Ttk_ContentIndex(nb->notebook.mgr, window) >= 0) {
740         Tcl_SetObjResult(interp, Tcl_ObjPrintf("%s already added",
741             Tk_PathName(window)));
742         Tcl_SetErrorCode(interp, "TTK", "NOTEBOOK", "PRESENT", NULL);
743         return TCL_ERROR;
744     }
745 #endif
746
747     /* Create and insert tab.
748      */
749     tab = CreateTab(interp, nb, window);
750     if (!tab) {
751         return TCL_ERROR;
752     }
753     if (ConfigureTab(interp, nb, tab, window, objc, objv) != TCL_OK) {
754         DestroyTab(nb, tab);
755         return TCL_ERROR;
756     }
757
758     Ttk_InsertContent(nb->notebook.mgr, destIndex, window, tab);
759
760     /* Adjust indices and/or autoselect first tab:
761      */
762     if (nb->notebook.currentIndex < 0) {
763         SelectTab(nb, destIndex);
764     } else if (nb->notebook.currentIndex >= destIndex) {
765         ++nb->notebook.currentIndex;
766     }
767
768     return TCL_OK;
769 }
770
771 static Ttk_ManagerSpec NotebookManagerSpec = {
772     { "notebook", Ttk_GeometryRequestProc, Ttk_LostContentProc },
773     NotebookSize,
774     NotebookPlaceContents,
775     TabRequest,
776     TabRemoved
777 };
778
779 /*------------------------------------------------------------------------
780  * +++ Event handlers.
781  */
782
783 /* NotebookEventHandler --
784  *      Tracks the active tab.
785  */
786 static const int NotebookEventMask
787     = StructureNotifyMask
788     | PointerMotionMask
789     | LeaveWindowMask
790     ;
791 static void NotebookEventHandler(ClientData clientData, XEvent *eventPtr)
792 {
793     Notebook *nb = (Notebook *)clientData;
794
795     if (eventPtr->type == DestroyNotify) { /* Remove self */
796         Tk_DeleteEventHandler(nb->core.tkwin,
797             NotebookEventMask, NotebookEventHandler, clientData);
798     } else if (eventPtr->type == MotionNotify) {
799         int index = IdentifyTab(nb, eventPtr->xmotion.x, eventPtr->xmotion.y);
800         ActivateTab(nb, index);
801     } else if (eventPtr->type == LeaveNotify) {
802         ActivateTab(nb, -1);
803     }
804 }
805
806 /*------------------------------------------------------------------------
807  * +++ Utilities.
808  */
809
810 /* FindTabIndex --
811  *      Find the index of the specified tab.
812  *      Tab identifiers are one of:
813  *
814  *      + positional specifications @x,y,
815  *      + "current",
816  *      + numeric indices [0..nTabs],
817  *      + content window names
818  *
819  *      Stores index of specified tab in *index_rtn, -1 if not found.
820  *
821  *      Returns TCL_ERROR and leaves an error message in interp->result
822  *      if the tab identifier was incorrect.
823  *
824  *      See also: GetTabIndex.
825  */
826 static int FindTabIndex(
827     Tcl_Interp *interp, Notebook *nb, Tcl_Obj *objPtr, int *index_rtn)
828 {
829     const char *string = Tcl_GetString(objPtr);
830     int x, y;
831
832     *index_rtn = -1;
833
834     /* Check for @x,y ...
835      */
836     if (string[0] == '@' && sscanf(string, "@%d,%d",&x,&y) == 2) {
837         *index_rtn = IdentifyTab(nb, x, y);
838         return TCL_OK;
839     }
840
841     /* ... or "current" ...
842      */
843     if (!strcmp(string, "current")) {
844         *index_rtn = nb->notebook.currentIndex;
845         return TCL_OK;
846     }
847
848     /* ... or integer index or content window name:
849      */
850     if (Ttk_GetContentIndexFromObj(
851             interp, nb->notebook.mgr, objPtr, index_rtn) == TCL_OK)
852     {
853         return TCL_OK;
854     }
855
856     /* Nothing matched; Ttk_GetContentIndexFromObj will have left error message.
857      */
858     return TCL_ERROR;
859 }
860
861 /* GetTabIndex --
862  *      Get the index of an existing tab.
863  *      Tab identifiers are as per FindTabIndex.
864  *      Returns TCL_ERROR if the tab does not exist.
865  */
866 static int GetTabIndex(
867     Tcl_Interp *interp, Notebook *nb, Tcl_Obj *objPtr, int *index_rtn)
868 {
869     int status = FindTabIndex(interp, nb, objPtr, index_rtn);
870
871     if (status == TCL_OK && *index_rtn < 0) {
872         Tcl_SetObjResult(interp, Tcl_ObjPrintf(
873             "tab '%s' not found", Tcl_GetString(objPtr)));
874         Tcl_SetErrorCode(interp, "TTK", "NOTEBOOK", "TAB", NULL);
875         status = TCL_ERROR;
876     }
877     return status;
878 }
879
880 /*------------------------------------------------------------------------
881  * +++ Widget command routines.
882  */
883
884 /* $nb add window ?options ... ?
885  */
886 static int NotebookAddCommand(
887     void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
888 {
889     Notebook *nb = (Notebook *)recordPtr;
890     Tk_Window window;
891     int index;
892     Tab *tab;
893
894     if (objc <= 2 || objc % 2 != 1) {
895         Tcl_WrongNumArgs(interp, 2, objv, "window ?-option value ...?");
896         return TCL_ERROR;
897     }
898
899     window = Tk_NameToWindow(interp,Tcl_GetString(objv[2]),nb->core.tkwin);
900     if (!window) {
901         return TCL_ERROR;
902     }
903     index = Ttk_ContentIndex(nb->notebook.mgr, window);
904
905     if (index < 0) { /* New tab */
906         return AddTab(interp, nb, Ttk_NumberContent(nb->notebook.mgr), window, objc-3,objv+3);
907     }
908
909     tab = (Tab *)Ttk_ContentData(nb->notebook.mgr, index);
910     if (tab->state == TAB_STATE_HIDDEN) {
911         tab->state = TAB_STATE_NORMAL;
912     }
913     if (ConfigureTab(interp, nb, tab, window, objc-3,objv+3) != TCL_OK) {
914         return TCL_ERROR;
915     }
916
917     TtkRedisplayWidget(&nb->core);
918
919     return TCL_OK;
920 }
921
922 /* $nb insert $index $tab ?-option value ...?
923  *      Insert new tab, or move existing one.
924  */
925 static int NotebookInsertCommand(
926     void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
927 {
928     Notebook *nb = (Notebook *)recordPtr;
929     int current = nb->notebook.currentIndex;
930     int nContent = Ttk_NumberContent(nb->notebook.mgr);
931     int srcIndex, destIndex;
932
933     if (objc < 4) {
934         Tcl_WrongNumArgs(interp, 2,objv, "index slave ?-option value ...?");
935         return TCL_ERROR;
936     }
937
938     if (!strcmp(Tcl_GetString(objv[2]), "end")) {
939         destIndex = Ttk_NumberContent(nb->notebook.mgr);
940     } else if (TCL_OK != Ttk_GetContentIndexFromObj(
941                 interp, nb->notebook.mgr, objv[2], &destIndex)) {
942         return TCL_ERROR;
943     }
944
945     if (Tcl_GetString(objv[3])[0] == '.') {
946         /* Window name -- could be new or existing content window.
947          */
948         Tk_Window window =
949             Tk_NameToWindow(interp,Tcl_GetString(objv[3]),nb->core.tkwin);
950
951         if (!window) {
952             return TCL_ERROR;
953         }
954
955         srcIndex = Ttk_ContentIndex(nb->notebook.mgr, window);
956         if (srcIndex < 0) {     /* New content window */
957             return AddTab(interp, nb, destIndex, window, objc-4,objv+4);
958         }
959     } else if (Ttk_GetContentIndexFromObj(
960                 interp, nb->notebook.mgr, objv[3], &srcIndex) != TCL_OK)
961     {
962         return TCL_ERROR;
963     }
964
965     /* Move existing content window:
966      */
967     if (ConfigureTab(interp, nb,
968              (Tab *)Ttk_ContentData(nb->notebook.mgr, srcIndex),
969                  Ttk_ContentWindow(nb->notebook.mgr, srcIndex),
970              objc-4,objv+4) != TCL_OK)
971     {
972         return TCL_ERROR;
973     }
974
975     if (destIndex >= nContent) {
976         destIndex  = nContent - 1;
977     }
978     Ttk_ReorderContent(nb->notebook.mgr, srcIndex, destIndex);
979
980     /* Adjust internal indexes:
981      */
982     nb->notebook.activeIndex = -1;
983     if (current == srcIndex) {
984         nb->notebook.currentIndex = destIndex;
985     } else if (destIndex <= current && current < srcIndex) {
986         ++nb->notebook.currentIndex;
987     } else if (srcIndex < current && current <= destIndex) {
988         --nb->notebook.currentIndex;
989     }
990
991     TtkRedisplayWidget(&nb->core);
992
993     return TCL_OK;
994 }
995
996 /* $nb forget $tab --
997  *      Removes the specified tab.
998  */
999 static int NotebookForgetCommand(
1000     void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
1001 {
1002     Notebook *nb = (Notebook *)recordPtr;
1003     int index;
1004
1005     if (objc != 3) {
1006         Tcl_WrongNumArgs(interp, 2, objv, "tab");
1007         return TCL_ERROR;
1008     }
1009
1010     if (GetTabIndex(interp, nb, objv[2], &index) != TCL_OK) {
1011         return TCL_ERROR;
1012     }
1013
1014     Ttk_ForgetContent(nb->notebook.mgr, index);
1015     TtkRedisplayWidget(&nb->core);
1016
1017     return TCL_OK;
1018 }
1019
1020 /* $nb hide $tab --
1021  *      Hides the specified tab.
1022  */
1023 static int NotebookHideCommand(
1024     void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
1025 {
1026     Notebook *nb = (Notebook *)recordPtr;
1027     int index;
1028     Tab *tab;
1029
1030     if (objc != 3) {
1031         Tcl_WrongNumArgs(interp, 2, objv, "tab");
1032         return TCL_ERROR;
1033     }
1034
1035     if (GetTabIndex(interp, nb, objv[2], &index) != TCL_OK) {
1036         return TCL_ERROR;
1037     }
1038
1039     tab = (Tab *)Ttk_ContentData(nb->notebook.mgr, index);
1040     tab->state = TAB_STATE_HIDDEN;
1041     if (index == nb->notebook.currentIndex) {
1042         SelectNearestTab(nb);
1043     }
1044
1045     TtkRedisplayWidget(&nb->core);
1046
1047     return TCL_OK;
1048 }
1049
1050 /* $nb identify $x $y --
1051  *      Returns name of tab element at $x,$y; empty string if none.
1052  */
1053 static int NotebookIdentifyCommand(
1054     void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
1055 {
1056     static const char *whatTable[] = { "element", "tab", NULL };
1057     enum { IDENTIFY_ELEMENT, IDENTIFY_TAB };
1058     int what = IDENTIFY_ELEMENT;
1059     Notebook *nb = (Notebook *)recordPtr;
1060     Ttk_Element element = NULL;
1061     int x, y;
1062     int tabIndex;
1063
1064     if (objc < 4 || objc > 5) {
1065         Tcl_WrongNumArgs(interp, 2,objv, "?what? x y");
1066         return TCL_ERROR;
1067     }
1068
1069     if (Tcl_GetIntFromObj(interp, objv[objc-2], &x) != TCL_OK
1070         || Tcl_GetIntFromObj(interp, objv[objc-1], &y) != TCL_OK
1071         || (objc == 5 && Tcl_GetIndexFromObjStruct(interp, objv[2], whatTable,
1072                 sizeof(char *), "option", 0, &what) != TCL_OK)
1073     ) {
1074         return TCL_ERROR;
1075     }
1076
1077     tabIndex = IdentifyTab(nb, x, y);
1078     if (tabIndex >= 0) {
1079         Tab *tab = (Tab *)Ttk_ContentData(nb->notebook.mgr, tabIndex);
1080         Ttk_State state = TabState(nb, tabIndex);
1081         Ttk_Layout tabLayout = nb->notebook.tabLayout;
1082
1083         Ttk_RebindSublayout(tabLayout, tab);
1084         Ttk_PlaceLayout(tabLayout, state, tab->parcel);
1085
1086         element = Ttk_IdentifyElement(tabLayout, x, y);
1087     }
1088
1089     switch (what) {
1090         case IDENTIFY_ELEMENT:
1091             if (element) {
1092                 const char *elementName = Ttk_ElementName(element);
1093
1094                 Tcl_SetObjResult(interp, Tcl_NewStringObj(elementName, -1));
1095             }
1096             break;
1097         case IDENTIFY_TAB:
1098             if (tabIndex >= 0) {
1099                 Tcl_SetObjResult(interp, Tcl_NewIntObj(tabIndex));
1100             }
1101             break;
1102     }
1103     return TCL_OK;
1104 }
1105
1106 /* $nb index $item --
1107  *      Returns the integer index of the tab specified by $item,
1108  *      the empty string if $item does not identify a tab.
1109  *      See above for valid item formats.
1110  */
1111 static int NotebookIndexCommand(
1112     void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
1113 {
1114     Notebook *nb = (Notebook *)recordPtr;
1115     int index;
1116     int status;
1117
1118     if (objc != 3) {
1119         Tcl_WrongNumArgs(interp, 2, objv, "tab");
1120         return TCL_ERROR;
1121     }
1122
1123     /*
1124      * Special-case for "end":
1125      */
1126     if (!strcmp("end", Tcl_GetString(objv[2]))) {
1127         int nContent = Ttk_NumberContent(nb->notebook.mgr);
1128         Tcl_SetObjResult(interp, Tcl_NewIntObj(nContent));
1129         return TCL_OK;
1130     }
1131
1132     status = FindTabIndex(interp, nb, objv[2], &index);
1133     if (status == TCL_OK && index >= 0) {
1134         Tcl_SetObjResult(interp, Tcl_NewIntObj(index));
1135     }
1136
1137     return status;
1138 }
1139
1140 /* $nb select ?$item? --
1141  *      Select the specified tab, or return the widget path of
1142  *      the currently-selected pane.
1143  */
1144 static int NotebookSelectCommand(
1145     void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
1146 {
1147     Notebook *nb = (Notebook *)recordPtr;
1148
1149     if (objc == 2) {
1150         if (nb->notebook.currentIndex >= 0) {
1151             Tk_Window pane = Ttk_ContentWindow(
1152                 nb->notebook.mgr, nb->notebook.currentIndex);
1153             Tcl_SetObjResult(interp, Tcl_NewStringObj(Tk_PathName(pane), -1));
1154         }
1155         return TCL_OK;
1156     } else if (objc == 3) {
1157         int index, status = GetTabIndex(interp, nb, objv[2], &index);
1158         if (status == TCL_OK) {
1159             SelectTab(nb, index);
1160         }
1161         return status;
1162     } /*else*/
1163     Tcl_WrongNumArgs(interp, 2, objv, "?tab?");
1164     return TCL_ERROR;
1165 }
1166
1167 /* $nb tabs --
1168  *      Return list of tabs.
1169  */
1170 static int NotebookTabsCommand(
1171     void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
1172 {
1173     Notebook *nb = (Notebook *)recordPtr;
1174     Ttk_Manager *mgr = nb->notebook.mgr;
1175     Tcl_Obj *result;
1176     int i;
1177
1178     if (objc != 2) {
1179         Tcl_WrongNumArgs(interp, 2, objv, "");
1180         return TCL_ERROR;
1181     }
1182
1183     result = Tcl_NewListObj(0, NULL);
1184     for (i = 0; i < Ttk_NumberContent(mgr); ++i) {
1185         const char *pathName = Tk_PathName(Ttk_ContentWindow(mgr,i));
1186
1187         Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj(pathName,-1));
1188     }
1189     Tcl_SetObjResult(interp, result);
1190     return TCL_OK;
1191 }
1192
1193 /* $nb tab $tab ?-option ?value -option value...??
1194  */
1195 static int NotebookTabCommand(
1196     void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
1197 {
1198     Notebook *nb = (Notebook *)recordPtr;
1199     Ttk_Manager *mgr = nb->notebook.mgr;
1200     int index;
1201     Tk_Window window;
1202     Tab *tab;
1203
1204     if (objc < 3) {
1205         Tcl_WrongNumArgs(interp, 2, objv, "tab ?-option ?value??...");
1206         return TCL_ERROR;
1207     }
1208
1209     if (GetTabIndex(interp, nb, objv[2], &index) != TCL_OK) {
1210         return TCL_ERROR;
1211     }
1212
1213     tab = (Tab *)Ttk_ContentData(mgr, index);
1214     window = Ttk_ContentWindow(mgr, index);
1215
1216     if (objc == 3) {
1217         return TtkEnumerateOptions(interp, tab,
1218             PaneOptionSpecs, nb->notebook.paneOptionTable, window);
1219     } else if (objc == 4) {
1220         return TtkGetOptionValue(interp, tab, objv[3],
1221             nb->notebook.paneOptionTable, window);
1222     } /* else */
1223
1224     if (ConfigureTab(interp, nb, tab, window, objc-3,objv+3) != TCL_OK) {
1225         return TCL_ERROR;
1226     }
1227
1228     /* If the current tab has become disabled or hidden,
1229      * select the next nondisabled, unhidden one:
1230      */
1231     if (index == nb->notebook.currentIndex && tab->state != TAB_STATE_NORMAL) {
1232         SelectNearestTab(nb);
1233     }
1234
1235     return TCL_OK;
1236 }
1237
1238 /* Subcommand table:
1239  */
1240 static const Ttk_Ensemble NotebookCommands[] = {
1241     { "add",            NotebookAddCommand,0 },
1242     { "configure",      TtkWidgetConfigureCommand,0 },
1243     { "cget",           TtkWidgetCgetCommand,0 },
1244     { "forget",         NotebookForgetCommand,0 },
1245     { "hide",           NotebookHideCommand,0 },
1246     { "identify",       NotebookIdentifyCommand,0 },
1247     { "index",          NotebookIndexCommand,0 },
1248     { "insert",         NotebookInsertCommand,0 },
1249     { "instate",        TtkWidgetInstateCommand,0 },
1250     { "select",         NotebookSelectCommand,0 },
1251     { "state",          TtkWidgetStateCommand,0 },
1252     { "tab",            NotebookTabCommand,0 },
1253     { "tabs",           NotebookTabsCommand,0 },
1254     { 0,0,0 }
1255 };
1256
1257 /*------------------------------------------------------------------------
1258  * +++ Widget class hooks.
1259  */
1260
1261 static void NotebookInitialize(Tcl_Interp *interp, void *recordPtr)
1262 {
1263     Notebook *nb = (Notebook *)recordPtr;
1264
1265     nb->notebook.mgr = Ttk_CreateManager(
1266             &NotebookManagerSpec, recordPtr, nb->core.tkwin);
1267
1268     nb->notebook.tabOptionTable = Tk_CreateOptionTable(interp,TabOptionSpecs);
1269     nb->notebook.paneOptionTable = Tk_CreateOptionTable(interp,PaneOptionSpecs);
1270
1271     nb->notebook.currentIndex = -1;
1272     nb->notebook.activeIndex = -1;
1273     nb->notebook.tabLayout = 0;
1274
1275     nb->notebook.clientArea = Ttk_MakeBox(0,0,1,1);
1276
1277     Tk_CreateEventHandler(
1278         nb->core.tkwin, NotebookEventMask, NotebookEventHandler, recordPtr);
1279 }
1280
1281 static void NotebookCleanup(void *recordPtr)
1282 {
1283     Notebook *nb = (Notebook *)recordPtr;
1284
1285     Ttk_DeleteManager(nb->notebook.mgr);
1286     if (nb->notebook.tabLayout)
1287         Ttk_FreeLayout(nb->notebook.tabLayout);
1288 }
1289
1290 static int NotebookConfigure(Tcl_Interp *interp, void *clientData, int mask)
1291 {
1292     Notebook *nb = (Notebook *)clientData;
1293
1294     /*
1295      * Error-checks:
1296      */
1297     if (nb->notebook.paddingObj) {
1298         /* Check for valid -padding: */
1299         Ttk_Padding unused;
1300         if (Ttk_GetPaddingFromObj(
1301                     interp, nb->core.tkwin, nb->notebook.paddingObj, &unused)
1302                 != TCL_OK) {
1303             return TCL_ERROR;
1304         }
1305     }
1306
1307     return TtkCoreConfigure(interp, clientData, mask);
1308 }
1309
1310 /* NotebookGetLayout  --
1311  *      GetLayout widget hook.
1312  */
1313 static Ttk_Layout NotebookGetLayout(
1314     Tcl_Interp *interp, Ttk_Theme theme, void *recordPtr)
1315 {
1316     Notebook *nb = (Notebook *)recordPtr;
1317     Ttk_Layout notebookLayout = TtkWidgetGetLayout(interp, theme, recordPtr);
1318     Ttk_Layout tabLayout;
1319
1320     if (!notebookLayout) {
1321         return NULL;
1322     }
1323
1324     tabLayout = Ttk_CreateSublayout(
1325         interp, theme, notebookLayout, ".Tab",  nb->notebook.tabOptionTable);
1326
1327     if (tabLayout) {
1328         if (nb->notebook.tabLayout) {
1329             Ttk_FreeLayout(nb->notebook.tabLayout);
1330         }
1331         nb->notebook.tabLayout = tabLayout;
1332     }
1333
1334     return notebookLayout;
1335 }
1336
1337 /*------------------------------------------------------------------------
1338  * +++ Display routines.
1339  */
1340
1341 static void DisplayTab(Notebook *nb, int index, Drawable d)
1342 {
1343     Ttk_Layout tabLayout = nb->notebook.tabLayout;
1344     Tab *tab = (Tab *)Ttk_ContentData(nb->notebook.mgr, index);
1345     Ttk_State state = TabState(nb, index);
1346
1347     if (tab->state != TAB_STATE_HIDDEN) {
1348         Ttk_RebindSublayout(tabLayout, tab);
1349         Ttk_PlaceLayout(tabLayout, state, tab->parcel);
1350         Ttk_DrawLayout(tabLayout, state, d);
1351     }
1352 }
1353
1354 static void NotebookDisplay(void *clientData, Drawable d)
1355 {
1356     Notebook *nb = (Notebook *)clientData;
1357     int nContent = Ttk_NumberContent(nb->notebook.mgr);
1358     int index;
1359
1360     /* Draw notebook background (base layout):
1361      */
1362     Ttk_DrawLayout(nb->core.layout, nb->core.state, d);
1363
1364     /* Draw tabs from left to right, but draw the current tab last
1365      * so it will overwrite its neighbors.
1366      */
1367     for (index = 0; index < nContent; ++index) {
1368         if (index != nb->notebook.currentIndex) {
1369             DisplayTab(nb, index, d);
1370         }
1371     }
1372     if (nb->notebook.currentIndex >= 0) {
1373         DisplayTab(nb, nb->notebook.currentIndex, d);
1374     }
1375 }
1376
1377 /*------------------------------------------------------------------------
1378  * +++ Widget specification and layout definitions.
1379  */
1380
1381 static WidgetSpec NotebookWidgetSpec =
1382 {
1383     "TNotebook",                /* className */
1384     sizeof(Notebook),           /* recordSize */
1385     NotebookOptionSpecs,        /* optionSpecs */
1386     NotebookCommands,           /* subcommands */
1387     NotebookInitialize,         /* initializeProc */
1388     NotebookCleanup,            /* cleanupProc */
1389     NotebookConfigure,          /* configureProc */
1390     TtkNullPostConfigure,       /* postConfigureProc */
1391     NotebookGetLayout,          /* getLayoutProc */
1392     NotebookSize,               /* geometryProc */
1393     NotebookDoLayout,           /* layoutProc */
1394     NotebookDisplay             /* displayProc */
1395 };
1396
1397 TTK_BEGIN_LAYOUT(NotebookLayout)
1398     TTK_NODE("Notebook.client", TTK_FILL_BOTH)
1399 TTK_END_LAYOUT
1400
1401 TTK_BEGIN_LAYOUT(TabLayout)
1402     TTK_GROUP("Notebook.tab", TTK_FILL_BOTH,
1403         TTK_GROUP("Notebook.padding", TTK_PACK_TOP|TTK_FILL_BOTH,
1404             TTK_GROUP("Notebook.focus", TTK_PACK_TOP|TTK_FILL_BOTH,
1405                 TTK_NODE("Notebook.label", TTK_PACK_TOP))))
1406 TTK_END_LAYOUT
1407
1408 /*------------------------------------------------------------------------
1409  * +++ Initialization.
1410  */
1411
1412 MODULE_SCOPE
1413 void TtkNotebook_Init(Tcl_Interp *interp)
1414 {
1415     Ttk_Theme themePtr = Ttk_GetDefaultTheme(interp);
1416
1417     Ttk_RegisterLayout(themePtr, "Tab", TabLayout);
1418     Ttk_RegisterLayout(themePtr, "TNotebook", NotebookLayout);
1419
1420     RegisterWidget(interp, "ttk::notebook", &NotebookWidgetSpec);
1421 }
1422
1423 /*EOF*/