source: trunk/kitgen/8.x/blt/generic/bltTabnotebook.c@ 181

Last change on this file since 181 was 175, checked in by demin, 12 years ago

initial commit

File size: 156.4 KB
Line 
1/*
2 * bltTabnotebook.c --
3 *
4 * This module implements a tab notebook widget for the BLT toolkit.
5 *
6 * Copyright 1998 Lucent Technologies, Inc.
7 *
8 * Permission to use, copy, modify, and distribute this software and
9 * its documentation for any purpose and without fee is hereby
10 * granted, provided that the above copyright notice appear in all
11 * copies and that both that the copyright notice and warranty
12 * disclaimer appear in supporting documentation, and that the names
13 * of Lucent Technologies or any of their entities not be used in
14 * advertising or publicity pertaining to distribution of the software
15 * without specific, written prior permission.
16 *
17 * Lucent Technologies disclaims all warranties with regard to this
18 * software, including all implied warranties of merchantability and
19 * fitness. In no event shall Lucent Technologies be liable for any
20 * special, indirect or consequential damages or any damages
21 * whatsoever resulting from loss of use, data or profits, whether in
22 * an action of contract, negligence or other tortuous action, arising
23 * out of or in connection with the use or performance of this
24 * software.
25 *
26 * Tabnotebook widget created by George A. Howlett (gah@bell-labs.com)
27 *
28 */
29
30#include "bltInt.h"
31
32#ifndef NO_TABNOTEBOOK
33#include "bltBind.h"
34#include "bltChain.h"
35#include "bltHash.h"
36#include "bltTile.h"
37
38#if (TK_MAJOR_VERSION == 4)
39#define TK_REPARENTED 0x2000
40#endif
41
42#define INVALID_FAIL 0
43#define INVALID_OK 1
44
45/*
46 * The macro below is used to modify a "char" value (e.g. by casting
47 * it to an unsigned character) so that it can be used safely with
48 * macros such as isspace.
49 */
50#define CLAMP(val,low,hi) \
51 (((val) < (low)) ? (low) : ((val) > (hi)) ? (hi) : (val))
52
53#define GAP 3
54#define SELECT_PADX 4
55#define SELECT_PADY 2
56#define OUTER_PAD 2
57#define LABEL_PAD 1
58#define LABEL_PADX 2
59#define LABEL_PADY 2
60#define IMAGE_PAD 1
61#define CORNER_OFFSET 3
62
63#define TAB_SCROLL_OFFSET 10
64
65#define SLANT_NONE 0
66#define SLANT_LEFT 1
67#define SLANT_RIGHT 2
68#define SLANT_BOTH (SLANT_LEFT | SLANT_RIGHT)
69
70#define END (-1)
71#define ODD(x) ((x) | 0x01)
72
73#define TABWIDTH(s, t) \
74 ((s)->side & SIDE_VERTICAL) ? (t)->height : (t)->width)
75#define TABHEIGHT(s, t) \
76 ((s)->side & SIDE_VERTICAL) ? (t)->height : (t)->width)
77
78#define VPORTWIDTH(s) \
79 (((s)->side & SIDE_HORIZONTAL) ? (Tk_Width((s)->tkwin) - 2 * (s)->inset) : \
80 (Tk_Height((s)->tkwin) - 2 * (s)->inset))
81
82#define VPORTHEIGHT(s) \
83 (((s)->side & SIDE_VERTICAL) ? (Tk_Width((s)->tkwin) - 2 * (s)->inset) : \
84 (Tk_Height((s)->tkwin) - 2 * (s)->inset))
85
86#define GETATTR(t,attr) \
87 (((t)->attr != NULL) ? (t)->attr : (t)->nbPtr->defTabStyle.attr)
88
89/*
90 * ----------------------------------------------------------------------------
91 *
92 * Internal widget flags:
93 *
94 * TNB_LAYOUT The layout of the widget needs to be
95 * recomputed.
96 *
97 * TNB_REDRAW A redraw request is pending for the widget.
98 *
99 * TNB_SCROLL A scroll request is pending.
100 *
101 * TNB_FOCUS The widget is receiving keyboard events.
102 * Draw the focus highlight border around the
103 * widget.
104 *
105 * TNB_MULTIPLE_TIER Notebook is using multiple tiers.
106 *
107 * TNB_STATIC Notebook does not scroll.
108 *
109 * ---------------------------------------------------------------------------
110 */
111#define TNB_LAYOUT (1<<0)
112#define TNB_REDRAW (1<<1)
113#define TNB_SCROLL (1<<2)
114#define TNB_FOCUS (1<<4)
115
116#define TNB_STATIC (1<<8)
117#define TNB_MULTIPLE_TIER (1<<9)
118
119#define PERFORATION_ACTIVE (1<<10)
120
121#define SIDE_TOP (1<<0)
122#define SIDE_RIGHT (1<<1)
123#define SIDE_LEFT (1<<2)
124#define SIDE_BOTTOM (1<<3)
125
126#define SIDE_VERTICAL (SIDE_LEFT | SIDE_RIGHT)
127#define SIDE_HORIZONTAL (SIDE_TOP | SIDE_BOTTOM)
128
129#define TAB_LABEL (ClientData)0
130#define TAB_PERFORATION (ClientData)1
131
132#define DEF_TNB_ACTIVE_BACKGROUND RGB_GREY90
133#define DEF_TNB_ACTIVE_BG_MONO STD_ACTIVE_BG_MONO
134#define DEF_TNB_ACTIVE_FOREGROUND STD_ACTIVE_FOREGROUND
135#define DEF_TNB_ACTIVE_FG_MONO STD_ACTIVE_FG_MONO
136#define DEF_TNB_BG_MONO STD_NORMAL_BG_MONO
137#define DEF_TNB_BACKGROUND STD_NORMAL_BACKGROUND
138#define DEF_TNB_BORDERWIDTH "1"
139#define DEF_TNB_COMMAND (char *)NULL
140#define DEF_TNB_CURSOR (char *)NULL
141#define DEF_TNB_DASHES "1"
142#define DEF_TNB_FOREGROUND STD_NORMAL_FOREGROUND
143#define DEF_TNB_FG_MONO STD_NORMAL_FG_MONO
144#define DEF_TNB_FONT STD_FONT
145#define DEF_TNB_GAP "3"
146#define DEF_TNB_HEIGHT "0"
147#define DEF_TNB_HIGHLIGHT_BACKGROUND STD_NORMAL_BACKGROUND
148#define DEF_TNB_HIGHLIGHT_BG_MONO STD_NORMAL_BG_MONO
149#define DEF_TNB_HIGHLIGHT_COLOR RGB_BLACK
150#define DEF_TNB_HIGHLIGHT_WIDTH "2"
151#define DEF_TNB_NORMAL_BACKGROUND STD_NORMAL_BACKGROUND
152#define DEF_TNB_NORMAL_FG_MONO STD_ACTIVE_FG_MONO
153#define DEF_TNB_OUTER_PAD "3"
154#define DEF_TNB_RELIEF "sunken"
155#define DEF_TNB_ROTATE "0.0"
156#define DEF_TNB_SCROLL_INCREMENT "0"
157#define DEF_TNB_SELECT_BACKGROUND STD_NORMAL_BACKGROUND
158#define DEF_TNB_SELECT_BG_MONO STD_SELECT_BG_MONO
159#define DEF_TNB_SELECT_BORDERWIDTH "1"
160#define DEF_TNB_SELECT_CMD (char *)NULL
161#define DEF_TNB_SELECT_FOREGROUND STD_SELECT_FOREGROUND
162#define DEF_TNB_SELECT_FG_MONO STD_SELECT_FG_MONO
163#define DEF_TNB_SELECT_MODE "multiple"
164#define DEF_TNB_SELECT_RELIEF "raised"
165#define DEF_TNB_SELECT_PAD "5"
166#define DEF_TNB_SHADOW_COLOR RGB_BLACK
167#define DEF_TNB_SIDE "top"
168#define DEF_TNB_SLANT "none"
169#define DEF_TNB_TAB_BACKGROUND RGB_GREY82
170#define DEF_TNB_TAB_BG_MONO STD_SELECT_BG_MONO
171#define DEF_TNB_TAB_RELIEF "raised"
172#define DEF_TNB_TAKE_FOCUS "1"
173#define DEF_TNB_TEXT_COLOR STD_NORMAL_FOREGROUND
174#define DEF_TNB_TEXT_MONO STD_NORMAL_FG_MONO
175#define DEF_TNB_TEXT_SIDE "left"
176#define DEF_TNB_TIERS "1"
177#define DEF_TNB_TILE (char *)NULL
178#define DEF_TNB_WIDTH "0"
179#define DEF_TNB_SAME_WIDTH "yes"
180#define DEF_TNB_TEAROFF "yes"
181#define DEF_TNB_PAGE_WIDTH "0"
182#define DEF_TNB_PAGE_HEIGHT "0"
183
184#define DEF_TAB_ACTIVE_BG (char *)NULL
185#define DEF_TAB_ACTIVE_FG (char *)NULL
186#define DEF_TAB_ANCHOR "center"
187#define DEF_TAB_BG (char *)NULL
188#define DEF_TAB_COMMAND (char *)NULL
189#define DEF_TAB_DATA (char *)NULL
190#define DEF_TAB_FG (char *)NULL
191#define DEF_TAB_FILL "none"
192#define DEF_TAB_FONT (char *)NULL
193#define DEF_TAB_HEIGHT "0"
194#define DEF_TAB_IMAGE (char *)NULL
195#define DEF_TAB_IPAD "0"
196#define DEF_TAB_PAD "3"
197#define DEF_TAB_PERF_COMMAND (char *)NULL
198#define DEF_TAB_SELECT_BG (char *)NULL
199#define DEF_TAB_SELECT_BORDERWIDTH "1"
200#define DEF_TAB_SELECT_CMD (char *)NULL
201#define DEF_TAB_SELECT_FG (char *)NULL
202#define DEF_TAB_SHADOW (char *)NULL
203#define DEF_TAB_STATE "normal"
204#define DEF_TAB_STIPPLE "BLT"
205#define DEF_TAB_BIND_TAGS "all"
206#define DEF_TAB_TEXT (char *)NULL
207#define DEF_TAB_VISUAL (char *)NULL
208#define DEF_TAB_WIDTH "0"
209#define DEF_TAB_WINDOW (char *)NULL
210
211typedef struct NotebookStruct Notebook;
212
213static void EmbeddedWidgetGeometryProc _ANSI_ARGS_((ClientData, Tk_Window));
214static void EmbeddedWidgetCustodyProc _ANSI_ARGS_((ClientData, Tk_Window));
215
216static Tk_GeomMgr tabMgrInfo =
217{
218 "notebook", /* Name of geometry manager used by winfo */
219 EmbeddedWidgetGeometryProc, /* Procedure to for new geometry requests */
220 EmbeddedWidgetCustodyProc, /* Procedure when window is taken away */
221};
222
223extern Tk_CustomOption bltDashesOption;
224extern Tk_CustomOption bltFillOption;
225extern Tk_CustomOption bltDistanceOption;
226extern Tk_CustomOption bltPositiveDistanceOption;
227extern Tk_CustomOption bltPositiveCountOption;
228extern Tk_CustomOption bltListOption;
229extern Tk_CustomOption bltPadOption;
230extern Tk_CustomOption bltShadowOption;
231extern Tk_CustomOption bltStateOption;
232extern Tk_CustomOption bltTileOption;
233extern Tk_CustomOption bltUidOption;
234
235static int StringToImage _ANSI_ARGS_((ClientData clientData,
236 Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
237 int offset));
238static char *ImageToString _ANSI_ARGS_((ClientData clientData,
239 Tk_Window tkwin, char *widgRec, int offset,
240 Tcl_FreeProc **freeProcPtrPtr));
241
242static int StringToWindow _ANSI_ARGS_((ClientData clientData,
243 Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
244 int offset));
245static char *WindowToString _ANSI_ARGS_((ClientData clientData,
246 Tk_Window tkwin, char *widgRec, int offset,
247 Tcl_FreeProc **freeProcPtrPtr));
248
249static int StringToSide _ANSI_ARGS_((ClientData clientData,
250 Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
251 int offset));
252static char *SideToString _ANSI_ARGS_((ClientData clientData,
253 Tk_Window tkwin, char *widgRec, int offset,
254 Tcl_FreeProc **freeProcPtrPtr));
255
256static int StringToSlant _ANSI_ARGS_((ClientData clientData,
257 Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
258 int offset));
259static char *SlantToString _ANSI_ARGS_((ClientData clientData,
260 Tk_Window tkwin, char *widgRec, int offset,
261 Tcl_FreeProc **freeProcPtrPtr));
262
263/*
264 * Contains a pointer to the widget that's currently being configured.
265 * This is used in the custom configuration parse routine for images.
266 */
267static Notebook *lastNotebookInstance;
268
269static Tk_CustomOption imageOption =
270{
271 StringToImage, ImageToString, (ClientData)&lastNotebookInstance,
272};
273
274static Tk_CustomOption sideOption =
275{
276 StringToSide, SideToString, (ClientData)0,
277};
278
279static Tk_CustomOption windowOption =
280{
281 StringToWindow, WindowToString, (ClientData)0,
282};
283
284static Tk_CustomOption slantOption =
285{
286 StringToSlant, SlantToString, (ClientData)0,
287};
288
289/*
290 * TabImage --
291 *
292 * When multiple instances of an image are displayed in the
293 * same widget, this can be inefficient in terms of both memory
294 * and time. We only need one instance of each image, regardless
295 * of number of times we use it. And searching/deleting instances
296 * can be very slow as the list gets large.
297 *
298 * The workaround, employed below, is to maintain a hash table of
299 * images that maintains a reference count for each image.
300 */
301
302typedef struct TabImageStruct {
303 int refCount; /* Reference counter for this image. */
304 Tk_Image tkImage; /* The Tk image being cached. */
305 int width, height; /* Dimensions of the cached image. */
306 Blt_HashEntry *hashPtr; /* Hash table pointer to the image. */
307
308} *TabImage;
309
310#define ImageHeight(image) ((image)->height)
311#define ImageWidth(image) ((image)->width)
312#define ImageBits(image) ((image)->tkImage)
313
314#define TAB_VISIBLE (1<<0)
315#define TAB_REDRAW (1<<2)
316
317typedef struct {
318 char *name; /* Identifier for tab entry */
319 int state; /* State of the tab: Disabled, active, or
320 * normal. */
321 unsigned int flags;
322
323 int tier; /* Index of tier [1..numTiers] containing
324 * this tab. */
325
326 int worldX, worldY; /* Position of the tab in world coordinates. */
327 int worldWidth, worldHeight;/* Dimensions of the tab, corrected for
328 * orientation (-side). It includes the
329 * border, padding, label, etc. */
330 int screenX, screenY;
331 short int screenWidth, screenHeight; /* */
332
333 Notebook *nbPtr; /* Notebook that includes this
334 * tab. Needed for callbacks can pass
335 * only a tab pointer. */
336 Blt_Uid tags;
337
338 /*
339 * Tab label:
340 */
341 Blt_Uid text; /* String displayed as the tab's label. */
342 TabImage image; /* Image displayed as the label. */
343
344 short int textWidth, textHeight;
345 short int labelWidth, labelHeight;
346 Blt_Pad iPadX, iPadY; /* Internal padding around the text */
347
348 Tk_Font font;
349
350 /*
351 * Normal:
352 */
353 XColor *textColor; /* Text color */
354 Tk_3DBorder border; /* Background color and border for tab.*/
355
356 /*
357 * Selected: Tab is currently selected.
358 */
359 XColor *selColor; /* Selected text color */
360 Tk_3DBorder selBorder; /* 3D border of selected folder. */
361
362 /*
363 * Active: Mouse passes over the tab.
364 */
365 Tk_3DBorder activeBorder; /* Active background color. */
366 XColor *activeFgColor; /* Active text color */
367
368 Shadow shadow;
369 Pixmap stipple; /* Stipple for outline of embedded window
370 * when torn off. */
371 /*
372 * Embedded widget information:
373 */
374 Tk_Window tkwin; /* Widget to be mapped when the tab is
375 * selected. If NULL, don't make
376 * space for the page. */
377
378 int reqWidth, reqHeight; /* If non-zero, overrides the
379 * requested dimensions of the
380 * embedded widget. */
381
382 Tk_Window container; /* The window containing the embedded
383 * widget. Does not necessarily have
384 * to be the parent. */
385
386 Tk_Anchor anchor; /* Anchor: indicates how the embedded
387 * widget is positioned within the
388 * extra space on the page. */
389
390 Blt_Pad padX, padY; /* Padding around embedded widget */
391
392 int fill; /* Indicates how the window should
393 * fill the page. */
394
395 /*
396 * Auxillary information:
397 */
398 Blt_Uid command; /* Command (malloc-ed) invoked when the tab
399 * is selected */
400 Blt_Uid data; /* This value isn't used in C code.
401 * It may be used by clients in Tcl bindings
402 * to associate extra data (other than the
403 * label or name) with the tab. */
404
405 Blt_ChainLink *linkPtr; /* Pointer to where the tab resides in the
406 * list of tabs. */
407 Blt_Uid perfCommand; /* Command (malloc-ed) invoked when the tab
408 * is selected */
409 GC textGC;
410 GC backGC;
411
412 Blt_Tile tile;
413
414} Tab;
415
416static Tk_ConfigSpec tabConfigSpecs[] =
417{
418 {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
419 "ActiveBackground", DEF_TAB_ACTIVE_BG,
420 Tk_Offset(Tab, activeBorder), TK_CONFIG_NULL_OK},
421 {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
422 "ActiveForeground", DEF_TAB_ACTIVE_FG,
423 Tk_Offset(Tab, activeFgColor), TK_CONFIG_NULL_OK},
424 {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
425 DEF_TAB_ANCHOR, Tk_Offset(Tab, anchor), TK_CONFIG_DONT_SET_DEFAULT},
426 {TK_CONFIG_BORDER, "-background", "background", "Background",
427 DEF_TAB_BG, Tk_Offset(Tab, border), TK_CONFIG_NULL_OK},
428 {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
429 {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
430 DEF_TAB_BIND_TAGS, Tk_Offset(Tab, tags),
431 TK_CONFIG_NULL_OK, &bltUidOption},
432 {TK_CONFIG_CUSTOM, "-command", "command", "Command",
433 DEF_TAB_COMMAND, Tk_Offset(Tab, command),
434 TK_CONFIG_NULL_OK, &bltUidOption},
435 {TK_CONFIG_CUSTOM, "-data", "data", "data",
436 DEF_TAB_DATA, Tk_Offset(Tab, data),
437 TK_CONFIG_NULL_OK, &bltUidOption},
438 {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
439 {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
440 DEF_TAB_FILL, Tk_Offset(Tab, fill),
441 TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
442 {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
443 DEF_TAB_FG, Tk_Offset(Tab, textColor), TK_CONFIG_NULL_OK},
444 {TK_CONFIG_FONT, "-font", "font", "Font",
445 DEF_TAB_FONT, Tk_Offset(Tab, font), 0},
446 {TK_CONFIG_CUSTOM, "-image", "image", "image",
447 DEF_TAB_IMAGE, Tk_Offset(Tab, image),
448 TK_CONFIG_NULL_OK, &imageOption},
449 {TK_CONFIG_CUSTOM, "-ipadx", "iPadX", "PadX",
450 DEF_TAB_IPAD, Tk_Offset(Tab, iPadX),
451 TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
452 {TK_CONFIG_CUSTOM, "-ipady", "iPadY", "PadY",
453 DEF_TAB_IPAD, Tk_Offset(Tab, iPadY),
454 TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
455 {TK_CONFIG_CUSTOM, "-padx", "padX", "PadX",
456 DEF_TAB_PAD, Tk_Offset(Tab, padX), 0, &bltPadOption},
457 {TK_CONFIG_CUSTOM, "-pady", "padY", "PadY",
458 DEF_TAB_PAD, Tk_Offset(Tab, padY), 0, &bltPadOption},
459 {TK_CONFIG_CUSTOM, "-perforationcommand", "perforationcommand",
460 "PerforationCommand",
461 DEF_TAB_PERF_COMMAND, Tk_Offset(Tab, perfCommand),
462 TK_CONFIG_NULL_OK, &bltUidOption},
463 {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Background",
464 DEF_TAB_SELECT_BG, Tk_Offset(Tab, selBorder), TK_CONFIG_NULL_OK},
465 {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground",
466 DEF_TAB_SELECT_FG, Tk_Offset(Tab, selColor), TK_CONFIG_NULL_OK},
467 {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
468 DEF_TAB_SHADOW, Tk_Offset(Tab, shadow),
469 TK_CONFIG_NULL_OK, &bltShadowOption},
470 {TK_CONFIG_CUSTOM, "-state", "state", "State",
471 DEF_TAB_STATE, Tk_Offset(Tab, state),
472 TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
473 {TK_CONFIG_BITMAP, "-stipple", "stipple", "Stipple",
474 DEF_TAB_STIPPLE, Tk_Offset(Tab, stipple), 0},
475 {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile",
476 (char *)NULL, Tk_Offset(Tab, tile), TK_CONFIG_NULL_OK,
477 &bltTileOption},
478 {TK_CONFIG_CUSTOM, "-text", "Text", "Text",
479 DEF_TAB_TEXT, Tk_Offset(Tab, text),
480 TK_CONFIG_NULL_OK, &bltUidOption},
481 {TK_CONFIG_CUSTOM, "-window", "window", "Window",
482 DEF_TAB_WINDOW, Tk_Offset(Tab, tkwin),
483 TK_CONFIG_NULL_OK, &windowOption},
484 {TK_CONFIG_CUSTOM, "-windowheight", "windowHeight", "WindowHeight",
485 DEF_TAB_HEIGHT, Tk_Offset(Tab, reqHeight),
486 TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
487 {TK_CONFIG_CUSTOM, "-windowwidth", "windowWidth", "WindowWidth",
488 DEF_TAB_WIDTH, Tk_Offset(Tab, reqWidth),
489 TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
490 {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
491 (char *)NULL, 0, 0}
492};
493
494/*
495 * TabAttributes --
496 */
497typedef struct {
498 Tk_Window tkwin; /* Default window to map pages. */
499
500 int reqWidth, reqHeight; /* Requested tab size. */
501 int constWidth;
502 int borderWidth; /* Width of 3D border around the tab's
503 * label. */
504 int pad; /* Extra padding of a tab entry */
505
506 XColor *activeFgColor; /* Active foreground. */
507 Tk_3DBorder activeBorder; /* Active background. */
508 XColor *selColor; /* Selected foreground. */
509 Tk_Font font;
510 XColor *textColor;
511
512 Tk_3DBorder border; /* Normal background. */
513 Tk_3DBorder selBorder; /* Selected background. */
514
515 Blt_Dashes dashes;
516 GC normalGC, activeGC;
517 int relief;
518 char *command;
519 char *perfCommand; /* Command (malloc-ed) invoked when the tab
520 * is selected */
521 double rotate;
522 int textSide;
523
524} TabAttributes;
525
526struct NotebookStruct {
527 Tk_Window tkwin; /* Window that embodies the widget.
528 * NULL means that the window has been
529 * destroyed but the data structures
530 * haven't yet been cleaned up.*/
531
532 Display *display; /* Display containing widget; needed,
533 * among other things, to release
534 * resources after tkwin has already
535 * gone away. */
536
537 Tcl_Interp *interp; /* Interpreter associated with widget. */
538
539 Tcl_Command cmdToken; /* Token for widget's command. */
540
541 unsigned int flags; /* For bitfield definitions, see below */
542
543 int inset; /* Total width of all borders, including
544 * traversal highlight and 3-D border.
545 * Indicates how much interior stuff must
546 * be offset from outside edges to leave
547 * room for borders. */
548
549 int inset2; /* Total width of 3-D folder border + corner,
550 * Indicates how much interior stuff must
551 * be offset from outside edges of folder.*/
552
553 int yPad; /* Extra offset for selected tab. Only
554 * for single tiers. */
555
556 int pageTop; /* Offset from top of notebook to the
557 * start of the page. */
558
559 Tk_Cursor cursor; /* X Cursor */
560
561 Tk_3DBorder border; /* 3D border surrounding the window. */
562 int borderWidth; /* Width of 3D border. */
563 int relief; /* 3D border relief. */
564
565 XColor *shadowColor; /* Shadow color around folder. */
566 /*
567 * Focus highlight ring
568 */
569 int highlightWidth; /* Width in pixels of highlight to draw
570 * around widget when it has the focus.
571 * <= 0 means don't draw a highlight. */
572 XColor *highlightBgColor; /* Color for drawing traversal highlight
573 * area when highlight is off. */
574 XColor *highlightColor; /* Color for drawing traversal highlight. */
575
576 GC highlightGC; /* GC for focus highlight. */
577
578 char *takeFocus; /* Says whether to select this widget during
579 * tab traveral operations. This value isn't
580 * used in C code, but for the widget's Tcl
581 * bindings. */
582
583
584 int side; /* Orientation of the notebook: either
585 * SIDE_LEFT, SIDE_RIGHT, SIDE_TOP, or
586 * SIDE_BOTTOM. */
587
588 int slant;
589 int overlap;
590 int gap;
591 int tabWidth, tabHeight;
592 int xSelectPad, ySelectPad; /* Padding around label of the selected tab. */
593 int outerPad; /* Padding around the exterior of the notebook
594 * and folder. */
595
596 TabAttributes defTabStyle; /* Global attribute information specific to
597 * tabs. */
598 Blt_Tile tile;
599
600 int reqWidth, reqHeight; /* Requested dimensions of the notebook
601 * window. */
602 int pageWidth, pageHeight; /* Dimensions of a page in the folder. */
603 int reqPageWidth, reqPageHeight; /* Requested dimensions of a page. */
604
605 int lastX, lastY;
606 /*
607 * Scrolling information:
608 */
609 int worldWidth;
610 int scrollOffset; /* Offset of viewport in world coordinates. */
611 char *scrollCmdPrefix; /* Command strings to control scrollbar.*/
612
613 int scrollUnits; /* Smallest unit of scrolling for tabs. */
614
615 /*
616 * Scanning information:
617 */
618 int scanAnchor; /* Scan anchor in screen coordinates. */
619 int scanOffset; /* Offset of the start of the scan in world
620 * coordinates.*/
621
622
623 int corner; /* Number of pixels to offset next point
624 * when drawing corners of the folder. */
625 int reqTiers; /* Requested number of tiers. Zero means to
626 * dynamically scroll if there are too many
627 * tabs to be display on a single tier. */
628 int nTiers; /* Actual number of tiers. */
629
630 Blt_HashTable imageTable;
631
632
633 Tab *selectPtr; /* The currently selected tab.
634 * (i.e. its page is displayed). */
635
636 Tab *activePtr; /* Tab last located under the pointer.
637 * It is displayed with its active
638 * foreground/background colors. */
639
640 Tab *focusPtr; /* Tab currently receiving focus. */
641
642 Tab *startPtr; /* The first tab on the first tier. */
643
644 Blt_Chain *chainPtr; /* List of tab entries. Used to
645 * arrange placement of tabs. */
646
647 Blt_HashTable tabTable; /* Hash table of tab entries. Used for
648 * lookups of tabs by name. */
649 int nextId;
650
651 int nVisible; /* Number of tabs that are currently visible
652 * in the view port. */
653
654 Blt_BindTable bindTable; /* Tab binding information */
655 Blt_HashTable tagTable; /* Table of bind tags. */
656
657 int tearoff;
658};
659
660static Tk_ConfigSpec configSpecs[] =
661{
662 {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
663 "activeBackground",
664 DEF_TNB_ACTIVE_BACKGROUND, Tk_Offset(Notebook, defTabStyle.activeBorder),
665 TK_CONFIG_COLOR_ONLY},
666 {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
667 "activeBackground",
668 DEF_TNB_ACTIVE_BG_MONO, Tk_Offset(Notebook, defTabStyle.activeBorder),
669 TK_CONFIG_MONO_ONLY},
670 {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
671 "activeForeground", DEF_TNB_ACTIVE_FOREGROUND,
672 Tk_Offset(Notebook, defTabStyle.activeFgColor), TK_CONFIG_COLOR_ONLY},
673 {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
674 "activeForeground", DEF_TNB_ACTIVE_FG_MONO,
675 Tk_Offset(Notebook, defTabStyle.activeFgColor), TK_CONFIG_MONO_ONLY},
676 {TK_CONFIG_BORDER, "-background", "background", "Background",
677 DEF_TNB_BG_MONO, Tk_Offset(Notebook, border), TK_CONFIG_MONO_ONLY},
678 {TK_CONFIG_BORDER, "-background", "background", "Background",
679 DEF_TNB_BACKGROUND, Tk_Offset(Notebook, border), TK_CONFIG_COLOR_ONLY},
680 {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
681 {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
682 {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
683 DEF_TNB_CURSOR, Tk_Offset(Notebook, cursor), TK_CONFIG_NULL_OK},
684 {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
685 DEF_TNB_BORDERWIDTH, Tk_Offset(Notebook, borderWidth),
686 TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
687 {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
688 DEF_TNB_DASHES, Tk_Offset(Notebook, defTabStyle.dashes),
689 TK_CONFIG_NULL_OK, &bltDashesOption},
690 {TK_CONFIG_SYNONYM, "-fg", "tabForeground", (char *)NULL,
691 (char *)NULL, 0, 0},
692 {TK_CONFIG_FONT, "-font", "font", "Font",
693 DEF_TNB_FONT, Tk_Offset(Notebook, defTabStyle.font), 0},
694 {TK_CONFIG_SYNONYM, "-foreground", "tabForeground", (char *)NULL,
695 (char *)NULL, 0, 0},
696 {TK_CONFIG_PIXELS, "-gap", "gap", "Gap",
697 DEF_TNB_GAP, Tk_Offset(Notebook, gap),
698 TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
699 {TK_CONFIG_CUSTOM, "-height", "height", "Height",
700 DEF_TNB_HEIGHT, Tk_Offset(Notebook, reqHeight),
701 TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
702 {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
703 "HighlightBackground",
704 DEF_TNB_HIGHLIGHT_BACKGROUND, Tk_Offset(Notebook, highlightBgColor),
705 TK_CONFIG_COLOR_ONLY},
706 {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
707 "HighlightBackground",
708 DEF_TNB_HIGHLIGHT_BG_MONO, Tk_Offset(Notebook, highlightBgColor),
709 TK_CONFIG_MONO_ONLY},
710 {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
711 DEF_TNB_HIGHLIGHT_COLOR, Tk_Offset(Notebook, highlightColor), 0},
712 {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
713 "HighlightThickness",
714 DEF_TNB_HIGHLIGHT_WIDTH, Tk_Offset(Notebook, highlightWidth),
715 TK_CONFIG_DONT_SET_DEFAULT},
716 {TK_CONFIG_CUSTOM, "-outerpad", "outerPad", "OuterPad",
717 DEF_TNB_OUTER_PAD, Tk_Offset(Notebook, outerPad),
718 TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
719 {TK_CONFIG_CUSTOM, "-pageheight", "pageHeight", "PageHeight",
720 DEF_TNB_PAGE_HEIGHT, Tk_Offset(Notebook, reqPageHeight),
721 TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
722 {TK_CONFIG_CUSTOM, "-pagewidth", "pageWidth", "PageWidth",
723 DEF_TNB_PAGE_WIDTH, Tk_Offset(Notebook, reqPageWidth),
724 TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
725 {TK_CONFIG_STRING, "-perforationcommand", "perforationcommand",
726 "PerforationCommand",
727 DEF_TAB_PERF_COMMAND, Tk_Offset(Notebook, defTabStyle.perfCommand),
728 TK_CONFIG_NULL_OK, &bltUidOption},
729 {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
730 DEF_TNB_RELIEF, Tk_Offset(Notebook, relief), 0},
731 {TK_CONFIG_DOUBLE, "-rotate", "rotate", "Rotate",
732 DEF_TNB_ROTATE, Tk_Offset(Notebook, defTabStyle.rotate),
733 TK_CONFIG_DONT_SET_DEFAULT},
734 {TK_CONFIG_BOOLEAN, "-samewidth", "sameWidth", "SameWidth",
735 DEF_TNB_SAME_WIDTH, Tk_Offset(Notebook, defTabStyle.constWidth),
736 TK_CONFIG_DONT_SET_DEFAULT},
737 {TK_CONFIG_STRING, "-scrollcommand", "scrollCommand", "ScrollCommand",
738 (char *)NULL, Tk_Offset(Notebook, scrollCmdPrefix), TK_CONFIG_NULL_OK},
739 {TK_CONFIG_CUSTOM, "-scrollincrement", "scrollIncrement",
740 "ScrollIncrement",
741 DEF_TNB_SCROLL_INCREMENT, Tk_Offset(Notebook, scrollUnits),
742 TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveDistanceOption},
743 {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
744 DEF_TNB_SELECT_BG_MONO, Tk_Offset(Notebook, defTabStyle.selBorder),
745 TK_CONFIG_MONO_ONLY},
746 {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
747 DEF_TNB_SELECT_BACKGROUND, Tk_Offset(Notebook, defTabStyle.selBorder),
748 TK_CONFIG_COLOR_ONLY},
749 {TK_CONFIG_STRING, "-selectcommand", "selectCommand", "SelectCommand",
750 DEF_TNB_SELECT_CMD, Tk_Offset(Notebook, defTabStyle.command),
751 TK_CONFIG_NULL_OK},
752 {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
753 DEF_TNB_SELECT_FG_MONO, Tk_Offset(Notebook, defTabStyle.selColor),
754 TK_CONFIG_MONO_ONLY},
755 {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
756 DEF_TNB_SELECT_FOREGROUND, Tk_Offset(Notebook, defTabStyle.selColor),
757 TK_CONFIG_COLOR_ONLY},
758 {TK_CONFIG_CUSTOM, "-selectpad", "selectPad", "SelectPad",
759 DEF_TNB_SELECT_PAD, Tk_Offset(Notebook, xSelectPad),
760 TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
761 {TK_CONFIG_COLOR, "-shadowcolor", "shadowColor", "ShadowColor",
762 DEF_TNB_SHADOW_COLOR, Tk_Offset(Notebook, shadowColor), 0},
763 {TK_CONFIG_CUSTOM, "-side", "side", "side",
764 DEF_TNB_SIDE, Tk_Offset(Notebook, side),
765 TK_CONFIG_DONT_SET_DEFAULT, &sideOption},
766 {TK_CONFIG_CUSTOM, "-slant", "slant", "Slant",
767 DEF_TNB_SLANT, Tk_Offset(Notebook, slant),
768 TK_CONFIG_DONT_SET_DEFAULT, &slantOption},
769 {TK_CONFIG_BORDER, "-tabbackground", "tabBackground", "Background",
770 DEF_TNB_TAB_BG_MONO, Tk_Offset(Notebook, defTabStyle.border),
771 TK_CONFIG_MONO_ONLY},
772 {TK_CONFIG_BORDER, "-tabbackground", "tabBackground", "Background",
773 DEF_TNB_TAB_BACKGROUND, Tk_Offset(Notebook, defTabStyle.border),
774 TK_CONFIG_COLOR_ONLY},
775 {TK_CONFIG_CUSTOM, "-tabborderwidth", "tabBorderWidth", "BorderWidth",
776 DEF_TNB_BORDERWIDTH, Tk_Offset(Notebook, defTabStyle.borderWidth),
777 TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
778 {TK_CONFIG_COLOR, "-tabforeground", "tabForeground", "Foreground",
779 DEF_TNB_TEXT_COLOR, Tk_Offset(Notebook, defTabStyle.textColor),
780 TK_CONFIG_COLOR_ONLY},
781 {TK_CONFIG_COLOR, "-tabforeground", "tabForeground", "Foreground",
782 DEF_TNB_TEXT_MONO, Tk_Offset(Notebook, defTabStyle.textColor),
783 TK_CONFIG_MONO_ONLY},
784 {TK_CONFIG_RELIEF, "-tabrelief", "tabRelief", "TabRelief",
785 DEF_TNB_TAB_RELIEF, Tk_Offset(Notebook, defTabStyle.relief), 0},
786 {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
787 DEF_TNB_TAKE_FOCUS, Tk_Offset(Notebook, takeFocus), TK_CONFIG_NULL_OK},
788 {TK_CONFIG_BOOLEAN, "-tearoff", "tearoff", "Tearoff",
789 DEF_TNB_TEAROFF, Tk_Offset(Notebook, tearoff),
790 TK_CONFIG_DONT_SET_DEFAULT},
791 {TK_CONFIG_CUSTOM, "-textside", "textSide", "TextSide",
792 DEF_TNB_TEXT_SIDE, Tk_Offset(Notebook, defTabStyle.textSide),
793 TK_CONFIG_DONT_SET_DEFAULT, &sideOption},
794 {TK_CONFIG_CUSTOM, "-tiers", "tiers", "Tiers",
795 DEF_TNB_TIERS, Tk_Offset(Notebook, reqTiers),
796 TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveCountOption},
797 {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile",
798 (char *)NULL, Tk_Offset(Notebook, tile), TK_CONFIG_NULL_OK,
799 &bltTileOption},
800 {TK_CONFIG_CUSTOM, "-width", "width", "Width",
801 DEF_TNB_WIDTH, Tk_Offset(Notebook, reqWidth),
802 TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
803 {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
804 (char *)NULL, 0, 0}
805};
806
807/* Forward Declarations */
808static void DestroyNotebook _ANSI_ARGS_((DestroyData dataPtr));
809static void DestroyTearoff _ANSI_ARGS_((DestroyData dataPtr));
810static void EmbeddedWidgetEventProc _ANSI_ARGS_((ClientData clientdata,
811 XEvent *eventPtr));
812static void TearoffEventProc _ANSI_ARGS_((ClientData clientdata,
813 XEvent *eventPtr));
814static void NotebookEventProc _ANSI_ARGS_((ClientData clientdata,
815 XEvent *eventPtr));
816static void DrawLabel _ANSI_ARGS_((Notebook *nbPtr, Tab *tabPtr,
817 Drawable drawable));
818static void DrawFolder _ANSI_ARGS_((Notebook *nbPtr, Tab *tabPtr,
819 Drawable drawable));
820static void DisplayNotebook _ANSI_ARGS_((ClientData clientData));
821static void DisplayTearoff _ANSI_ARGS_((ClientData clientData));
822static void NotebookInstDeletedCmd _ANSI_ARGS_((ClientData clientdata));
823static int NotebookInstCmd _ANSI_ARGS_((ClientData clientdata,
824 Tcl_Interp *interp, int argc, char **argv));
825static void GetWindowRectangle _ANSI_ARGS_((Tab *tabPtr, Tk_Window parent,
826 int tearOff, XRectangle *rectPtr));
827static void ArrangeWindow _ANSI_ARGS_((Tk_Window tkwin, XRectangle *rectPtr,
828 int force));
829static void EventuallyRedraw _ANSI_ARGS_((Notebook *nbPtr));
830static void EventuallyRedrawTearoff _ANSI_ARGS_((Tab *tabPtr));
831static void ComputeLayout _ANSI_ARGS_((Notebook *nbPtr));
832static void DrawOuterBorders _ANSI_ARGS_((Notebook *nbPtr,
833 Drawable drawable));
834
835static Tk_ImageChangedProc ImageChangedProc;
836static Blt_TileChangedProc TileChangedProc;
837static Blt_BindTagProc GetTags;
838static Blt_BindPickProc PickTab;
839static Tcl_IdleProc AdoptWindow;
840static Tcl_CmdProc NotebookCmd;
841
842static ClientData
843MakeTag(nbPtr, tagName)
844 Notebook *nbPtr;
845 char *tagName;
846{
847 Blt_HashEntry *hPtr;
848 int isNew;
849
850 hPtr = Blt_CreateHashEntry(&(nbPtr->tagTable), tagName, &isNew);
851 assert(hPtr);
852 return Blt_GetHashKey(&(nbPtr->tagTable), hPtr);
853}
854
855/*
856 *----------------------------------------------------------------------
857 *
858 * WorldToScreen --
859 *
860 * Converts world coordinates to screen coordinates. Note that
861 * the world view is always tabs up.
862 *
863 * Results:
864 * The screen coordinates are returned via *xScreenPtr and *yScreenPtr.
865 *
866 *----------------------------------------------------------------------
867 */
868static void
869WorldToScreen(nbPtr, x, y, xScreenPtr, yScreenPtr)
870 Notebook *nbPtr;
871 int x, y;
872 int *xScreenPtr, *yScreenPtr;
873{
874 int sx, sy;
875
876 sx = sy = 0; /* Suppress compiler warning. */
877
878 /* Translate world X-Y to screen coordinates */
879 /*
880 * Note that the world X-coordinate is translated by the selected label's
881 * X padding. This is done only to keep the scroll range is between
882 * 0.0 and 1.0, rather adding/subtracting the pad in various locations.
883 * It may be changed back in the future.
884 */
885 x += (nbPtr->inset +
886 nbPtr->xSelectPad -
887 nbPtr->scrollOffset);
888 y += nbPtr->inset + nbPtr->yPad;
889
890 switch (nbPtr->side) {
891 case SIDE_TOP:
892 sx = x, sy = y; /* Do nothing */
893 break;
894 case SIDE_RIGHT:
895 sx = Tk_Width(nbPtr->tkwin) - y;
896 sy = x;
897 break;
898 case SIDE_LEFT:
899 sx = y, sy = x; /* Flip coordinates */
900 break;
901 case SIDE_BOTTOM:
902 sx = x;
903 sy = Tk_Height(nbPtr->tkwin) - y;
904 break;
905 }
906 *xScreenPtr = sx;
907 *yScreenPtr = sy;
908}
909
910/*
911 *----------------------------------------------------------------------
912 *
913 * EventuallyRedraw --
914 *
915 * Queues a request to redraw the widget at the next idle point.
916 *
917 * Results:
918 * None.
919 *
920 * Side effects:
921 * Information gets redisplayed. Right now we don't do selective
922 * redisplays: the whole window will be redrawn.
923 *
924 *----------------------------------------------------------------------
925 */
926static void
927EventuallyRedraw(nbPtr)
928 Notebook *nbPtr;
929{
930 if ((nbPtr->tkwin != NULL) && !(nbPtr->flags & TNB_REDRAW)) {
931 nbPtr->flags |= TNB_REDRAW;
932 Tcl_DoWhenIdle(DisplayNotebook, nbPtr);
933 }
934}
935
936/*
937 *----------------------------------------------------------------------
938 *
939 * EventuallyRedrawTearoff --
940 *
941 * Queues a request to redraw the tearoff at the next idle point.
942 *
943 * Results:
944 * None.
945 *
946 * Side effects:
947 * Information gets redisplayed. Right now we don't do selective
948 * redisplays: the whole window will be redrawn.
949 *
950 *----------------------------------------------------------------------
951 */
952static void
953EventuallyRedrawTearoff(tabPtr)
954 Tab *tabPtr;
955{
956 if ((tabPtr->tkwin != NULL) && !(tabPtr->flags & TAB_REDRAW)) {
957 tabPtr->flags |= TAB_REDRAW;
958 Tcl_DoWhenIdle(DisplayTearoff, tabPtr);
959 }
960}
961
962/*
963 *----------------------------------------------------------------------
964 *
965 * ImageChangedProc
966 *
967 * This routine is called whenever an image displayed in a tab
968 * changes. In this case, we assume that everything will change
969 * and queue a request to re-layout and redraw the entire notebook.
970 *
971 * Results:
972 * None.
973 *
974 *----------------------------------------------------------------------
975 */
976/* ARGSUSED */
977static void
978ImageChangedProc(clientData, x, y, width, height, imageWidth, imageHeight)
979 ClientData clientData;
980 int x, y, width, height; /* Not used. */
981 int imageWidth, imageHeight;/* Not used. */
982{
983 Notebook *nbPtr = clientData;
984
985 nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
986 EventuallyRedraw(nbPtr);
987}
988
989/*
990 *----------------------------------------------------------------------
991 *
992 * GetImage --
993 *
994 * This is a wrapper procedure for Tk_GetImage. The problem is
995 * that if the same image is used repeatedly in the same widget,
996 * the separate instances are saved in a linked list. This makes
997 * it especially slow to destroy the widget. As a workaround,
998 * this routine hashes the image and maintains a reference count
999 * for it.
1000 *
1001 * Results:
1002 * Returns a pointer to the new image.
1003 *
1004 *----------------------------------------------------------------------
1005 */
1006static TabImage
1007GetImage(nbPtr, interp, tkwin, name)
1008 Notebook *nbPtr;
1009 Tcl_Interp *interp;
1010 Tk_Window tkwin;
1011 char *name;
1012{
1013 struct TabImageStruct *imagePtr;
1014 int isNew;
1015 Blt_HashEntry *hPtr;
1016
1017 hPtr = Blt_CreateHashEntry(&(nbPtr->imageTable), name, &isNew);
1018 if (isNew) {
1019 Tk_Image tkImage;
1020 int width, height;
1021
1022 tkImage = Tk_GetImage(interp, tkwin, name, ImageChangedProc, nbPtr);
1023 if (tkImage == NULL) {
1024 Blt_DeleteHashEntry(&(nbPtr->imageTable), hPtr);
1025 return NULL;
1026 }
1027 Tk_SizeOfImage(tkImage, &width, &height);
1028 imagePtr = Blt_Malloc(sizeof(struct TabImageStruct));
1029 imagePtr->tkImage = tkImage;
1030 imagePtr->hashPtr = hPtr;
1031 imagePtr->refCount = 1;
1032 imagePtr->width = width;
1033 imagePtr->height = height;
1034 Blt_SetHashValue(hPtr, imagePtr);
1035 } else {
1036 imagePtr = (struct TabImageStruct *)Blt_GetHashValue(hPtr);
1037 imagePtr->refCount++;
1038 }
1039 return imagePtr;
1040}
1041
1042/*
1043 *----------------------------------------------------------------------
1044 *
1045 * FreeImage --
1046 *
1047 * Releases the image if it's not being used anymore by this
1048 * widget. Note there may be several uses of the same image
1049 * by many tabs.
1050 *
1051 * Results:
1052 * None.
1053 *
1054 * Side Effects:
1055 * The reference count is decremented and the image is freed
1056 * is it's not being used anymore.
1057 *
1058 *----------------------------------------------------------------------
1059 */
1060static void
1061FreeImage(nbPtr, imagePtr)
1062 Notebook *nbPtr;
1063 struct TabImageStruct *imagePtr;
1064{
1065 imagePtr->refCount--;
1066 if (imagePtr->refCount == 0) {
1067 Blt_DeleteHashEntry(&(nbPtr->imageTable), imagePtr->hashPtr);
1068 Tk_FreeImage(imagePtr->tkImage);
1069 Blt_Free(imagePtr);
1070 }
1071}
1072
1073/*
1074 *----------------------------------------------------------------------
1075 *
1076 * StringToImage --
1077 *
1078 * Converts an image name into a Tk image token.
1079 *
1080 * Results:
1081 * If the string is successfully converted, TCL_OK is returned.
1082 * Otherwise, TCL_ERROR is returned and an error message is left
1083 * in interpreter's result field.
1084 *
1085 *----------------------------------------------------------------------
1086 */
1087/*ARGSUSED*/
1088static int
1089StringToImage(clientData, interp, tkwin, string, widgRec, offset)
1090 ClientData clientData; /* Contains a pointer to the notebook containing
1091 * this image. */
1092 Tcl_Interp *interp; /* Interpreter to send results back to */
1093 Tk_Window tkwin; /* Window associated with the notebook. */
1094 char *string; /* String representation */
1095 char *widgRec; /* Widget record */
1096 int offset; /* Offset to field in structure */
1097{
1098 Notebook *nbPtr = *(Notebook **)clientData;
1099 TabImage *imagePtr = (TabImage *) (widgRec + offset);
1100 TabImage image;
1101
1102 image = NULL;
1103 if ((string != NULL) && (*string != '\0')) {
1104 image = GetImage(nbPtr, interp, tkwin, string);
1105 if (image == NULL) {
1106 return TCL_ERROR;
1107 }
1108 }
1109 if (*imagePtr != NULL) {
1110 FreeImage(nbPtr, *imagePtr);
1111 }
1112 *imagePtr = image;
1113 return TCL_OK;
1114}
1115
1116/*
1117 *----------------------------------------------------------------------
1118 *
1119 * ImageToString --
1120 *
1121 * Converts the Tk image back to its string representation (i.e.
1122 * its name).
1123 *
1124 * Results:
1125 * The name of the image is returned.
1126 *
1127 *----------------------------------------------------------------------
1128 */
1129/*ARGSUSED*/
1130static char *
1131ImageToString(clientData, tkwin, widgRec, offset, freeProcPtr)
1132 ClientData clientData; /* Pointer to notebook containing image. */
1133 Tk_Window tkwin; /* Not used. */
1134 char *widgRec; /* Widget record */
1135 int offset; /* Offset of field in record */
1136 Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
1137{
1138 Notebook *nbPtr = *(Notebook **)clientData;
1139 TabImage *imagePtr = (TabImage *) (widgRec + offset);
1140
1141 if (*imagePtr == NULL) {
1142 return "";
1143 }
1144 return Blt_GetHashKey(&(nbPtr->imageTable), (*imagePtr)->hashPtr);
1145}
1146
1147/*
1148 *----------------------------------------------------------------------
1149 *
1150 * StringToWindow --
1151 *
1152 * Converts a window name into Tk window.
1153 *
1154 * Results:
1155 * If the string is successfully converted, TCL_OK is returned.
1156 * Otherwise, TCL_ERROR is returned and an error message is left
1157 * in interpreter's result field.
1158 *
1159 *----------------------------------------------------------------------
1160 */
1161/*ARGSUSED*/
1162static int
1163StringToWindow(clientData, interp, parent, string, widgRec, offset)
1164 ClientData clientData; /* Not used. */
1165 Tcl_Interp *interp; /* Interpreter to send results back to */
1166 Tk_Window parent; /* Parent window */
1167 char *string; /* String representation. */
1168 char *widgRec; /* Widget record */
1169 int offset; /* Offset to field in structure */
1170{
1171 Tab *tabPtr = (Tab *)widgRec;
1172 Tk_Window *tkwinPtr = (Tk_Window *)(widgRec + offset);
1173 Tk_Window old, tkwin;
1174 Notebook *nbPtr;
1175
1176 old = *tkwinPtr;
1177 tkwin = NULL;
1178 nbPtr = tabPtr->nbPtr;
1179 if ((string != NULL) && (*string != '\0')) {
1180 tkwin = Tk_NameToWindow(interp, string, parent);
1181 if (tkwin == NULL) {
1182 return TCL_ERROR;
1183 }
1184 if (tkwin == old) {
1185 return TCL_OK;
1186 }
1187 /*
1188 * Allow only widgets that are children of the notebook to be
1189 * embedded into the page. This way we can make assumptions about
1190 * the window based upon its parent; either it's the notebook window
1191 * or it has been torn off.
1192 */
1193 parent = Tk_Parent(tkwin);
1194 if (parent != nbPtr->tkwin) {
1195 Tcl_AppendResult(interp, "can't manage \"", Tk_PathName(tkwin),
1196 "\" in notebook \"", Tk_PathName(nbPtr->tkwin), "\"",
1197 (char *)NULL);
1198 return TCL_ERROR;
1199 }
1200 Tk_ManageGeometry(tkwin, &tabMgrInfo, tabPtr);
1201 Tk_CreateEventHandler(tkwin, StructureNotifyMask,
1202 EmbeddedWidgetEventProc, tabPtr);
1203
1204 /*
1205 * We need to make the window to exist immediately. If the
1206 * window is torn off (placed into another container window),
1207 * the timing between the container and the its new child
1208 * (this window) gets tricky. This should work for Tk 4.2.
1209 */
1210 Tk_MakeWindowExist(tkwin);
1211 }
1212 if (old != NULL) {
1213 if (tabPtr->container != NULL) {
1214 Tcl_EventuallyFree(tabPtr, DestroyTearoff);
1215 }
1216 Tk_DeleteEventHandler(old, StructureNotifyMask,
1217 EmbeddedWidgetEventProc, tabPtr);
1218 Tk_ManageGeometry(old, (Tk_GeomMgr *) NULL, tabPtr);
1219 Tk_UnmapWindow(old);
1220 }
1221 *tkwinPtr = tkwin;
1222 return TCL_OK;
1223}
1224
1225/*
1226 *----------------------------------------------------------------------
1227 *
1228 * WindowToString --
1229 *
1230 * Converts the Tk window back to its string representation (i.e.
1231 * its name).
1232 *
1233 * Results:
1234 * The name of the window is returned.
1235 *
1236 *----------------------------------------------------------------------
1237 */
1238/*ARGSUSED*/
1239static char *
1240WindowToString(clientData, parent, widgRec, offset, freeProcPtr)
1241 ClientData clientData; /* Not used. */
1242 Tk_Window parent; /* Not used. */
1243 char *widgRec; /* Widget record */
1244 int offset; /* Offset of field in record */
1245 Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
1246{
1247 Tk_Window tkwin = *(Tk_Window *)(widgRec + offset);
1248
1249 if (tkwin == NULL) {
1250 return "";
1251 }
1252 return Tk_PathName(tkwin);
1253}
1254
1255/*
1256 *----------------------------------------------------------------------
1257 *
1258 * StringToSide --
1259 *
1260 * Converts "left", "right", "top", "bottom", into a numeric token
1261 * designating the side of the notebook which to display tabs.
1262 *
1263 * Results:
1264 * If the string is successfully converted, TCL_OK is returned.
1265 * Otherwise, TCL_ERROR is returned and an error message is left
1266 * in interpreter's result field.
1267 *
1268 *----------------------------------------------------------------------
1269 */
1270/*ARGSUSED */
1271static int
1272StringToSide(clientData, interp, parent, string, widgRec, offset)
1273 ClientData clientData; /* Not used. */
1274 Tcl_Interp *interp; /* Interpreter to send results back to */
1275 Tk_Window parent; /* Parent window */
1276 char *string; /* Option value string */
1277 char *widgRec; /* Widget record */
1278 int offset; /* offset to field in structure */
1279{
1280 int *sidePtr = (int *)(widgRec + offset);
1281 char c;
1282 unsigned int length;
1283
1284 c = string[0];
1285 length = strlen(string);
1286 if ((c == 'l') && (strncmp(string, "left", length) == 0)) {
1287 *sidePtr = SIDE_LEFT;
1288 } else if ((c == 'r') && (strncmp(string, "right", length) == 0)) {
1289 *sidePtr = SIDE_RIGHT;
1290 } else if ((c == 't') && (strncmp(string, "top", length) == 0)) {
1291 *sidePtr = SIDE_TOP;
1292 } else if ((c == 'b') && (strncmp(string, "bottom", length) == 0)) {
1293 *sidePtr = SIDE_BOTTOM;
1294 } else {
1295 Tcl_AppendResult(interp, "bad side \"", string,
1296 "\": should be left, right, top, or bottom", (char *)NULL);
1297 return TCL_ERROR;
1298 }
1299 return TCL_OK;
1300}
1301
1302/*
1303 *----------------------------------------------------------------------
1304 *
1305 * SideToString --
1306 *
1307 * Converts the window into its string representation (its name).
1308 *
1309 * Results:
1310 * The name of the window is returned.
1311 *
1312 *----------------------------------------------------------------------
1313 */
1314/*ARGSUSED*/
1315static char *
1316SideToString(clientData, parent, widgRec, offset, freeProcPtr)
1317 ClientData clientData; /* Not used. */
1318 Tk_Window parent; /* Not used. */
1319 char *widgRec; /* Widget record */
1320 int offset; /* offset of windows array in record */
1321 Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
1322{
1323 int side = *(int *)(widgRec + offset);
1324
1325 switch (side) {
1326 case SIDE_LEFT:
1327 return "left";
1328 case SIDE_RIGHT:
1329 return "right";
1330 case SIDE_BOTTOM:
1331 return "bottom";
1332 case SIDE_TOP:
1333 return "top";
1334 }
1335 return "unknown side value";
1336}
1337
1338/*
1339 *----------------------------------------------------------------------
1340 *
1341 * StringToSlant --
1342 *
1343 * Converts the slant style string into its numeric representation.
1344 *
1345 * Valid style strings are:
1346 *
1347 * "none" Both sides are straight.
1348 * "left" Left side is slanted.
1349 * "right" Right side is slanted.
1350 * "both" Both sides are slanted.
1351 *
1352 *----------------------------------------------------------------------
1353 */
1354/*ARGSUSED*/
1355static int
1356StringToSlant(clientData, interp, tkwin, string, widgRec, offset)
1357 ClientData clientData; /* Not used. */
1358 Tcl_Interp *interp; /* Interpreter to send results back to */
1359 Tk_Window tkwin; /* Not used. */
1360 char *string; /* String representation of attribute. */
1361 char *widgRec; /* Widget record */
1362 int offset; /* Offset of field in widget record. */
1363{
1364 int *slantPtr = (int *)(widgRec + offset);
1365 unsigned int length;
1366 char c;
1367
1368 c = string[0];
1369 length = strlen(string);
1370 if ((c == 'n') && (strncmp(string, "none", length) == 0)) {
1371 *slantPtr = SLANT_NONE;
1372 } else if ((c == 'l') && (strncmp(string, "left", length) == 0)) {
1373 *slantPtr = SLANT_LEFT;
1374 } else if ((c == 'r') && (strncmp(string, "right", length) == 0)) {
1375 *slantPtr = SLANT_RIGHT;
1376 } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) {
1377 *slantPtr = SLANT_BOTH;
1378 } else {
1379 Tcl_AppendResult(interp, "bad argument \"", string,
1380 "\": should be \"none\", \"left\", \"right\", or \"both\"",
1381 (char *)NULL);
1382 return TCL_ERROR;
1383 }
1384 return TCL_OK;
1385}
1386
1387/*
1388 *----------------------------------------------------------------------
1389 *
1390 * SlantToString --
1391 *
1392 * Returns the slant style string based upon the slant flags.
1393 *
1394 * Results:
1395 * The slant style string is returned.
1396 *
1397 *----------------------------------------------------------------------
1398 */
1399/*ARGSUSED*/
1400static char *
1401SlantToString(clientData, tkwin, widgRec, offset, freeProcPtr)
1402 ClientData clientData; /* Not used. */
1403 Tk_Window tkwin; /* Not used. */
1404 char *widgRec; /* Widget structure record. */
1405 int offset; /* Offset of field in widget record. */
1406 Tcl_FreeProc **freeProcPtr; /* Not used. */
1407{
1408 int slant = *(int *)(widgRec + offset);
1409
1410 switch (slant) {
1411 case SLANT_LEFT:
1412 return "left";
1413 case SLANT_RIGHT:
1414 return "right";
1415 case SLANT_NONE:
1416 return "none";
1417 case SLANT_BOTH:
1418 return "both";
1419 default:
1420 return "unknown value";
1421 }
1422}
1423
1424
1425
1426static int
1427WorldY(tabPtr)
1428 Tab *tabPtr;
1429{
1430 int tier;
1431
1432 tier = tabPtr->nbPtr->nTiers - tabPtr->tier;
1433 return tier * tabPtr->nbPtr->tabHeight;
1434}
1435
1436static int
1437TabIndex(nbPtr, tabPtr)
1438 Notebook *nbPtr;
1439 Tab *tabPtr;
1440{
1441 Tab *t2Ptr;
1442 int count;
1443 Blt_ChainLink *linkPtr;
1444
1445 count = 0;
1446 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
1447 linkPtr = Blt_ChainNextLink(linkPtr)) {
1448 t2Ptr = Blt_ChainGetValue(linkPtr);
1449 if (t2Ptr == tabPtr) {
1450 return count;
1451 }
1452 count++;
1453 }
1454 return -1;
1455}
1456
1457/*
1458 * ----------------------------------------------------------------------
1459 *
1460 * RenumberTiers --
1461 *
1462 * In multi-tier mode, we need to find the start of the tier
1463 * containing the newly selected tab.
1464 *
1465 * Tiers are draw from the last tier to the first, so that
1466 * the the lower-tiered tabs will partially cover the bottoms
1467 * of tab directly above it. This simplifies the drawing of
1468 * tabs because we don't worry how tabs are clipped by their
1469 * neighbors.
1470 *
1471 * In addition, tabs are re-marked with the correct tier number.
1472 *
1473 * Results:
1474 * None.
1475 *
1476 * Side Effects:
1477 * Renumbering the tab's tier will change the vertical placement
1478 * of the tab (i.e. shift tiers).
1479 *
1480 * ----------------------------------------------------------------------
1481 */
1482static void
1483RenumberTiers(nbPtr, tabPtr)
1484 Notebook *nbPtr;
1485 Tab *tabPtr;
1486{
1487 int tier;
1488 Tab *prevPtr;
1489 Blt_ChainLink *linkPtr, *lastPtr;
1490
1491 nbPtr->focusPtr = nbPtr->selectPtr = tabPtr;
1492 Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr, NULL);
1493
1494 tier = tabPtr->tier;
1495 for (linkPtr = Blt_ChainPrevLink(tabPtr->linkPtr); linkPtr != NULL;
1496 linkPtr = lastPtr) {
1497 lastPtr = Blt_ChainPrevLink(linkPtr);
1498 prevPtr = Blt_ChainGetValue(linkPtr);
1499 if ((prevPtr == NULL) || (prevPtr->tier != tier)) {
1500 break;
1501 }
1502 tabPtr = prevPtr;
1503 }
1504 nbPtr->startPtr = tabPtr;
1505 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
1506 linkPtr = Blt_ChainNextLink(linkPtr)) {
1507 tabPtr = Blt_ChainGetValue(linkPtr);
1508 tabPtr->tier = (tabPtr->tier - tier + 1);
1509 if (tabPtr->tier < 1) {
1510 tabPtr->tier += nbPtr->nTiers;
1511 }
1512 tabPtr->worldY = WorldY(tabPtr);
1513 }
1514}
1515
1516/*
1517 *----------------------------------------------------------------------
1518 *
1519 * PickTab --
1520 *
1521 * Searches the tab located within the given screen X-Y coordinates
1522 * in the viewport. Note that tabs overlap slightly, so that its
1523 * important to search from the innermost tier out.
1524 *
1525 * Results:
1526 * Returns the pointer to the tab. If the pointer isn't contained
1527 * by any tab, NULL is returned.
1528 *
1529 *----------------------------------------------------------------------
1530 */
1531/*ARGSUSED*/
1532static ClientData
1533PickTab(clientData, x, y, contextPtr)
1534 ClientData clientData;
1535 int x, y; /* Screen coordinates to test. */
1536 ClientData *contextPtr;
1537{
1538 Notebook *nbPtr = clientData;
1539 Tab *tabPtr;
1540 Blt_ChainLink *linkPtr;
1541
1542 if (contextPtr != NULL) {
1543 *contextPtr = NULL;
1544 }
1545 tabPtr = nbPtr->selectPtr;
1546 if ((nbPtr->tearoff) && (tabPtr != NULL) &&
1547 (tabPtr->container == NULL) && (tabPtr->tkwin != NULL)) {
1548 int top, bottom, left, right;
1549 int sx, sy;
1550
1551 /* Check first for perforation on the selected tab. */
1552 WorldToScreen(nbPtr, tabPtr->worldX + 2,
1553 tabPtr->worldY + tabPtr->worldHeight + 4, &sx, &sy);
1554 if (nbPtr->side & SIDE_HORIZONTAL) {
1555 left = sx - 2;
1556 right = left + tabPtr->screenWidth;
1557 top = sy - 4;
1558 bottom = sy + 4;
1559 } else {
1560 left = sx - 4;
1561 right = sx + 4;
1562 top = sy - 2;
1563 bottom = top + tabPtr->screenHeight;
1564 }
1565 if ((x >= left) && (y >= top) && (x < right) && (y < bottom)) {
1566 if (contextPtr != NULL) {
1567 *contextPtr = TAB_PERFORATION;
1568 }
1569 return nbPtr->selectPtr;
1570 }
1571 }
1572 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
1573 linkPtr = Blt_ChainNextLink(linkPtr)) {
1574 tabPtr = Blt_ChainGetValue(linkPtr);
1575 if (!(tabPtr->flags & TAB_VISIBLE)) {
1576 continue;
1577 }
1578 if ((x >= tabPtr->screenX) && (y >= tabPtr->screenY) &&
1579 (x <= (tabPtr->screenX + tabPtr->screenWidth)) &&
1580 (y < (tabPtr->screenY + tabPtr->screenHeight))) {
1581 if (contextPtr != NULL) {
1582 *contextPtr = TAB_LABEL;
1583 }
1584 return tabPtr;
1585 }
1586 }
1587 return NULL;
1588}
1589
1590static Tab *
1591TabLeft(tabPtr)
1592 Tab *tabPtr;
1593{
1594 Blt_ChainLink *linkPtr;
1595
1596 linkPtr = Blt_ChainPrevLink(tabPtr->linkPtr);
1597 if (linkPtr != NULL) {
1598 Tab *newPtr;
1599
1600 newPtr = Blt_ChainGetValue(linkPtr);
1601 /* Move only if the next tab is on another tier. */
1602 if (newPtr->tier == tabPtr->tier) {
1603 tabPtr = newPtr;
1604 }
1605 }
1606 return tabPtr;
1607}
1608
1609static Tab *
1610TabRight(tabPtr)
1611 Tab *tabPtr;
1612{
1613 Blt_ChainLink *linkPtr;
1614
1615 linkPtr = Blt_ChainNextLink(tabPtr->linkPtr);
1616 if (linkPtr != NULL) {
1617 Tab *newPtr;
1618
1619 newPtr = Blt_ChainGetValue(linkPtr);
1620 /* Move only if the next tab is on another tier. */
1621 if (newPtr->tier == tabPtr->tier) {
1622 tabPtr = newPtr;
1623 }
1624 }
1625 return tabPtr;
1626}
1627
1628static Tab *
1629TabUp(tabPtr)
1630 Tab *tabPtr;
1631{
1632 if (tabPtr != NULL) {
1633 Notebook *nbPtr;
1634 int x, y;
1635 int worldX, worldY;
1636
1637 nbPtr = tabPtr->nbPtr;
1638 worldX = tabPtr->worldX + (tabPtr->worldWidth / 2);
1639 worldY = tabPtr->worldY - (nbPtr->tabHeight / 2);
1640 WorldToScreen(nbPtr, worldX, worldY, &x, &y);
1641
1642 tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL);
1643 if (tabPtr == NULL) {
1644 /*
1645 * We might have inadvertly picked the gap between two tabs,
1646 * so if the first pick fails, try again a little to the left.
1647 */
1648 WorldToScreen(nbPtr, worldX + nbPtr->gap, worldY, &x, &y);
1649 tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL);
1650 }
1651 if ((tabPtr == NULL) &&
1652 (nbPtr->focusPtr->tier < (nbPtr->nTiers - 1))) {
1653 worldY -= nbPtr->tabHeight;
1654 WorldToScreen(nbPtr, worldX, worldY, &x, &y);
1655 tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL);
1656 }
1657 if (tabPtr == NULL) {
1658 tabPtr = nbPtr->focusPtr;
1659 }
1660 }
1661 return tabPtr;
1662}
1663
1664static Tab *
1665TabDown(tabPtr)
1666 Tab *tabPtr;
1667{
1668 if (tabPtr != NULL) {
1669 Notebook *nbPtr;
1670 int x, y;
1671 int worldX, worldY;
1672
1673 nbPtr = tabPtr->nbPtr;
1674 worldX = tabPtr->worldX + (tabPtr->worldWidth / 2);
1675 worldY = tabPtr->worldY + (3 * nbPtr->tabHeight) / 2;
1676 WorldToScreen(nbPtr, worldX, worldY, &x, &y);
1677 tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL);
1678 if (tabPtr == NULL) {
1679 /*
1680 * We might have inadvertly picked the gap between two tabs,
1681 * so if the first pick fails, try again a little to the left.
1682 */
1683 WorldToScreen(nbPtr, worldX - nbPtr->gap, worldY, &x, &y);
1684 tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL);
1685 }
1686 if ((tabPtr == NULL) && (nbPtr->focusPtr->tier > 2)) {
1687 worldY += nbPtr->tabHeight;
1688 WorldToScreen(nbPtr, worldX, worldY, &x, &y);
1689 tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL);
1690 }
1691 if (tabPtr == NULL) {
1692 tabPtr = nbPtr->focusPtr;
1693 }
1694 }
1695 return tabPtr;
1696}
1697
1698/*
1699 *----------------------------------------------------------------------
1700 *
1701 * GetTab --
1702 *
1703 * Converts a string representing a tab index into a tab pointer.
1704 * The index may be in one of the following forms:
1705 *
1706 * number Tab at position in the list of tabs.
1707 * @x,y Tab closest to the specified X-Y screen coordinates.
1708 * "active" Tab mouse is located over.
1709 * "focus" Tab is the widget's focus.
1710 * "select" Currently selected tab.
1711 * "right" Next tab from the focus tab.
1712 * "left" Previous tab from the focus tab.
1713 * "up" Next tab from the focus tab.
1714 * "down" Previous tab from the focus tab.
1715 * "end" Last tab in list.
1716 *
1717 * Results:
1718 * If the string is successfully converted, TCL_OK is returned.
1719 * The pointer to the node is returned via tabPtrPtr.
1720 * Otherwise, TCL_ERROR is returned and an error message is left
1721 * in interpreter's result field.
1722 *
1723 *----------------------------------------------------------------------
1724 */
1725static int
1726GetTab(nbPtr, string, tabPtrPtr, allowNull)
1727 Notebook *nbPtr;
1728 char *string;
1729 Tab **tabPtrPtr;
1730 int allowNull; /* Allow NULL tabPtr */
1731{
1732 Tab *tabPtr;
1733 Blt_ChainLink *linkPtr;
1734 int position;
1735 char c;
1736
1737 c = string[0];
1738 tabPtr = NULL;
1739 if (nbPtr->focusPtr == NULL) {
1740 nbPtr->focusPtr = nbPtr->selectPtr;
1741 Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr, NULL);
1742 }
1743 if ((isdigit(UCHAR(c))) &&
1744 (Tcl_GetInt(nbPtr->interp, string, &position) == TCL_OK)) {
1745 linkPtr = Blt_ChainGetNthLink(nbPtr->chainPtr, position);
1746 if (linkPtr == NULL) {
1747 Tcl_AppendResult(nbPtr->interp, "can't find tab \"", string,
1748 "\" in \"", Tk_PathName(nbPtr->tkwin),
1749 "\": no such index", (char *)NULL);
1750 return TCL_ERROR;
1751 }
1752 tabPtr = Blt_ChainGetValue(linkPtr);
1753 } else if ((c == 'a') && (strcmp(string, "active") == 0)) {
1754 tabPtr = nbPtr->activePtr;
1755 } else if ((c == 'c') && (strcmp(string, "current") == 0)) {
1756 tabPtr = (Tab *)Blt_GetCurrentItem(nbPtr->bindTable);
1757 } else if ((c == 's') && (strcmp(string, "select") == 0)) {
1758 tabPtr = nbPtr->selectPtr;
1759 } else if ((c == 'f') && (strcmp(string, "focus") == 0)) {
1760 tabPtr = nbPtr->focusPtr;
1761 } else if ((c == 'u') && (strcmp(string, "up") == 0)) {
1762 switch (nbPtr->side) {
1763 case SIDE_LEFT:
1764 case SIDE_RIGHT:
1765 tabPtr = TabLeft(nbPtr->focusPtr);
1766 break;
1767
1768 case SIDE_BOTTOM:
1769 tabPtr = TabDown(nbPtr->focusPtr);
1770 break;
1771
1772 case SIDE_TOP:
1773 tabPtr = TabUp(nbPtr->focusPtr);
1774 break;
1775 }
1776 } else if ((c == 'd') && (strcmp(string, "down") == 0)) {
1777 switch (nbPtr->side) {
1778 case SIDE_LEFT:
1779 case SIDE_RIGHT:
1780 tabPtr = TabRight(nbPtr->focusPtr);
1781 break;
1782
1783 case SIDE_BOTTOM:
1784 tabPtr = TabUp(nbPtr->focusPtr);
1785 break;
1786
1787 case SIDE_TOP:
1788 tabPtr = TabDown(nbPtr->focusPtr);
1789 break;
1790 }
1791 } else if ((c == 'l') && (strcmp(string, "left") == 0)) {
1792 switch (nbPtr->side) {
1793 case SIDE_LEFT:
1794 tabPtr = TabUp(nbPtr->focusPtr);
1795 break;
1796
1797 case SIDE_RIGHT:
1798 tabPtr = TabDown(nbPtr->focusPtr);
1799 break;
1800
1801 case SIDE_BOTTOM:
1802 case SIDE_TOP:
1803 tabPtr = TabLeft(nbPtr->focusPtr);
1804 break;
1805 }
1806 } else if ((c == 'r') && (strcmp(string, "right") == 0)) {
1807 switch (nbPtr->side) {
1808 case SIDE_LEFT:
1809 tabPtr = TabDown(nbPtr->focusPtr);
1810 break;
1811
1812 case SIDE_RIGHT:
1813 tabPtr = TabUp(nbPtr->focusPtr);
1814 break;
1815
1816 case SIDE_BOTTOM:
1817 case SIDE_TOP:
1818 tabPtr = TabRight(nbPtr->focusPtr);
1819 break;
1820 }
1821 } else if ((c == 'e') && (strcmp(string, "end") == 0)) {
1822 linkPtr = Blt_ChainLastLink(nbPtr->chainPtr);
1823 if (linkPtr != NULL) {
1824 tabPtr = Blt_ChainGetValue(linkPtr);
1825 }
1826 } else if (c == '@') {
1827 int x, y;
1828
1829 if (Blt_GetXY(nbPtr->interp, nbPtr->tkwin, string, &x, &y)
1830 != TCL_OK) {
1831 return TCL_ERROR;
1832 }
1833 tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL);
1834 } else {
1835 Blt_HashEntry *hPtr;
1836
1837 hPtr = Blt_FindHashEntry(&(nbPtr->tabTable), string);
1838 if (hPtr != NULL) {
1839 tabPtr = (Tab *)Blt_GetHashValue(hPtr);
1840 }
1841 }
1842 *tabPtrPtr = tabPtr;
1843 Tcl_ResetResult(nbPtr->interp);
1844
1845 if ((!allowNull) && (tabPtr == NULL)) {
1846 Tcl_AppendResult(nbPtr->interp, "can't find tab \"", string,
1847 "\" in \"", Tk_PathName(nbPtr->tkwin), "\"", (char *)NULL);
1848 return TCL_ERROR;
1849 }
1850 return TCL_OK;
1851}
1852
1853static Tab *
1854NextOrLastTab(tabPtr)
1855 Tab *tabPtr;
1856{
1857 if (tabPtr->linkPtr != NULL) {
1858 Blt_ChainLink *linkPtr;
1859
1860 linkPtr = Blt_ChainNextLink(tabPtr->linkPtr);
1861 if (linkPtr == NULL) {
1862 linkPtr = Blt_ChainPrevLink(tabPtr->linkPtr);
1863 }
1864 if (linkPtr != NULL) {
1865 return Blt_ChainGetValue(linkPtr);
1866 }
1867 }
1868 return NULL;
1869}
1870
1871/*
1872 * --------------------------------------------------------------
1873 *
1874 * EmbeddedWidgetEventProc --
1875 *
1876 * This procedure is invoked by the Tk dispatcher for various
1877 * events on embedded widgets contained in the notebook.
1878 *
1879 * Results:
1880 * None.
1881 *
1882 * Side effects:
1883 * When an embedded widget gets deleted, internal structures get
1884 * cleaned up. When it gets resized, the notebook is redisplayed.
1885 *
1886 * --------------------------------------------------------------
1887 */
1888static void
1889EmbeddedWidgetEventProc(clientData, eventPtr)
1890 ClientData clientData; /* Information about the tab window. */
1891 XEvent *eventPtr; /* Information about event. */
1892{
1893 Tab *tabPtr = clientData;
1894
1895 if ((tabPtr == NULL) || (tabPtr->tkwin == NULL)) {
1896 return;
1897 }
1898 switch (eventPtr->type) {
1899 case ConfigureNotify:
1900 /*
1901 * If the window's requested size changes, redraw the window.
1902 * But only if it's currently the selected page.
1903 */
1904 if ((tabPtr->container == NULL) && (Tk_IsMapped(tabPtr->tkwin)) &&
1905 (tabPtr->nbPtr->selectPtr == tabPtr)) {
1906 EventuallyRedraw(tabPtr->nbPtr);
1907 }
1908 break;
1909
1910 case DestroyNotify:
1911 /*
1912 * Mark the tab as deleted by dereferencing the Tk window
1913 * pointer. Redraw the window only if the tab is currently
1914 * visible.
1915 */
1916 if ((Tk_IsMapped(tabPtr->tkwin)) &&
1917 (tabPtr->nbPtr->selectPtr == tabPtr)) {
1918 EventuallyRedraw(tabPtr->nbPtr);
1919 }
1920 Tk_DeleteEventHandler(tabPtr->tkwin, StructureNotifyMask,
1921 EmbeddedWidgetEventProc, tabPtr);
1922 tabPtr->tkwin = NULL;
1923 break;
1924
1925 }
1926}
1927
1928/*
1929 * ----------------------------------------------------------------------
1930 *
1931 * EmbeddedWidgetCustodyProc --
1932 *
1933 * This procedure is invoked when a tab window has been
1934 * stolen by another geometry manager. The information and
1935 * memory associated with the tab window is released.
1936 *
1937 * Results:
1938 * None.
1939 *
1940 * Side effects:
1941 * Arranges for the widget formerly associated with the tab
1942 * window to have its layout re-computed and arranged at the
1943 * next idle point.
1944 *
1945 * ---------------------------------------------------------------------
1946 */
1947 /* ARGSUSED */
1948static void
1949EmbeddedWidgetCustodyProc(clientData, tkwin)
1950 ClientData clientData; /* Information about the former tab window. */
1951 Tk_Window tkwin; /* Not used. */
1952{
1953 Tab *tabPtr = clientData;
1954 Notebook *nbPtr;
1955
1956 if ((tabPtr == NULL) || (tabPtr->tkwin == NULL)) {
1957 return;
1958 }
1959 nbPtr = tabPtr->nbPtr;
1960 if (tabPtr->container != NULL) {
1961 Tcl_EventuallyFree(tabPtr, DestroyTearoff);
1962 }
1963 /*
1964 * Mark the tab as deleted by dereferencing the Tk window
1965 * pointer. Redraw the window only if the tab is currently
1966 * visible.
1967 */
1968 if (tabPtr->tkwin != NULL) {
1969 if (Tk_IsMapped(tabPtr->tkwin) && (nbPtr->selectPtr == tabPtr)) {
1970 nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
1971 EventuallyRedraw(nbPtr);
1972 }
1973 Tk_DeleteEventHandler(tabPtr->tkwin, StructureNotifyMask,
1974 EmbeddedWidgetEventProc, tabPtr);
1975 tabPtr->tkwin = NULL;
1976 }
1977}
1978
1979/*
1980 * -------------------------------------------------------------------------
1981 *
1982 * EmbeddedWidgetGeometryProc --
1983 *
1984 * This procedure is invoked by Tk_GeometryRequest for tab
1985 * windows managed by the widget.
1986 *
1987 * Results:
1988 * None.
1989 *
1990 * Side effects:
1991 * Arranges for tkwin, and all its managed siblings, to be
1992 * repacked and drawn at the next idle point.
1993 *
1994 * ------------------------------------------------------------------------
1995 */
1996 /* ARGSUSED */
1997static void
1998EmbeddedWidgetGeometryProc(clientData, tkwin)
1999 ClientData clientData; /* Information about window that got new
2000 * preferred geometry. */
2001 Tk_Window tkwin; /* Other Tk-related information about the
2002 * window. */
2003{
2004 Tab *tabPtr = clientData;
2005
2006 if ((tabPtr == NULL) || (tabPtr->tkwin == NULL)) {
2007 fprintf(stderr, "%s: line %d \"tkwin is null\"", __FILE__, __LINE__);
2008 return;
2009 }
2010 tabPtr->nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
2011 EventuallyRedraw(tabPtr->nbPtr);
2012}
2013
2014/*
2015 * ----------------------------------------------------------------------
2016 *
2017 * DestroyTab --
2018 *
2019 * ----------------------------------------------------------------------
2020 */
2021static void
2022DestroyTab(nbPtr, tabPtr)
2023 Notebook *nbPtr;
2024 Tab *tabPtr;
2025{
2026 Blt_HashEntry *hPtr;
2027
2028 if (tabPtr->flags & TAB_REDRAW) {
2029 Tcl_CancelIdleCall(DisplayTearoff, tabPtr);
2030 }
2031 if (tabPtr->container != NULL) {
2032 Tk_DestroyWindow(tabPtr->container);
2033 }
2034 if (tabPtr->tkwin != NULL) {
2035 Tk_ManageGeometry(tabPtr->tkwin, (Tk_GeomMgr *)NULL, tabPtr);
2036 Tk_DeleteEventHandler(tabPtr->tkwin, StructureNotifyMask,
2037 EmbeddedWidgetEventProc, tabPtr);
2038 if (Tk_IsMapped(tabPtr->tkwin)) {
2039 Tk_UnmapWindow(tabPtr->tkwin);
2040 }
2041 }
2042 if (tabPtr == nbPtr->activePtr) {
2043 nbPtr->activePtr = NULL;
2044 }
2045 if (tabPtr == nbPtr->selectPtr) {
2046 nbPtr->selectPtr = NextOrLastTab(tabPtr);
2047 }
2048 if (tabPtr == nbPtr->focusPtr) {
2049 nbPtr->focusPtr = nbPtr->selectPtr;
2050 Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr, NULL);
2051 }
2052 if (tabPtr == nbPtr->startPtr) {
2053 nbPtr->startPtr = NULL;
2054 }
2055 Tk_FreeOptions(tabConfigSpecs, (char *)tabPtr, nbPtr->display, 0);
2056 if (tabPtr->text != NULL) {
2057 Blt_FreeUid(tabPtr->text);
2058 }
2059 hPtr = Blt_FindHashEntry(&(nbPtr->tabTable), tabPtr->name);
2060 assert(hPtr);
2061 Blt_DeleteHashEntry(&(nbPtr->tabTable), hPtr);
2062
2063 if (tabPtr->image != NULL) {
2064 FreeImage(nbPtr, tabPtr->image);
2065 }
2066 if (tabPtr->name != NULL) {
2067 Blt_Free(tabPtr->name);
2068 }
2069 if (tabPtr->textGC != NULL) {
2070 Tk_FreeGC(nbPtr->display, tabPtr->textGC);
2071 }
2072 if (tabPtr->backGC != NULL) {
2073 Tk_FreeGC(nbPtr->display, tabPtr->backGC);
2074 }
2075 if (tabPtr->command != NULL) {
2076 Blt_FreeUid(tabPtr->command);
2077 }
2078 if (tabPtr->linkPtr != NULL) {
2079 Blt_ChainDeleteLink(nbPtr->chainPtr, tabPtr->linkPtr);
2080 }
2081 if (tabPtr->tags != NULL) {
2082 Blt_FreeUid(tabPtr->tags);
2083 }
2084 Blt_DeleteBindings(nbPtr->bindTable, tabPtr);
2085 Blt_Free(tabPtr);
2086}
2087
2088/*
2089 * ----------------------------------------------------------------------
2090 *
2091 * CreateTab --
2092 *
2093 * Creates a new tab structure. A tab contains information about
2094 * the state of the tab and its embedded window.
2095 *
2096 * Results:
2097 * Returns a pointer to the new tab structure.
2098 *
2099 * ----------------------------------------------------------------------
2100 */
2101static Tab *
2102CreateTab(nbPtr)
2103 Notebook *nbPtr;
2104{
2105 Tab *tabPtr;
2106 Blt_HashEntry *hPtr;
2107 int isNew;
2108 char string[200];
2109
2110 tabPtr = Blt_Calloc(1, sizeof(Tab));
2111 assert(tabPtr);
2112 tabPtr->nbPtr = nbPtr;
2113 sprintf(string, "tab%d", nbPtr->nextId++);
2114 tabPtr->name = Blt_Strdup(string);
2115 tabPtr->text = Blt_GetUid(string);
2116 tabPtr->fill = FILL_NONE;
2117 tabPtr->anchor = TK_ANCHOR_CENTER;
2118 tabPtr->container = NULL;
2119 tabPtr->state = STATE_NORMAL;
2120 hPtr = Blt_CreateHashEntry(&(nbPtr->tabTable), string, &isNew);
2121 Blt_SetHashValue(hPtr, tabPtr);
2122 return tabPtr;
2123}
2124
2125/*
2126 *----------------------------------------------------------------------
2127 *
2128 * TileChangedProc
2129 *
2130 * Stub for image change notifications. Since we immediately draw
2131 * the image into a pixmap, we don't really care about image changes.
2132 *
2133 * It would be better if Tk checked for NULL proc pointers.
2134 *
2135 * Results:
2136 * None.
2137 *
2138 *----------------------------------------------------------------------
2139 */
2140/*ARGSUSED*/
2141static void
2142TileChangedProc(clientData, tile)
2143 ClientData clientData;
2144 Blt_Tile tile; /* Not used. */
2145{
2146 Notebook *nbPtr = clientData;
2147
2148 if (nbPtr->tkwin != NULL) {
2149 EventuallyRedraw(nbPtr);
2150 }
2151}
2152
2153static int
2154ConfigureTab(nbPtr, tabPtr)
2155 Notebook *nbPtr;
2156 Tab *tabPtr;
2157{
2158 GC newGC;
2159 XGCValues gcValues;
2160 unsigned long gcMask;
2161 int labelWidth, labelHeight;
2162 Tk_Font font;
2163 Tk_3DBorder border;
2164
2165 font = GETATTR(tabPtr, font);
2166 labelWidth = labelHeight = 0;
2167 if (tabPtr->text != NULL) {
2168 TextStyle ts;
2169 double rotWidth, rotHeight;
2170
2171 Blt_InitTextStyle(&ts);
2172 ts.font = font;
2173 ts.shadow.offset = tabPtr->shadow.offset;
2174 ts.padX.side1 = ts.padX.side2 = 2;
2175 Blt_GetTextExtents(&ts, tabPtr->text, &labelWidth, &labelHeight);
2176 Blt_GetBoundingBox(labelWidth, labelHeight, nbPtr->defTabStyle.rotate,
2177 &rotWidth, &rotHeight, (Point2D *)NULL);
2178 labelWidth = ROUND(rotWidth);
2179 labelHeight = ROUND(rotHeight);
2180 }
2181 tabPtr->textWidth = (short int)labelWidth;
2182 tabPtr->textHeight = (short int)labelHeight;
2183 if (tabPtr->image != NULL) {
2184 int width, height;
2185
2186 width = ImageWidth(tabPtr->image) + 2 * IMAGE_PAD;
2187 height = ImageHeight(tabPtr->image) + 2 * IMAGE_PAD;
2188 if (nbPtr->defTabStyle.textSide & SIDE_VERTICAL) {
2189 labelWidth += width;
2190 labelHeight = MAX(labelHeight, height);
2191 } else {
2192 labelHeight += height;
2193 labelWidth = MAX(labelWidth, width);
2194 }
2195 }
2196 labelWidth += PADDING(tabPtr->iPadX);
2197 labelHeight += PADDING(tabPtr->iPadY);
2198
2199 tabPtr->labelWidth = ODD(labelWidth);
2200 tabPtr->labelHeight = ODD(labelHeight);
2201
2202 newGC = NULL;
2203 if (tabPtr->text != NULL) {
2204 XColor *colorPtr;
2205
2206 gcMask = GCForeground | GCFont;
2207 colorPtr = GETATTR(tabPtr, textColor);
2208 gcValues.foreground = colorPtr->pixel;
2209 gcValues.font = Tk_FontId(font);
2210 newGC = Tk_GetGC(nbPtr->tkwin, gcMask, &gcValues);
2211 }
2212 if (tabPtr->textGC != NULL) {
2213 Tk_FreeGC(nbPtr->display, tabPtr->textGC);
2214 }
2215 tabPtr->textGC = newGC;
2216
2217 gcMask = GCForeground | GCStipple | GCFillStyle;
2218 gcValues.fill_style = FillStippled;
2219 border = GETATTR(tabPtr, border);
2220 gcValues.foreground = Tk_3DBorderColor(border)->pixel;
2221 gcValues.stipple = tabPtr->stipple;
2222 newGC = Tk_GetGC(nbPtr->tkwin, gcMask, &gcValues);
2223 if (tabPtr->backGC != NULL) {
2224 Tk_FreeGC(nbPtr->display, tabPtr->backGC);
2225 }
2226 tabPtr->backGC = newGC;
2227 /*
2228 * GC for tiled background.
2229 */
2230 if (tabPtr->tile != NULL) {
2231 Blt_SetTileChangedProc(tabPtr->tile, TileChangedProc, nbPtr);
2232 }
2233 if (tabPtr->flags & TAB_VISIBLE) {
2234 EventuallyRedraw(nbPtr);
2235 }
2236 return TCL_OK;
2237}
2238
2239
2240/*
2241 * --------------------------------------------------------------
2242 *
2243 * TearoffEventProc --
2244 *
2245 * This procedure is invoked by the Tk dispatcher for various
2246 * events on the tearoff widget.
2247 *
2248 * Results:
2249 * None.
2250 *
2251 * Side effects:
2252 * When the tearoff gets deleted, internal structures get
2253 * cleaned up. When it gets resized or exposed, it's redisplayed.
2254 *
2255 * --------------------------------------------------------------
2256 */
2257static void
2258TearoffEventProc(clientData, eventPtr)
2259 ClientData clientData; /* Information about the tab window. */
2260 XEvent *eventPtr; /* Information about event. */
2261{
2262 Tab *tabPtr = clientData;
2263
2264 if ((tabPtr == NULL) || (tabPtr->tkwin == NULL) ||
2265 (tabPtr->container == NULL)) {
2266 return;
2267 }
2268 switch (eventPtr->type) {
2269 case Expose:
2270 if (eventPtr->xexpose.count == 0) {
2271 EventuallyRedrawTearoff(tabPtr);
2272 }
2273 break;
2274
2275 case ConfigureNotify:
2276 EventuallyRedrawTearoff(tabPtr);
2277 break;
2278
2279 case DestroyNotify:
2280 if (tabPtr->flags & TAB_REDRAW) {
2281 tabPtr->flags &= ~TAB_REDRAW;
2282 Tcl_CancelIdleCall(DisplayTearoff, clientData);
2283 }
2284 Tk_DestroyWindow(tabPtr->container);
2285 tabPtr->container = NULL;
2286 break;
2287
2288 }
2289}
2290
2291/*
2292 * ----------------------------------------------------------------------------
2293 *
2294 * GetReqWidth --
2295 *
2296 * Returns the width requested by the embedded tab window and
2297 * any requested padding around it. This represents the requested
2298 * width of the page.
2299 *
2300 * Results:
2301 * Returns the requested width of the page.
2302 *
2303 * ----------------------------------------------------------------------------
2304 */
2305static int
2306GetReqWidth(tabPtr)
2307 Tab *tabPtr;
2308{
2309 int width;
2310
2311 if (tabPtr->reqWidth > 0) {
2312 width = tabPtr->reqWidth;
2313 } else {
2314 width = Tk_ReqWidth(tabPtr->tkwin);
2315 }
2316 width += PADDING(tabPtr->padX) +
2317 2 * Tk_Changes(tabPtr->tkwin)->border_width;
2318 if (width < 1) {
2319 width = 1;
2320 }
2321 return width;
2322}
2323
2324/*
2325 * ----------------------------------------------------------------------------
2326 *
2327 * GetReqHeight --
2328 *
2329 * Returns the height requested by the window and padding around
2330 * the window. This represents the requested height of the page.
2331 *
2332 * Results:
2333 * Returns the requested height of the page.
2334 *
2335 * ----------------------------------------------------------------------------
2336 */
2337static int
2338GetReqHeight(tabPtr)
2339 Tab *tabPtr;
2340{
2341 int height;
2342
2343 if (tabPtr->reqHeight > 0) {
2344 height = tabPtr->reqHeight;
2345 } else {
2346 height = Tk_ReqHeight(tabPtr->tkwin);
2347 }
2348 height += PADDING(tabPtr->padY) +
2349 2 * Tk_Changes(tabPtr->tkwin)->border_width;
2350 if (height < 1) {
2351 height = 1;
2352 }
2353 return height;
2354}
2355
2356/*
2357 * ----------------------------------------------------------------------------
2358 *
2359 * TranslateAnchor --
2360 *
2361 * Translate the coordinates of a given bounding box based upon the
2362 * anchor specified. The anchor indicates where the given xy position
2363 * is in relation to the bounding box.
2364 *
2365 * nw --- n --- ne
2366 * | | x,y ---+
2367 * w center e | |
2368 * | | +-----+
2369 * sw --- s --- se
2370 *
2371 * Results:
2372 * The translated coordinates of the bounding box are returned.
2373 *
2374 * ----------------------------------------------------------------------------
2375 */
2376static void
2377TranslateAnchor(dx, dy, anchor, xPtr, yPtr)
2378 int dx, dy; /* Difference between outer and inner regions
2379 */
2380 Tk_Anchor anchor; /* Direction of the anchor */
2381 int *xPtr, *yPtr;
2382{
2383 int x, y;
2384
2385 x = y = 0;
2386 switch (anchor) {
2387 case TK_ANCHOR_NW: /* Upper left corner */
2388 break;
2389 case TK_ANCHOR_W: /* Left center */
2390 y = (dy / 2);
2391 break;
2392 case TK_ANCHOR_SW: /* Lower left corner */
2393 y = dy;
2394 break;
2395 case TK_ANCHOR_N: /* Top center */
2396 x = (dx / 2);
2397 break;
2398 case TK_ANCHOR_CENTER: /* Centered */
2399 x = (dx / 2);
2400 y = (dy / 2);
2401 break;
2402 case TK_ANCHOR_S: /* Bottom center */
2403 x = (dx / 2);
2404 y = dy;
2405 break;
2406 case TK_ANCHOR_NE: /* Upper right corner */
2407 x = dx;
2408 break;
2409 case TK_ANCHOR_E: /* Right center */
2410 x = dx;
2411 y = (dy / 2);
2412 break;
2413 case TK_ANCHOR_SE: /* Lower right corner */
2414 x = dx;
2415 y = dy;
2416 break;
2417 }
2418 *xPtr = (*xPtr) + x;
2419 *yPtr = (*yPtr) + y;
2420}
2421
2422
2423static void
2424GetWindowRectangle(tabPtr, parent, tearoff, rectPtr)
2425 Tab *tabPtr;
2426 Tk_Window parent;
2427 int tearoff;
2428 XRectangle *rectPtr;
2429{
2430 int pad;
2431 Notebook *nbPtr;
2432 int cavityWidth, cavityHeight;
2433 int width, height;
2434 int dx, dy;
2435 int x, y;
2436
2437 nbPtr = tabPtr->nbPtr;
2438 pad = nbPtr->inset + nbPtr->inset2;
2439
2440 if (!tearoff) {
2441 switch (nbPtr->side) {
2442 case SIDE_RIGHT:
2443 case SIDE_BOTTOM:
2444 x = nbPtr->inset + nbPtr->inset2;
2445 y = nbPtr->inset + nbPtr->inset2;
2446 break;
2447
2448 case SIDE_LEFT:
2449 x = nbPtr->pageTop;
2450 y = nbPtr->inset + nbPtr->inset2;
2451 break;
2452
2453 case SIDE_TOP:
2454 x = nbPtr->inset + nbPtr->inset2;
2455 y = nbPtr->pageTop;
2456 break;
2457 }
2458
2459 if (nbPtr->side & SIDE_VERTICAL) {
2460 cavityWidth = Tk_Width(nbPtr->tkwin) - (nbPtr->pageTop + pad);
2461 cavityHeight = Tk_Height(nbPtr->tkwin) - (2 * pad);
2462 } else {
2463 cavityWidth = Tk_Width(nbPtr->tkwin) - (2 * pad);
2464 cavityHeight = Tk_Height(nbPtr->tkwin) - (nbPtr->pageTop + pad);
2465 }
2466
2467 } else {
2468 x = nbPtr->inset + nbPtr->inset2;
2469#define TEAR_OFF_TAB_SIZE 5
2470 y = nbPtr->inset + nbPtr->inset2 + nbPtr->yPad + nbPtr->outerPad +
2471 TEAR_OFF_TAB_SIZE;
2472 cavityWidth = Tk_Width(parent) - (2 * pad);
2473 cavityHeight = Tk_Height(parent) - (y + pad);
2474 }
2475 cavityWidth -= PADDING(tabPtr->padX);
2476 cavityHeight -= PADDING(tabPtr->padY);
2477 if (cavityWidth < 1) {
2478 cavityWidth = 1;
2479 }
2480 if (cavityHeight < 1) {
2481 cavityHeight = 1;
2482 }
2483 width = GetReqWidth(tabPtr);
2484 height = GetReqHeight(tabPtr);
2485
2486 /*
2487 * Resize the embedded window is of the following is true:
2488 *
2489 * 1) It's been torn off.
2490 * 2) The -fill option (horizontal or vertical) is set.
2491 * 3) the window is bigger than the cavity.
2492 */
2493 if ((tearoff) || (cavityWidth < width) || (tabPtr->fill & FILL_X)) {
2494 width = cavityWidth;
2495 }
2496 if ((tearoff) || (cavityHeight < height) || (tabPtr->fill & FILL_Y)) {
2497 height = cavityHeight;
2498 }
2499 dx = (cavityWidth - width);
2500 dy = (cavityHeight - height);
2501 if ((dx > 0) || (dy > 0)) {
2502 TranslateAnchor(dx, dy, tabPtr->anchor, &x, &y);
2503 }
2504 /* Remember that X11 windows must be at least 1 pixel. */
2505 if (width < 1) {
2506 width = 1;
2507 }
2508 if (height < 1) {
2509 height = 1;
2510 }
2511 rectPtr->x = (short)(x + tabPtr->padLeft);
2512 rectPtr->y = (short)(y + tabPtr->padTop);
2513 rectPtr->width = (short)width;
2514 rectPtr->height = (short)height;
2515}
2516
2517static void
2518ArrangeWindow(tkwin, rectPtr, force)
2519 Tk_Window tkwin;
2520 XRectangle *rectPtr;
2521 int force;
2522{
2523 if ((force) ||
2524 (rectPtr->x != Tk_X(tkwin)) ||
2525 (rectPtr->y != Tk_Y(tkwin)) ||
2526 (rectPtr->width != Tk_Width(tkwin)) ||
2527 (rectPtr->height != Tk_Height(tkwin))) {
2528 Tk_MoveResizeWindow(tkwin, rectPtr->x, rectPtr->y,
2529 rectPtr->width, rectPtr->height);
2530 }
2531 if (!Tk_IsMapped(tkwin)) {
2532 Tk_MapWindow(tkwin);
2533 }
2534}
2535
2536
2537/*ARGSUSED*/
2538static void
2539GetTags(table, object, context, list)
2540 Blt_BindTable table;
2541 ClientData object;
2542 ClientData context;
2543 Blt_List list;
2544{
2545 Tab *tabPtr = (Tab *)object;
2546 Notebook *nbPtr;
2547
2548 nbPtr = (Notebook *)table->clientData;
2549 if (context == TAB_PERFORATION) {
2550 Blt_ListAppend(list, MakeTag(nbPtr, "Perforation"), 0);
2551 } else if (context == TAB_LABEL) {
2552 Blt_ListAppend(list, MakeTag(nbPtr, tabPtr->name), 0);
2553 if (tabPtr->tags != NULL) {
2554 int nNames;
2555 char **names;
2556 register char **p;
2557
2558 /*
2559 * This is a space/time trade-off in favor of space. The tags
2560 * are stored as character strings in a hash table. That way,
2561 * tabs can share the strings. It's likely that they will. The
2562 * down side is that the same string is split over and over again.
2563 */
2564 if (Tcl_SplitList((Tcl_Interp *)NULL, tabPtr->tags, &nNames,
2565 &names) == TCL_OK) {
2566 for (p = names; *p != NULL; p++) {
2567 Blt_ListAppend(list, MakeTag(nbPtr, *p), 0);
2568 }
2569 Blt_Free(names);
2570 }
2571 }
2572 }
2573}
2574
2575/*
2576 * --------------------------------------------------------------
2577 *
2578 * NotebookEventProc --
2579 *
2580 * This procedure is invoked by the Tk dispatcher for various
2581 * events on notebook widgets.
2582 *
2583 * Results:
2584 * None.
2585 *
2586 * Side Effects:
2587 * When the window gets deleted, internal structures get
2588 * cleaned up. When it gets exposed, it is redisplayed.
2589 *
2590 * --------------------------------------------------------------
2591 */
2592static void
2593NotebookEventProc(clientData, eventPtr)
2594 ClientData clientData; /* Information about window. */
2595 XEvent *eventPtr; /* Information about event. */
2596{
2597 Notebook *nbPtr = clientData;
2598
2599 switch (eventPtr->type) {
2600 case Expose:
2601 if (eventPtr->xexpose.count == 0) {
2602 EventuallyRedraw(nbPtr);
2603 }
2604 break;
2605
2606 case ConfigureNotify:
2607 nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
2608 EventuallyRedraw(nbPtr);
2609 break;
2610
2611 case FocusIn:
2612 case FocusOut:
2613 if (eventPtr->xfocus.detail != NotifyInferior) {
2614 if (eventPtr->type == FocusIn) {
2615 nbPtr->flags |= TNB_FOCUS;
2616 } else {
2617 nbPtr->flags &= ~TNB_FOCUS;
2618 }
2619 EventuallyRedraw(nbPtr);
2620 }
2621 break;
2622
2623 case DestroyNotify:
2624 if (nbPtr->tkwin != NULL) {
2625 nbPtr->tkwin = NULL;
2626 Tcl_DeleteCommandFromToken(nbPtr->interp, nbPtr->cmdToken);
2627 }
2628 if (nbPtr->flags & TNB_REDRAW) {
2629 Tcl_CancelIdleCall(DisplayNotebook, nbPtr);
2630 }
2631 Tcl_EventuallyFree(nbPtr, DestroyNotebook);
2632 break;
2633
2634 }
2635}
2636
2637/*
2638 * ----------------------------------------------------------------------
2639 *
2640 * DestroyNotebook --
2641 *
2642 * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
2643 * to clean up the internal structure of the widget at a safe
2644 * time (when no-one is using it anymore).
2645 *
2646 * Results:
2647 * None.
2648 *
2649 * Side Effects:
2650 * Everything associated with the widget is freed up.
2651 *
2652 * ----------------------------------------------------------------------
2653 */
2654static void
2655DestroyNotebook(dataPtr)
2656 DestroyData dataPtr; /* Pointer to the widget record. */
2657{
2658 Notebook *nbPtr = (Notebook *)dataPtr;
2659 Tab *tabPtr;
2660 Blt_ChainLink *linkPtr;
2661
2662 if (nbPtr->highlightGC != NULL) {
2663 Tk_FreeGC(nbPtr->display, nbPtr->highlightGC);
2664 }
2665 if (nbPtr->tile != NULL) {
2666 Blt_FreeTile(nbPtr->tile);
2667 }
2668 if (nbPtr->defTabStyle.activeGC != NULL) {
2669 Blt_FreePrivateGC(nbPtr->display, nbPtr->defTabStyle.activeGC);
2670 }
2671 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
2672 linkPtr = Blt_ChainNextLink(linkPtr)) {
2673 tabPtr = Blt_ChainGetValue(linkPtr);
2674 tabPtr->linkPtr = NULL;
2675 DestroyTab(nbPtr, tabPtr);
2676 }
2677 Blt_ChainDestroy(nbPtr->chainPtr);
2678 Blt_DestroyBindingTable(nbPtr->bindTable);
2679 Blt_DeleteHashTable(&(nbPtr->tabTable));
2680 Blt_DeleteHashTable(&(nbPtr->tagTable));
2681 Tk_FreeOptions(configSpecs, (char *)nbPtr, nbPtr->display, 0);
2682 Blt_Free(nbPtr);
2683}
2684
2685/*
2686 * ----------------------------------------------------------------------
2687 *
2688 * CreateNotebook --
2689 *
2690 * ----------------------------------------------------------------------
2691 */
2692static Notebook *
2693CreateNotebook(interp, tkwin)
2694 Tcl_Interp *interp;
2695 Tk_Window tkwin;
2696{
2697 Notebook *nbPtr;
2698
2699 nbPtr = Blt_Calloc(1, sizeof(Notebook));
2700 assert(nbPtr);
2701
2702 Tk_SetClass(tkwin, "Tabnotebook");
2703 nbPtr->tkwin = tkwin;
2704 nbPtr->display = Tk_Display(tkwin);
2705 nbPtr->interp = interp;
2706
2707 nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
2708 nbPtr->side = SIDE_TOP;
2709 nbPtr->borderWidth = nbPtr->highlightWidth = 2;
2710 nbPtr->ySelectPad = SELECT_PADY;
2711 nbPtr->xSelectPad = SELECT_PADX;
2712 nbPtr->relief = TK_RELIEF_SUNKEN;
2713 nbPtr->defTabStyle.relief = TK_RELIEF_RAISED;
2714 nbPtr->defTabStyle.borderWidth = 1;
2715 nbPtr->defTabStyle.constWidth = TRUE;
2716 nbPtr->defTabStyle.textSide = SIDE_LEFT;
2717 nbPtr->scrollUnits = 2;
2718 nbPtr->corner = CORNER_OFFSET;
2719 nbPtr->gap = GAP;
2720 nbPtr->outerPad = OUTER_PAD;
2721 nbPtr->slant = SLANT_NONE;
2722 nbPtr->overlap = 0;
2723 nbPtr->tearoff = TRUE;
2724 nbPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, nbPtr, PickTab,
2725 GetTags);
2726 nbPtr->chainPtr = Blt_ChainCreate();
2727 Blt_InitHashTable(&(nbPtr->tabTable), BLT_STRING_KEYS);
2728 Blt_InitHashTable(&(nbPtr->imageTable), BLT_STRING_KEYS);
2729 Blt_InitHashTable(&(nbPtr->tagTable), BLT_STRING_KEYS);
2730#if (TK_MAJOR_VERSION > 4)
2731 Blt_SetWindowInstanceData(tkwin, nbPtr);
2732#endif
2733 return nbPtr;
2734}
2735
2736/*
2737 * ----------------------------------------------------------------------
2738 *
2739 * ConfigureNotebook --
2740 *
2741 * This procedure is called to process an argv/argc list, plus
2742 * the Tk option database, in order to configure (or reconfigure)
2743 * the widget.
2744 *
2745 * Results:
2746 * The return value is a standard Tcl result. If TCL_ERROR is
2747 * returned, then interp->result contains an error message.
2748 *
2749 * Side Effects:
2750 * Configuration information, such as text string, colors, font,
2751 * etc. get set for nbPtr; old resources get freed, if there
2752 * were any. The widget is redisplayed.
2753 *
2754 * ----------------------------------------------------------------------
2755 */
2756static int
2757ConfigureNotebook(interp, nbPtr, argc, argv, flags)
2758 Tcl_Interp *interp; /* Interpreter to report errors. */
2759 Notebook *nbPtr; /* Information about widget; may or
2760 * may not already have values for
2761 * some fields. */
2762 int argc;
2763 char **argv;
2764 int flags;
2765{
2766 XGCValues gcValues;
2767 unsigned long gcMask;
2768 GC newGC;
2769
2770 lastNotebookInstance = nbPtr;
2771 if (Tk_ConfigureWidget(interp, nbPtr->tkwin, configSpecs, argc, argv,
2772 (char *)nbPtr, flags) != TCL_OK) {
2773 return TCL_ERROR;
2774 }
2775 if (Blt_ConfigModified(configSpecs, "-width", "-height", "-side", "-gap",
2776 "-slant", (char *)NULL)) {
2777 nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
2778 }
2779 if ((nbPtr->reqHeight > 0) && (nbPtr->reqWidth > 0)) {
2780 Tk_GeometryRequest(nbPtr->tkwin, nbPtr->reqWidth,
2781 nbPtr->reqHeight);
2782 }
2783 /*
2784 * GC for focus highlight.
2785 */
2786 gcMask = GCForeground;
2787 gcValues.foreground = nbPtr->highlightColor->pixel;
2788 newGC = Tk_GetGC(nbPtr->tkwin, gcMask, &gcValues);
2789 if (nbPtr->highlightGC != NULL) {
2790 Tk_FreeGC(nbPtr->display, nbPtr->highlightGC);
2791 }
2792 nbPtr->highlightGC = newGC;
2793
2794 /*
2795 * GC for tiled background.
2796 */
2797 if (nbPtr->tile != NULL) {
2798 Blt_SetTileChangedProc(nbPtr->tile, TileChangedProc, nbPtr);
2799 }
2800 /*
2801 * GC for active line.
2802 */
2803 gcMask = GCForeground | GCLineWidth | GCLineStyle | GCCapStyle;
2804 gcValues.foreground = nbPtr->defTabStyle.activeFgColor->pixel;
2805 gcValues.line_width = 0;
2806 gcValues.cap_style = CapProjecting;
2807 gcValues.line_style = (LineIsDashed(nbPtr->defTabStyle.dashes))
2808 ? LineOnOffDash : LineSolid;
2809
2810 newGC = Blt_GetPrivateGC(nbPtr->tkwin, gcMask, &gcValues);
2811 if (LineIsDashed(nbPtr->defTabStyle.dashes)) {
2812 nbPtr->defTabStyle.dashes.offset = 2;
2813 Blt_SetDashes(nbPtr->display, newGC, &(nbPtr->defTabStyle.dashes));
2814 }
2815 if (nbPtr->defTabStyle.activeGC != NULL) {
2816 Blt_FreePrivateGC(nbPtr->display,
2817 nbPtr->defTabStyle.activeGC);
2818 }
2819 nbPtr->defTabStyle.activeGC = newGC;
2820
2821 nbPtr->defTabStyle.rotate = FMOD(nbPtr->defTabStyle.rotate, 360.0);
2822 if (nbPtr->defTabStyle.rotate < 0.0) {
2823 nbPtr->defTabStyle.rotate += 360.0;
2824 }
2825 nbPtr->inset = nbPtr->highlightWidth + nbPtr->borderWidth + nbPtr->outerPad;
2826 if (Blt_ConfigModified(configSpecs, "-font", "-*foreground", "-rotate",
2827 "-*background", "-side", (char *)NULL)) {
2828 Blt_ChainLink *linkPtr;
2829 Tab *tabPtr;
2830
2831 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr);
2832 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
2833 tabPtr = Blt_ChainGetValue(linkPtr);
2834 ConfigureTab(nbPtr, tabPtr);
2835 }
2836 nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
2837 }
2838 nbPtr->inset2 = nbPtr->defTabStyle.borderWidth + nbPtr->corner;
2839 EventuallyRedraw(nbPtr);
2840 return TCL_OK;
2841}
2842
2843/*
2844 * --------------------------------------------------------------
2845 *
2846 * Notebook operations
2847 *
2848 * --------------------------------------------------------------
2849 */
2850/*
2851 *----------------------------------------------------------------------
2852 *
2853 * ActivateOp --
2854 *
2855 * Selects the tab to appear active.
2856 *
2857 *----------------------------------------------------------------------
2858 */
2859/*ARGSUSED*/
2860static int
2861ActivateOp(nbPtr, interp, argc, argv)
2862 Notebook *nbPtr;
2863 Tcl_Interp *interp;
2864 int argc; /* Not used. */
2865 char **argv;
2866{
2867 Tab *tabPtr;
2868
2869 if (argv[2][0] == '\0') {
2870 tabPtr = NULL;
2871 } else if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
2872 return TCL_ERROR;
2873 }
2874 if ((tabPtr != NULL) && (tabPtr->state == STATE_DISABLED)) {
2875 tabPtr = NULL;
2876 }
2877 if (tabPtr != nbPtr->activePtr) {
2878 nbPtr->activePtr = tabPtr;
2879 EventuallyRedraw(nbPtr);
2880 }
2881 return TCL_OK;
2882}
2883
2884/*
2885 *----------------------------------------------------------------------
2886 *
2887 * BindOp --
2888 *
2889 * .t bind index sequence command
2890 *
2891 *----------------------------------------------------------------------
2892 */
2893/*ARGSUSED*/
2894static int
2895BindOp(nbPtr, interp, argc, argv)
2896 Notebook *nbPtr;
2897 Tcl_Interp *interp;
2898 int argc; /* Not used. */
2899 char **argv;
2900{
2901 if (argc == 2) {
2902 Blt_HashEntry *hPtr;
2903 Blt_HashSearch cursor;
2904 char *tagName;
2905
2906 for (hPtr = Blt_FirstHashEntry(&(nbPtr->tagTable), &cursor);
2907 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
2908 tagName = Blt_GetHashKey(&(nbPtr->tagTable), hPtr);
2909 Tcl_AppendElement(interp, tagName);
2910 }
2911 return TCL_OK;
2912 }
2913 return Blt_ConfigureBindings(interp, nbPtr->bindTable,
2914 MakeTag(nbPtr, argv[2]), argc - 3, argv + 3);
2915}
2916
2917/*
2918 *----------------------------------------------------------------------
2919 *
2920 * CgetOp --
2921 *
2922 *----------------------------------------------------------------------
2923 */
2924/*ARGSUSED*/
2925static int
2926CgetOp(nbPtr, interp, argc, argv)
2927 Notebook *nbPtr;
2928 Tcl_Interp *interp;
2929 int argc; /* Not used. */
2930 char **argv;
2931{
2932 lastNotebookInstance = nbPtr;
2933 return Tk_ConfigureValue(interp, nbPtr->tkwin, configSpecs,
2934 (char *)nbPtr, argv[2], 0);
2935}
2936
2937/*
2938 *----------------------------------------------------------------------
2939 *
2940 * ConfigureOp --
2941 *
2942 * This procedure is called to process an argv/argc list, plus
2943 * the Tk option database, in order to configure (or reconfigure)
2944 * the widget.
2945 *
2946 * Results:
2947 * A standard Tcl result. If TCL_ERROR is returned, then
2948 * interp->result contains an error message.
2949 *
2950 * Side Effects:
2951 * Configuration information, such as text string, colors, font,
2952 * etc. get set for nbPtr; old resources get freed, if there
2953 * were any. The widget is redisplayed.
2954 *
2955 *----------------------------------------------------------------------
2956 */
2957static int
2958ConfigureOp(nbPtr, interp, argc, argv)
2959 Notebook *nbPtr;
2960 Tcl_Interp *interp;
2961 int argc;
2962 char **argv;
2963{
2964
2965 lastNotebookInstance = nbPtr;
2966 if (argc == 2) {
2967 return Tk_ConfigureInfo(interp, nbPtr->tkwin, configSpecs,
2968 (char *)nbPtr, (char *)NULL, 0);
2969 } else if (argc == 3) {
2970 return Tk_ConfigureInfo(interp, nbPtr->tkwin, configSpecs,
2971 (char *)nbPtr, argv[2], 0);
2972 }
2973 if (ConfigureNotebook(interp, nbPtr, argc - 2, argv + 2,
2974 TK_CONFIG_ARGV_ONLY) != TCL_OK) {
2975 return TCL_ERROR;
2976 }
2977 EventuallyRedraw(nbPtr);
2978 return TCL_OK;
2979}
2980
2981/*
2982 *----------------------------------------------------------------------
2983 *
2984 * DeleteOp --
2985 *
2986 * Deletes tab from the set. Deletes either a range of
2987 * tabs or a single node.
2988 *
2989 *----------------------------------------------------------------------
2990 */
2991/*ARGSUSED*/
2992static int
2993DeleteOp(nbPtr, interp, argc, argv)
2994 Notebook *nbPtr;
2995 Tcl_Interp *interp;
2996 int argc; /* Not used. */
2997 char **argv;
2998{
2999 Tab *firstPtr, *lastPtr;
3000
3001 lastPtr = NULL;
3002 if (GetTab(nbPtr, argv[2], &firstPtr, INVALID_FAIL) != TCL_OK) {
3003 return TCL_ERROR;
3004 }
3005 if ((argc == 4) &&
3006 (GetTab(nbPtr, argv[3], &lastPtr, INVALID_FAIL) != TCL_OK)) {
3007 return TCL_ERROR;
3008 }
3009 if (lastPtr == NULL) {
3010 DestroyTab(nbPtr, firstPtr);
3011 } else {
3012 Tab *tabPtr;
3013 Blt_ChainLink *linkPtr, *nextLinkPtr;
3014
3015 tabPtr = NULL; /* Suppress compiler warning. */
3016
3017 /* Make sure that the first tab is before the last. */
3018 for (linkPtr = firstPtr->linkPtr; linkPtr != NULL;
3019 linkPtr = Blt_ChainNextLink(linkPtr)) {
3020 tabPtr = Blt_ChainGetValue(linkPtr);
3021 if (tabPtr == lastPtr) {
3022 break;
3023 }
3024 }
3025 if (tabPtr != lastPtr) {
3026 return TCL_OK;
3027 }
3028 linkPtr = firstPtr->linkPtr;
3029 while (linkPtr != NULL) {
3030 nextLinkPtr = Blt_ChainNextLink(linkPtr);
3031 tabPtr = Blt_ChainGetValue(linkPtr);
3032 DestroyTab(nbPtr, tabPtr);
3033 linkPtr = nextLinkPtr;
3034 if (tabPtr == lastPtr) {
3035 break;
3036 }
3037 }
3038 }
3039 nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
3040 EventuallyRedraw(nbPtr);
3041 return TCL_OK;
3042}
3043
3044/*
3045 *----------------------------------------------------------------------
3046 *
3047 * FocusOp --
3048 *
3049 * Selects the tab to get focus.
3050 *
3051 *----------------------------------------------------------------------
3052 */
3053/*ARGSUSED*/
3054static int
3055FocusOp(nbPtr, interp, argc, argv)
3056 Notebook *nbPtr;
3057 Tcl_Interp *interp;
3058 int argc; /* Not used. */
3059 char **argv;
3060{
3061 Tab *tabPtr;
3062
3063 if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_FAIL) != TCL_OK) {
3064 return TCL_ERROR;
3065 }
3066 if (tabPtr != NULL) {
3067 nbPtr->focusPtr = tabPtr;
3068 Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr, NULL);
3069 EventuallyRedraw(nbPtr);
3070 }
3071 return TCL_OK;
3072}
3073
3074/*
3075 *----------------------------------------------------------------------
3076 *
3077 * IndexOp --
3078 *
3079 * Converts a string representing a tab index.
3080 *
3081 * Results:
3082 * A standard Tcl result. Interp->result will contain the
3083 * identifier of each index found. If an index could not be found,
3084 * then the serial identifier will be the empty string.
3085 *
3086 *----------------------------------------------------------------------
3087 */
3088/*ARGSUSED*/
3089static int
3090IndexOp(nbPtr, interp, argc, argv)
3091 Notebook *nbPtr;
3092 Tcl_Interp *interp;
3093 int argc;
3094 char **argv;
3095{
3096 Tab *tabPtr;
3097
3098 if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
3099 return TCL_ERROR;
3100 }
3101 if (tabPtr != NULL) {
3102 Tcl_SetResult(interp, Blt_Itoa(TabIndex(nbPtr, tabPtr)),
3103 TCL_VOLATILE);
3104 }
3105 return TCL_OK;
3106}
3107
3108/*
3109 *----------------------------------------------------------------------
3110 *
3111 * IdOp --
3112 *
3113 * Converts a tab index into the tab identifier.
3114 *
3115 * Results:
3116 * A standard Tcl result. Interp->result will contain the
3117 * identifier of each index found. If an index could not be found,
3118 * then the serial identifier will be the empty string.
3119 *
3120 *----------------------------------------------------------------------
3121 */
3122/*ARGSUSED*/
3123static int
3124IdOp(nbPtr, interp, argc, argv)
3125 Notebook *nbPtr;
3126 Tcl_Interp *interp;
3127 int argc; /* Not used. */
3128 char **argv;
3129{
3130 Tab *tabPtr;
3131
3132 if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
3133 return TCL_ERROR;
3134 }
3135 if (tabPtr == NULL) {
3136 Tcl_SetResult(interp, "", TCL_STATIC);
3137 } else {
3138 Tcl_SetResult(interp, tabPtr->name, TCL_VOLATILE);
3139 }
3140 return TCL_OK;
3141}
3142
3143/*
3144 *----------------------------------------------------------------------
3145 *
3146 * InsertOp --
3147 *
3148 * Add new entries into a tab set.
3149 *
3150 * .t insert end label option-value label option-value...
3151 *
3152 *----------------------------------------------------------------------
3153 */
3154/*ARGSUSED*/
3155static int
3156InsertOp(nbPtr, interp, argc, argv)
3157 Notebook *nbPtr;
3158 Tcl_Interp *interp;
3159 int argc; /* Not used. */
3160 char **argv;
3161{
3162 Tab *tabPtr;
3163 Blt_ChainLink *linkPtr, *beforeLinkPtr;
3164 char c;
3165
3166 c = argv[2][0];
3167 if ((c == 'e') && (strcmp(argv[2], "end") == 0)) {
3168 beforeLinkPtr = NULL;
3169 } else if (isdigit(UCHAR(c))) {
3170 int position;
3171
3172 if (Tcl_GetInt(interp, argv[2], &position) != TCL_OK) {
3173 return TCL_ERROR;
3174 }
3175 if (position < 0) {
3176 beforeLinkPtr = Blt_ChainFirstLink(nbPtr->chainPtr);
3177 } else if (position > Blt_ChainGetLength(nbPtr->chainPtr)) {
3178 beforeLinkPtr = NULL;
3179 } else {
3180 beforeLinkPtr = Blt_ChainGetNthLink(nbPtr->chainPtr, position);
3181 }
3182 } else {
3183 Tab *beforePtr;
3184
3185 if (GetTab(nbPtr, argv[2], &beforePtr, INVALID_FAIL) != TCL_OK) {
3186 return TCL_ERROR;
3187 }
3188 beforeLinkPtr = beforePtr->linkPtr;
3189 }
3190 nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
3191 EventuallyRedraw(nbPtr);
3192 tabPtr = CreateTab(nbPtr);
3193 if (tabPtr == NULL) {
3194 return TCL_ERROR;
3195 }
3196 lastNotebookInstance = nbPtr;
3197 if (Blt_ConfigureWidgetComponent(interp, nbPtr->tkwin, tabPtr->name,
3198 "Tab", tabConfigSpecs, argc - 3, argv + 3, (char *)tabPtr, 0)
3199 != TCL_OK) {
3200 DestroyTab(nbPtr, tabPtr);
3201 return TCL_ERROR;
3202 }
3203 if (ConfigureTab(nbPtr, tabPtr) != TCL_OK) {
3204 DestroyTab(nbPtr, tabPtr);
3205 return TCL_ERROR;
3206 }
3207 linkPtr = Blt_ChainNewLink();
3208 if (beforeLinkPtr == NULL) {
3209 Blt_ChainAppendLink(nbPtr->chainPtr, linkPtr);
3210 } else {
3211 Blt_ChainLinkBefore(nbPtr->chainPtr, linkPtr, beforeLinkPtr);
3212 }
3213 tabPtr->linkPtr = linkPtr;
3214 Blt_ChainSetValue(linkPtr, tabPtr);
3215 Tcl_SetResult(interp, tabPtr->name, TCL_VOLATILE);
3216 return TCL_OK;
3217
3218}
3219
3220/*
3221 * Preprocess the command string for percent substitution.
3222 */
3223static void
3224PercentSubst(nbPtr, tabPtr, command, resultPtr)
3225 Notebook *nbPtr;
3226 Tab *tabPtr;
3227 char *command;
3228 Tcl_DString *resultPtr;
3229{
3230 register char *last, *p;
3231 /*
3232 * Get the full path name of the node, in case we need to
3233 * substitute for it.
3234 */
3235 Tcl_DStringInit(resultPtr);
3236 for (last = p = command; *p != '\0'; p++) {
3237 if (*p == '%') {
3238 char *string;
3239 char buf[3];
3240
3241 if (p > last) {
3242 *p = '\0';
3243 Tcl_DStringAppend(resultPtr, last, -1);
3244 *p = '%';
3245 }
3246 switch (*(p + 1)) {
3247 case '%': /* Percent sign */
3248 string = "%";
3249 break;
3250 case 'W': /* Widget name */
3251 string = Tk_PathName(nbPtr->tkwin);
3252 break;
3253 case 'i': /* Tab Index */
3254 string = Blt_Itoa(TabIndex(nbPtr, tabPtr));
3255 break;
3256 case 'n': /* Tab name */
3257 string = tabPtr->name;
3258 break;
3259 default:
3260 if (*(p + 1) == '\0') {
3261 p--;
3262 }
3263 buf[0] = *p, buf[1] = *(p + 1), buf[2] = '\0';
3264 string = buf;
3265 break;
3266 }
3267 Tcl_DStringAppend(resultPtr, string, -1);
3268 p++;
3269 last = p + 1;
3270 }
3271 }
3272 if (p > last) {
3273 *p = '\0';
3274 Tcl_DStringAppend(resultPtr, last, -1);
3275 }
3276}
3277
3278/*
3279 *----------------------------------------------------------------------
3280 *
3281 * InvokeOp --
3282 *
3283 * This procedure is called to invoke a selection command.
3284 *
3285 * .h invoke index
3286 *
3287 * Results:
3288 * A standard Tcl result. If TCL_ERROR is returned, then
3289 * interp->result contains an error message.
3290 *
3291 * Side Effects:
3292 * Configuration information, such as text string, colors, font,
3293 * etc. get set; old resources get freed, if there were any.
3294 * The widget is redisplayed if needed.
3295 *
3296 *----------------------------------------------------------------------
3297 */
3298/*ARGSUSED*/
3299static int
3300InvokeOp(nbPtr, interp, argc, argv)
3301 Notebook *nbPtr;
3302 Tcl_Interp *interp; /* Not used. */
3303 int argc;
3304 char **argv;
3305{
3306 Tab *tabPtr;
3307 char *command;
3308
3309 if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
3310 return TCL_ERROR;
3311 }
3312 if ((tabPtr == NULL) || (tabPtr->state == STATE_DISABLED)) {
3313 return TCL_OK;
3314 }
3315 Tcl_Preserve(tabPtr);
3316 command = GETATTR(tabPtr, command);
3317 if (command != NULL) {
3318 Tcl_DString dString;
3319 int result;
3320
3321 PercentSubst(nbPtr, tabPtr, command, &dString);
3322 result = Tcl_GlobalEval(nbPtr->interp,
3323 Tcl_DStringValue(&dString));
3324 Tcl_DStringFree(&dString);
3325 if (result != TCL_OK) {
3326 return TCL_ERROR;
3327 }
3328 }
3329 Tcl_Release(tabPtr);
3330 return TCL_OK;
3331}
3332
3333/*
3334 *----------------------------------------------------------------------
3335 *
3336 * MoveOp --
3337 *
3338 * Moves a tab to a new location.
3339 *
3340 *----------------------------------------------------------------------
3341 */
3342/*ARGSUSED*/
3343static int
3344MoveOp(nbPtr, interp, argc, argv)
3345 Notebook *nbPtr;
3346 Tcl_Interp *interp;
3347 int argc; /* Not used. */
3348 char **argv;
3349{
3350 Tab *tabPtr, *linkPtr;
3351 int before;
3352
3353 if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
3354 return TCL_ERROR;
3355 }
3356 if ((tabPtr == NULL) || (tabPtr->state == STATE_DISABLED)) {
3357 return TCL_OK;
3358 }
3359 if ((argv[3][0] == 'b') && (strcmp(argv[3], "before") == 0)) {
3360 before = 1;
3361 } else if ((argv[3][0] == 'a') && (strcmp(argv[3], "after") == 0)) {
3362 before = 0;
3363 } else {
3364 Tcl_AppendResult(interp, "bad key word \"", argv[3],
3365 "\": should be \"after\" or \"before\"", (char *)NULL);
3366 return TCL_ERROR;
3367 }
3368 if (GetTab(nbPtr, argv[4], &linkPtr, INVALID_FAIL) != TCL_OK) {
3369 return TCL_ERROR;
3370 }
3371 if (tabPtr == linkPtr) {
3372 return TCL_OK;
3373 }
3374 Blt_ChainUnlinkLink(nbPtr->chainPtr, tabPtr->linkPtr);
3375 if (before) {
3376 Blt_ChainLinkBefore(nbPtr->chainPtr, tabPtr->linkPtr,
3377 linkPtr->linkPtr);
3378 } else {
3379 Blt_ChainLinkAfter(nbPtr->chainPtr, tabPtr->linkPtr,
3380 linkPtr->linkPtr);
3381 }
3382 nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
3383 EventuallyRedraw(nbPtr);
3384 return TCL_OK;
3385}
3386
3387/*ARGSUSED*/
3388static int
3389NearestOp(nbPtr, interp, argc, argv)
3390 Notebook *nbPtr;
3391 Tcl_Interp *interp;
3392 int argc; /* Not used. */
3393 char **argv;
3394{
3395 int x, y; /* Screen coordinates of the test point. */
3396 Tab *tabPtr;
3397
3398 if ((Tk_GetPixels(interp, nbPtr->tkwin, argv[2], &x) != TCL_OK) ||
3399 (Tk_GetPixels(interp, nbPtr->tkwin, argv[3], &y) != TCL_OK)) {
3400 return TCL_ERROR;
3401 }
3402 if (nbPtr->nVisible > 0) {
3403 tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL);
3404 if (tabPtr != NULL) {
3405 Tcl_SetResult(interp, tabPtr->name, TCL_VOLATILE);
3406 }
3407 }
3408 return TCL_OK;
3409}
3410
3411/*
3412 *----------------------------------------------------------------------
3413 *
3414 * SelectOp --
3415 *
3416 * This procedure is called to selecta tab.
3417 *
3418 * .h select index
3419 *
3420 * Results:
3421 * A standard Tcl result. If TCL_ERROR is returned, then
3422 * interp->result contains an error message.
3423 *
3424 * Side Effects:
3425 * Configuration information, such as text string, colors, font,
3426 * etc. get set; old resources get freed, if there were any.
3427 * The widget is redisplayed if needed.
3428 *
3429 *----------------------------------------------------------------------
3430 */
3431/*ARGSUSED*/
3432static int
3433SelectOp(nbPtr, interp, argc, argv)
3434 Notebook *nbPtr;
3435 Tcl_Interp *interp; /* Not used. */
3436 int argc;
3437 char **argv;
3438{
3439 Tab *tabPtr;
3440
3441 if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
3442 return TCL_ERROR;
3443 }
3444 if ((tabPtr == NULL) || (tabPtr->state == STATE_DISABLED)) {
3445 return TCL_OK;
3446 }
3447 if ((nbPtr->selectPtr != NULL) && (nbPtr->selectPtr != tabPtr) &&
3448 (nbPtr->selectPtr->tkwin != NULL)) {
3449 if (nbPtr->selectPtr->container == NULL) {
3450 if (Tk_IsMapped(nbPtr->selectPtr->tkwin)) {
3451 Tk_UnmapWindow(nbPtr->selectPtr->tkwin);
3452 }
3453 } else {
3454 /* Redraw now unselected container. */
3455 EventuallyRedrawTearoff(nbPtr->selectPtr);
3456 }
3457 }
3458 nbPtr->selectPtr = tabPtr;
3459 if ((nbPtr->nTiers > 1) && (tabPtr->tier != nbPtr->startPtr->tier)) {
3460 RenumberTiers(nbPtr, tabPtr);
3461 Blt_PickCurrentItem(nbPtr->bindTable);
3462 }
3463 nbPtr->flags |= (TNB_SCROLL);
3464 if (tabPtr->container != NULL) {
3465 EventuallyRedrawTearoff(tabPtr);
3466 }
3467 EventuallyRedraw(nbPtr);
3468 return TCL_OK;
3469}
3470
3471static int
3472ViewOp(nbPtr, interp, argc, argv)
3473 Notebook *nbPtr;
3474 Tcl_Interp *interp;
3475 int argc;
3476 char **argv;
3477{
3478 int width;
3479
3480 width = VPORTWIDTH(nbPtr);
3481 if (argc == 2) {
3482 double fract;
3483
3484 /*
3485 * Note: we are bounding the fractions between 0.0 and 1.0 to
3486 * support the "canvas"-style of scrolling.
3487 */
3488
3489 fract = (double)nbPtr->scrollOffset / nbPtr->worldWidth;
3490 Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
3491 fract = (double)(nbPtr->scrollOffset + width) / nbPtr->worldWidth;
3492 Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
3493 return TCL_OK;
3494 }
3495 if (Blt_GetScrollInfo(interp, argc - 2, argv + 2, &(nbPtr->scrollOffset),
3496 nbPtr->worldWidth, width, nbPtr->scrollUnits,
3497 BLT_SCROLL_MODE_CANVAS) != TCL_OK) {
3498 return TCL_ERROR;
3499 }
3500 nbPtr->flags |= TNB_SCROLL;
3501 EventuallyRedraw(nbPtr);
3502 return TCL_OK;
3503}
3504
3505
3506static void
3507AdoptWindow(clientData)
3508 ClientData clientData;
3509{
3510 Tab *tabPtr = clientData;
3511 int x, y;
3512 Notebook *nbPtr = tabPtr->nbPtr;
3513
3514 x = nbPtr->inset + nbPtr->inset2 + tabPtr->padLeft;
3515#define TEAR_OFF_TAB_SIZE 5
3516 y = nbPtr->inset + nbPtr->inset2 + nbPtr->yPad +
3517 nbPtr->outerPad + TEAR_OFF_TAB_SIZE + tabPtr->padTop;
3518 Blt_RelinkWindow(tabPtr->tkwin, tabPtr->container, x, y);
3519 Tk_MapWindow(tabPtr->tkwin);
3520}
3521
3522static void
3523DestroyTearoff(dataPtr)
3524 DestroyData dataPtr;
3525{
3526 Tab *tabPtr = (Tab *)dataPtr;
3527
3528 if (tabPtr->container != NULL) {
3529 Notebook *nbPtr;
3530 Tk_Window tkwin;
3531 nbPtr = tabPtr->nbPtr;
3532
3533 tkwin = tabPtr->container;
3534 if (tabPtr->flags & TAB_REDRAW) {
3535 Tcl_CancelIdleCall(DisplayTearoff, tabPtr);
3536 }
3537 Tk_DeleteEventHandler(tkwin, StructureNotifyMask, TearoffEventProc,
3538 tabPtr);
3539 if (tabPtr->tkwin != NULL) {
3540 XRectangle rect;
3541
3542 GetWindowRectangle(tabPtr, nbPtr->tkwin, FALSE, &rect);
3543 Blt_RelinkWindow(tabPtr->tkwin, nbPtr->tkwin, rect.x, rect.y);
3544 if (tabPtr == nbPtr->selectPtr) {
3545 ArrangeWindow(tabPtr->tkwin, &rect, TRUE);
3546 } else {
3547 Tk_UnmapWindow(tabPtr->tkwin);
3548 }
3549 }
3550 Tk_DestroyWindow(tkwin);
3551 tabPtr->container = NULL;
3552 }
3553}
3554
3555static int
3556CreateTearoff(nbPtr, name, tabPtr)
3557 Notebook *nbPtr;
3558 char *name;
3559 Tab *tabPtr;
3560{
3561 Tk_Window tkwin;
3562 int width, height;
3563
3564 tkwin = Tk_CreateWindowFromPath(nbPtr->interp, nbPtr->tkwin, name,
3565 (char *)NULL);
3566 if (tkwin == NULL) {
3567 return TCL_ERROR;
3568 }
3569 tabPtr->container = tkwin;
3570 if (Tk_WindowId(tkwin) == None) {
3571 Tk_MakeWindowExist(tkwin);
3572 }
3573 Tk_SetClass(tkwin, "Tearoff");
3574 Tk_CreateEventHandler(tkwin, (ExposureMask | StructureNotifyMask),
3575 TearoffEventProc, tabPtr);
3576 if (Tk_WindowId(tabPtr->tkwin) == None) {
3577 Tk_MakeWindowExist(tabPtr->tkwin);
3578 }
3579 width = Tk_Width(tabPtr->tkwin);
3580 if (width < 2) {
3581 width = (tabPtr->reqWidth > 0)
3582 ? tabPtr->reqWidth : Tk_ReqWidth(tabPtr->tkwin);
3583 }
3584 width += PADDING(tabPtr->padX) + 2 *
3585 Tk_Changes(tabPtr->tkwin)->border_width;
3586 width += 2 * (nbPtr->inset2 + nbPtr->inset);
3587#define TEAR_OFF_TAB_SIZE 5
3588 height = Tk_Height(tabPtr->tkwin);
3589 if (height < 2) {
3590 height = (tabPtr->reqHeight > 0)
3591 ? tabPtr->reqHeight : Tk_ReqHeight(tabPtr->tkwin);
3592 }
3593 height += PADDING(tabPtr->padY) +
3594 2 * Tk_Changes(tabPtr->tkwin)->border_width;
3595 height += nbPtr->inset + nbPtr->inset2 + nbPtr->yPad +
3596 TEAR_OFF_TAB_SIZE + nbPtr->outerPad;
3597 Tk_GeometryRequest(tkwin, width, height);
3598 Tk_UnmapWindow(tabPtr->tkwin);
3599 /* Tk_MoveWindow(tabPtr->tkwin, 0, 0); */
3600 Tcl_SetResult(nbPtr->interp, Tk_PathName(tkwin), TCL_VOLATILE);
3601#ifdef WIN32
3602 AdoptWindow(tabPtr);
3603#else
3604 Tcl_DoWhenIdle(AdoptWindow, tabPtr);
3605#endif
3606 return TCL_OK;
3607}
3608
3609/*
3610 *----------------------------------------------------------------------
3611 *
3612 * TabCgetOp --
3613 *
3614 * .h tab cget index option
3615 *
3616 *----------------------------------------------------------------------
3617 */
3618/*ARGSUSED*/
3619static int
3620TabCgetOp(nbPtr, interp, argc, argv)
3621 Notebook *nbPtr;
3622 Tcl_Interp *interp;
3623 int argc; /* Not used. */
3624 char **argv;
3625{
3626 Tab *tabPtr;
3627
3628 if (GetTab(nbPtr, argv[3], &tabPtr, INVALID_FAIL) != TCL_OK) {
3629 return TCL_ERROR;
3630 }
3631 lastNotebookInstance = nbPtr;
3632 return Tk_ConfigureValue(interp, nbPtr->tkwin, tabConfigSpecs,
3633 (char *)tabPtr, argv[4], 0);
3634}
3635
3636/*
3637 *----------------------------------------------------------------------
3638 *
3639 * TabConfigureOp --
3640 *
3641 * This procedure is called to process a list of configuration
3642 * options database, in order to reconfigure the options for
3643 * one or more tabs in the widget.
3644 *
3645 * .h tab configure index ?index...? ?option value?...
3646 *
3647 * Results:
3648 * A standard Tcl result. If TCL_ERROR is returned, then
3649 * interp->result contains an error message.
3650 *
3651 * Side Effects:
3652 * Configuration information, such as text string, colors, font,
3653 * etc. get set; old resources get freed, if there were any.
3654 * The widget is redisplayed if needed.
3655 *
3656 *----------------------------------------------------------------------
3657 */
3658static int
3659TabConfigureOp(nbPtr, interp, argc, argv)
3660 Notebook *nbPtr;
3661 Tcl_Interp *interp;
3662 int argc;
3663 char **argv;
3664{
3665 int nTabs, nOpts, result;
3666 char **options;
3667 register int i;
3668 Tab *tabPtr;
3669
3670 /* Figure out where the option value pairs begin */
3671 argc -= 3;
3672 argv += 3;
3673 for (i = 0; i < argc; i++) {
3674 if (argv[i][0] == '-') {
3675 break;
3676 }
3677 if (GetTab(nbPtr, argv[i], &tabPtr, INVALID_FAIL) != TCL_OK) {
3678 return TCL_ERROR; /* Can't find node. */
3679 }
3680 }
3681 nTabs = i; /* Number of tab indices specified */
3682 nOpts = argc - i; /* Number of options specified */
3683 options = argv + i; /* Start of options in argv */
3684
3685 for (i = 0; i < nTabs; i++) {
3686 GetTab(nbPtr, argv[i], &tabPtr, INVALID_FAIL);
3687 if (argc == 1) {
3688 return Tk_ConfigureInfo(interp, nbPtr->tkwin, tabConfigSpecs,
3689 (char *)tabPtr, (char *)NULL, 0);
3690 } else if (argc == 2) {
3691 return Tk_ConfigureInfo(interp, nbPtr->tkwin, tabConfigSpecs,
3692 (char *)tabPtr, argv[2], 0);
3693 }
3694 Tcl_Preserve(tabPtr);
3695 lastNotebookInstance = nbPtr;
3696 result = Tk_ConfigureWidget(interp, nbPtr->tkwin, tabConfigSpecs,
3697 nOpts, options, (char *)tabPtr, TK_CONFIG_ARGV_ONLY);
3698 if (result == TCL_OK) {
3699 result = ConfigureTab(nbPtr, tabPtr);
3700 }
3701 Tcl_Release(tabPtr);
3702 if (result == TCL_ERROR) {
3703 return TCL_ERROR;
3704 }
3705 if (tabPtr->flags & TAB_VISIBLE) {
3706 nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
3707 EventuallyRedraw(nbPtr);
3708 }
3709 }
3710 return TCL_OK;
3711}
3712
3713/*
3714 *----------------------------------------------------------------------
3715 *
3716 * TabDockallOp --
3717 *
3718 * .h tab dockall
3719 *
3720 *----------------------------------------------------------------------
3721 */
3722/*ARGSUSED*/
3723static int
3724TabDockallOp(nbPtr, interp, argc, argv)
3725 Notebook *nbPtr;
3726 Tcl_Interp *interp;
3727 int argc; /* Not used. */
3728 char **argv; /* Not used. */
3729{
3730 Tab *tabPtr;
3731 Blt_ChainLink *linkPtr;
3732
3733 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
3734 linkPtr = Blt_ChainNextLink(linkPtr)) {
3735 tabPtr = Blt_ChainGetValue(linkPtr);
3736 if (tabPtr->container != NULL) {
3737 Tcl_EventuallyFree(tabPtr, DestroyTearoff);
3738 }
3739 }
3740 return TCL_OK;
3741}
3742
3743/*
3744 *----------------------------------------------------------------------
3745 *
3746 * TabNamesOp --
3747 *
3748 * .h tab names pattern
3749 *
3750 *----------------------------------------------------------------------
3751 */
3752/*ARGSUSED*/
3753static int
3754TabNamesOp(nbPtr, interp, argc, argv)
3755 Notebook *nbPtr;
3756 Tcl_Interp *interp;
3757 int argc; /* Not used. */
3758 char **argv; /* Not used. */
3759{
3760 Tab *tabPtr;
3761 Blt_ChainLink *linkPtr;
3762
3763 if (argc == 3) {
3764 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
3765 linkPtr = Blt_ChainNextLink(linkPtr)) {
3766 tabPtr = Blt_ChainGetValue(linkPtr);
3767 Tcl_AppendElement(interp, tabPtr->name);
3768 }
3769 } else {
3770 register int i;
3771
3772 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
3773 linkPtr = Blt_ChainNextLink(linkPtr)) {
3774 tabPtr = Blt_ChainGetValue(linkPtr);
3775 for (i = 3; i < argc; i++) {
3776 if (Tcl_StringMatch(tabPtr->name, argv[i])) {
3777 Tcl_AppendElement(interp, tabPtr->name);
3778 break;
3779 }
3780 }
3781 }
3782 }
3783 return TCL_OK;
3784}
3785/*
3786 *----------------------------------------------------------------------
3787 *
3788 * TabTearoffOp --
3789 *
3790 * .h tab tearoff index ?title?
3791 *
3792 *----------------------------------------------------------------------
3793 */
3794/*ARGSUSED*/
3795static int
3796TabTearoffOp(nbPtr, interp, argc, argv)
3797 Notebook *nbPtr;
3798 Tcl_Interp *interp;
3799 int argc; /* Not used. */
3800 char **argv;
3801{
3802 Tab *tabPtr;
3803 int result;
3804 Tk_Window tkwin;
3805
3806 if (GetTab(nbPtr, argv[3], &tabPtr, INVALID_OK) != TCL_OK) {
3807 return TCL_ERROR;
3808 }
3809 if ((tabPtr == NULL) || (tabPtr->tkwin == NULL) ||
3810 (tabPtr->state == STATE_DISABLED)) {
3811 return TCL_OK; /* No-op */
3812 }
3813 if (argc == 4) {
3814 Tk_Window parent;
3815
3816 parent = (tabPtr->container == NULL)
3817 ? nbPtr->tkwin : tabPtr->container;
3818 Tcl_SetResult(nbPtr->interp, Tk_PathName(parent), TCL_VOLATILE);
3819 return TCL_OK;
3820 }
3821 Tcl_Preserve(tabPtr);
3822 result = TCL_OK;
3823
3824 tkwin = Tk_NameToWindow(interp, argv[4], nbPtr->tkwin);
3825 Tcl_ResetResult(interp);
3826
3827 if (tabPtr->container != NULL) {
3828 Tcl_EventuallyFree(tabPtr, DestroyTearoff);
3829 }
3830 if ((tkwin != nbPtr->tkwin) && (tabPtr->container == NULL)) {
3831 result = CreateTearoff(nbPtr, argv[4], tabPtr);
3832 }
3833 Tcl_Release(tabPtr);
3834 EventuallyRedraw(nbPtr);
3835 return result;
3836}
3837
3838/*
3839 *----------------------------------------------------------------------
3840 *
3841 * TabOp --
3842 *
3843 * This procedure handles tab operations.
3844 *
3845 * Results:
3846 * A standard Tcl result.
3847 *
3848 *----------------------------------------------------------------------
3849 */
3850static Blt_OpSpec tabOps[] =
3851{
3852 {"cget", 2, (Blt_Op)TabCgetOp, 5, 5, "nameOrIndex option",},
3853 {"configure", 2, (Blt_Op)TabConfigureOp, 4, 0,
3854 "nameOrIndex ?option value?...",},
3855 {"dockall", 1, (Blt_Op)TabDockallOp, 3, 3, "" },
3856 {"names", 1, (Blt_Op)TabNamesOp, 3, 0, "?pattern...?",},
3857 {"tearoff", 1, (Blt_Op)TabTearoffOp, 4, 5, "index ?parent?",},
3858};
3859
3860static int nTabOps = sizeof(tabOps) / sizeof(Blt_OpSpec);
3861
3862static int
3863TabOp(nbPtr, interp, argc, argv)
3864 Notebook *nbPtr;
3865 Tcl_Interp *interp;
3866 int argc;
3867 char **argv;
3868{
3869 Blt_Op proc;
3870 int result;
3871
3872 proc = Blt_GetOp(interp, nTabOps, tabOps, BLT_OP_ARG2, argc, argv, 0);
3873 if (proc == NULL) {
3874 return TCL_ERROR;
3875 }
3876 result = (*proc) (nbPtr, interp, argc, argv);
3877 return result;
3878}
3879
3880/*
3881 *----------------------------------------------------------------------
3882 *
3883 * PerforationActivateOp --
3884 *
3885 * This procedure is called to highlight the perforation.
3886 *
3887 * .h perforation highlight boolean
3888 *
3889 * Results:
3890 * A standard Tcl result. If TCL_ERROR is returned, then
3891 * interp->result contains an error message.
3892 *
3893 *----------------------------------------------------------------------
3894 */
3895/*ARGSUSED*/
3896static int
3897PerforationActivateOp(nbPtr, interp, argc, argv)
3898 Notebook *nbPtr;
3899 Tcl_Interp *interp; /* Not used. */
3900 int argc;
3901 char **argv;
3902{
3903 int bool;
3904
3905 if (Tcl_GetBoolean(interp, argv[3], &bool) != TCL_OK) {
3906 return TCL_ERROR;
3907 }
3908 if (bool) {
3909 nbPtr->flags |= PERFORATION_ACTIVE;
3910 } else {
3911 nbPtr->flags &= ~PERFORATION_ACTIVE;
3912 }
3913 EventuallyRedraw(nbPtr);
3914 return TCL_OK;
3915}
3916
3917/*
3918 *----------------------------------------------------------------------
3919 *
3920 * PerforationInvokeOp --
3921 *
3922 * This procedure is called to invoke a perforation command.
3923 *
3924 * .t perforation invoke
3925 *
3926 * Results:
3927 * A standard Tcl result. If TCL_ERROR is returned, then
3928 * interp->result contains an error message.
3929 *
3930 *----------------------------------------------------------------------
3931 */
3932/*ARGSUSED*/
3933static int
3934PerforationInvokeOp(nbPtr, interp, argc, argv)
3935 Notebook *nbPtr;
3936 Tcl_Interp *interp; /* Not used. */
3937 int argc;
3938 char **argv;
3939{
3940
3941 if (nbPtr->selectPtr != NULL) {
3942 char *cmd;
3943
3944 cmd = GETATTR(nbPtr->selectPtr, perfCommand);
3945 if (cmd != NULL) {
3946 Tcl_DString dString;
3947 int result;
3948
3949 PercentSubst(nbPtr, nbPtr->selectPtr, cmd, &dString);
3950 Tcl_Preserve(nbPtr);
3951 result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString));
3952 Tcl_Release(nbPtr);
3953 Tcl_DStringFree(&dString);
3954 if (result != TCL_OK) {
3955 return TCL_ERROR;
3956 }
3957 }
3958 }
3959 return TCL_OK;
3960}
3961
3962/*
3963 *----------------------------------------------------------------------
3964 *
3965 * PerforationOp --
3966 *
3967 * This procedure handles tab operations.
3968 *
3969 * Results:
3970 * A standard Tcl result.
3971 *
3972 *----------------------------------------------------------------------
3973 */
3974static Blt_OpSpec perforationOps[] =
3975{
3976 {"activate", 1, (Blt_Op)PerforationActivateOp, 4, 4, "boolean" },
3977 {"invoke", 1, (Blt_Op)PerforationInvokeOp, 3, 3, "",},
3978};
3979
3980static int nPerforationOps = sizeof(perforationOps) / sizeof(Blt_OpSpec);
3981
3982static int
3983PerforationOp(nbPtr, interp, argc, argv)
3984 Notebook *nbPtr;
3985 Tcl_Interp *interp;
3986 int argc;
3987 char **argv;
3988{
3989 Blt_Op proc;
3990 int result;
3991
3992 proc = Blt_GetOp(interp, nPerforationOps, perforationOps, BLT_OP_ARG2,
3993 argc, argv, 0);
3994 if (proc == NULL) {
3995 return TCL_ERROR;
3996 }
3997 result = (*proc) (nbPtr, interp, argc, argv);
3998 return result;
3999}
4000
4001/*
4002 *----------------------------------------------------------------------
4003 *
4004 * ScanOp --
4005 *
4006 * Implements the quick scan.
4007 *
4008 *----------------------------------------------------------------------
4009 */
4010/*ARGSUSED*/
4011static int
4012ScanOp(nbPtr, interp, argc, argv)
4013 Notebook *nbPtr;
4014 Tcl_Interp *interp;
4015 int argc; /* Not used. */
4016 char **argv;
4017{
4018 int x, y;
4019 char c;
4020 unsigned int length;
4021 int oper;
4022
4023#define SCAN_MARK 1
4024#define SCAN_DRAGTO 2
4025 c = argv[2][0];
4026 length = strlen(argv[2]);
4027 if ((c == 'm') && (strncmp(argv[2], "mark", length) == 0)) {
4028 oper = SCAN_MARK;
4029 } else if ((c == 'd') && (strncmp(argv[2], "dragto", length) == 0)) {
4030 oper = SCAN_DRAGTO;
4031 } else {
4032 Tcl_AppendResult(interp, "bad scan operation \"", argv[2],
4033 "\": should be either \"mark\" or \"dragto\"", (char *)NULL);
4034 return TCL_ERROR;
4035 }
4036 if ((Tk_GetPixels(interp, nbPtr->tkwin, argv[3], &x) != TCL_OK) ||
4037 (Tk_GetPixels(interp, nbPtr->tkwin, argv[4], &y) != TCL_OK)) {
4038 return TCL_ERROR;
4039 }
4040 if (oper == SCAN_MARK) {
4041 if (nbPtr->side & SIDE_VERTICAL) {
4042 nbPtr->scanAnchor = y;
4043 } else {
4044 nbPtr->scanAnchor = x;
4045 }
4046 nbPtr->scanOffset = nbPtr->scrollOffset;
4047 } else {
4048 int offset, delta;
4049
4050 if (nbPtr->side & SIDE_VERTICAL) {
4051 delta = nbPtr->scanAnchor - y;
4052 } else {
4053 delta = nbPtr->scanAnchor - x;
4054 }
4055 offset = nbPtr->scanOffset + (10 * delta);
4056 offset = Blt_AdjustViewport(offset, nbPtr->worldWidth,
4057 VPORTWIDTH(nbPtr), nbPtr->scrollUnits, BLT_SCROLL_MODE_CANVAS);
4058 nbPtr->scrollOffset = offset;
4059 nbPtr->flags |= TNB_SCROLL;
4060 EventuallyRedraw(nbPtr);
4061 }
4062 return TCL_OK;
4063}
4064
4065/*ARGSUSED*/
4066static int
4067SeeOp(nbPtr, interp, argc, argv)
4068 Notebook *nbPtr;
4069 Tcl_Interp *interp; /* Not used. */
4070 int argc;
4071 char **argv;
4072{
4073 Tab *tabPtr;
4074
4075 if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
4076 return TCL_ERROR;
4077 }
4078 if (tabPtr != NULL) {
4079 int left, right, width;
4080
4081 width = VPORTWIDTH(nbPtr);
4082 left = nbPtr->scrollOffset + nbPtr->xSelectPad;
4083 right = nbPtr->scrollOffset + width - nbPtr->xSelectPad;
4084
4085 /* If the tab is partially obscured, scroll so that it's
4086 * entirely in view. */
4087 if (tabPtr->worldX < left) {
4088 nbPtr->scrollOffset = tabPtr->worldX;
4089 if (TabIndex(nbPtr, tabPtr) > 0) {
4090 nbPtr->scrollOffset -= TAB_SCROLL_OFFSET;
4091 }
4092 } else if ((tabPtr->worldX + tabPtr->worldWidth) >= right) {
4093 Blt_ChainLink *linkPtr;
4094
4095 nbPtr->scrollOffset = tabPtr->worldX + tabPtr->worldWidth -
4096 (width - 2 * nbPtr->xSelectPad);
4097 linkPtr = Blt_ChainNextLink(tabPtr->linkPtr);
4098 if (linkPtr != NULL) {
4099 Tab *nextPtr;
4100
4101 nextPtr = Blt_ChainGetValue(linkPtr);
4102 if (nextPtr->tier == tabPtr->tier) {
4103 nbPtr->scrollOffset += TAB_SCROLL_OFFSET;
4104 }
4105 }
4106 }
4107 nbPtr->flags |= TNB_SCROLL;
4108 EventuallyRedraw(nbPtr);
4109 }
4110 return TCL_OK;
4111}
4112
4113/*ARGSUSED*/
4114static int
4115SizeOp(nbPtr, interp, argc, argv)
4116 Notebook *nbPtr;
4117 Tcl_Interp *interp;
4118 int argc; /* Not used. */
4119 char **argv; /* Not used. */
4120{
4121 Tcl_SetResult(interp, Blt_Itoa(Blt_ChainGetLength(nbPtr->chainPtr)),
4122 TCL_VOLATILE);
4123 return TCL_OK;
4124}
4125
4126
4127
4128static int
4129CountTabs(nbPtr)
4130 Notebook *nbPtr;
4131{
4132 int count;
4133 int width, height;
4134 Blt_ChainLink *linkPtr;
4135 register Tab *tabPtr;
4136 register int pageWidth, pageHeight;
4137 int labelWidth, labelHeight;
4138 int tabWidth, tabHeight;
4139
4140 pageWidth = pageHeight = 0;
4141 count = 0;
4142
4143 labelWidth = labelHeight = 0;
4144
4145 /*
4146 * Pass 1: Figure out the maximum area needed for a label and a
4147 * page. Both the label and page dimensions are adjusted
4148 * for orientation. In addition, reset the visibility
4149 * flags and reorder the tabs.
4150 */
4151 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
4152 linkPtr = Blt_ChainNextLink(linkPtr)) {
4153 tabPtr = Blt_ChainGetValue(linkPtr);
4154
4155 /* Reset visibility flag and order of tabs. */
4156
4157 tabPtr->flags &= ~TAB_VISIBLE;
4158 count++;
4159
4160 if (tabPtr->tkwin != NULL) {
4161 width = GetReqWidth(tabPtr);
4162 if (pageWidth < width) {
4163 pageWidth = width;
4164 }
4165 height = GetReqHeight(tabPtr);
4166 if (pageHeight < height) {
4167 pageHeight = height;
4168 }
4169 }
4170 if (labelWidth < tabPtr->labelWidth) {
4171 labelWidth = tabPtr->labelWidth;
4172 }
4173 if (labelHeight < tabPtr->labelHeight) {
4174 labelHeight = tabPtr->labelHeight;
4175 }
4176 }
4177
4178 nbPtr->overlap = 0;
4179
4180 /*
4181 * Pass 2: Set the individual sizes of each tab. This is different
4182 * for constant and variable width tabs. Add the extra space
4183 * needed for slanted tabs, now that we know maximum tab
4184 * height.
4185 */
4186 if (nbPtr->defTabStyle.constWidth) {
4187 int slant;
4188
4189 tabWidth = 2 * nbPtr->inset2;
4190 tabHeight = nbPtr->inset2 /* + 4 */;
4191
4192 if (nbPtr->side & SIDE_VERTICAL) {
4193 tabWidth += labelHeight;
4194 tabHeight += labelWidth;
4195 slant = labelWidth;
4196 } else {
4197 tabWidth += labelWidth;
4198 tabHeight += labelHeight;
4199 slant = labelHeight;
4200 }
4201 if (nbPtr->slant & SLANT_LEFT) {
4202 tabWidth += slant;
4203 nbPtr->overlap += tabHeight / 2;
4204 }
4205 if (nbPtr->slant & SLANT_RIGHT) {
4206 tabWidth += slant;
4207 nbPtr->overlap += tabHeight / 2;
4208 }
4209 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
4210 linkPtr = Blt_ChainNextLink(linkPtr)) {
4211 tabPtr = Blt_ChainGetValue(linkPtr);
4212 tabPtr->worldWidth = tabWidth;
4213 tabPtr->worldHeight = tabHeight;
4214 }
4215 } else {
4216 int slant;
4217
4218 tabWidth = tabHeight = 0;
4219 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
4220 linkPtr = Blt_ChainNextLink(linkPtr)) {
4221 tabPtr = Blt_ChainGetValue(linkPtr);
4222
4223 width = 2 * nbPtr->inset2;
4224 height = nbPtr->inset2 /* + 4 */;
4225 if (nbPtr->side & SIDE_VERTICAL) {
4226 width += tabPtr->labelHeight;
4227 height += labelWidth;
4228 slant = labelWidth;
4229 } else {
4230 width += tabPtr->labelWidth;
4231 height += labelHeight;
4232 slant = labelHeight;
4233 }
4234 width += (nbPtr->slant & SLANT_LEFT) ? slant : nbPtr->corner;
4235 width += (nbPtr->slant & SLANT_RIGHT) ? slant : nbPtr->corner;
4236
4237 tabPtr->worldWidth = width; /* + 2 * (nbPtr->corner + nbPtr->xSelectPad) */ ;
4238 tabPtr->worldHeight = height;
4239
4240 if (tabWidth < width) {
4241 tabWidth = width;
4242 }
4243 if (tabHeight < height) {
4244 tabHeight = height;
4245 }
4246 }
4247 if (nbPtr->slant & SLANT_LEFT) {
4248 nbPtr->overlap += tabHeight / 2;
4249 }
4250 if (nbPtr->slant & SLANT_RIGHT) {
4251 nbPtr->overlap += tabHeight / 2;
4252 }
4253 }
4254
4255 nbPtr->tabWidth = tabWidth;
4256 nbPtr->tabHeight = tabHeight;
4257
4258 /*
4259 * Let the user override any page dimension.
4260 */
4261 nbPtr->pageWidth = pageWidth;
4262 nbPtr->pageHeight = pageHeight;
4263 if (nbPtr->reqPageWidth > 0) {
4264 nbPtr->pageWidth = nbPtr->reqPageWidth;
4265 }
4266 if (nbPtr->reqPageHeight > 0) {
4267 nbPtr->pageHeight = nbPtr->reqPageHeight;
4268 }
4269 return count;
4270}
4271
4272
4273static void
4274WidenTabs(nbPtr, startPtr, nTabs, adjustment)
4275 Notebook *nbPtr;
4276 Tab *startPtr;
4277 int nTabs;
4278 int adjustment;
4279{
4280 register Tab *tabPtr;
4281 register int i;
4282 int ration;
4283 Blt_ChainLink *linkPtr;
4284 int x;
4285
4286 x = startPtr->tier;
4287 while (adjustment > 0) {
4288 ration = adjustment / nTabs;
4289 if (ration == 0) {
4290 ration = 1;
4291 }
4292 linkPtr = startPtr->linkPtr;
4293 for (i = 0; (linkPtr != NULL) && (i < nTabs) && (adjustment > 0); i++) {
4294 tabPtr = Blt_ChainGetValue(linkPtr);
4295 adjustment -= ration;
4296 tabPtr->worldWidth += ration;
4297 assert(x == tabPtr->tier);
4298 linkPtr = Blt_ChainNextLink(linkPtr);
4299 }
4300 }
4301 /*
4302 * Go back and reset the world X-coordinates of the tabs,
4303 * now that their widths have changed.
4304 */
4305 x = 0;
4306 linkPtr = startPtr->linkPtr;
4307 for (i = 0; (i < nTabs) && (linkPtr != NULL); i++) {
4308 tabPtr = Blt_ChainGetValue(linkPtr);
4309 tabPtr->worldX = x;
4310 x += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
4311 linkPtr = Blt_ChainNextLink(linkPtr);
4312 }
4313}
4314
4315
4316static void
4317AdjustTabSizes(nbPtr, nTabs)
4318 Notebook *nbPtr;
4319 int nTabs;
4320{
4321 int tabsPerTier;
4322 int total, count, extra;
4323 Tab *startPtr, *nextPtr;
4324 Blt_ChainLink *linkPtr;
4325 register Tab *tabPtr;
4326 int x, maxWidth;
4327
4328 tabsPerTier = (nTabs + (nbPtr->nTiers - 1)) / nbPtr->nTiers;
4329 x = 0;
4330 maxWidth = 0;
4331 if (nbPtr->defTabStyle.constWidth) {
4332 register int i;
4333
4334 linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr);
4335 count = 1;
4336 while (linkPtr != NULL) {
4337 for (i = 0; i < tabsPerTier; i++) {
4338 tabPtr = Blt_ChainGetValue(linkPtr);
4339 tabPtr->tier = count;
4340 tabPtr->worldX = x;
4341 x += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
4342 linkPtr = Blt_ChainNextLink(linkPtr);
4343 if (x > maxWidth) {
4344 maxWidth = x;
4345 }
4346 if (linkPtr == NULL) {
4347 goto done;
4348 }
4349 }
4350 count++;
4351 x = 0;
4352 }
4353 }
4354 done:
4355 /* Add to tab widths to fill out row. */
4356 if (((nTabs % tabsPerTier) != 0) && (nbPtr->defTabStyle.constWidth)) {
4357 return;
4358 }
4359 startPtr = NULL;
4360 count = total = 0;
4361 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
4362 /*empty*/ ) {
4363 tabPtr = Blt_ChainGetValue(linkPtr);
4364 if (startPtr == NULL) {
4365 startPtr = tabPtr;
4366 }
4367 count++;
4368 total += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
4369 linkPtr = Blt_ChainNextLink(linkPtr);
4370 if (linkPtr != NULL) {
4371 nextPtr = Blt_ChainGetValue(linkPtr);
4372 if (tabPtr->tier == nextPtr->tier) {
4373 continue;
4374 }
4375 }
4376 total += nbPtr->overlap;
4377 extra = nbPtr->worldWidth - total;
4378 assert(count > 0);
4379 if (extra > 0) {
4380 WidenTabs(nbPtr, startPtr, count, extra);
4381 }
4382 count = total = 0;
4383 startPtr = NULL;
4384 }
4385}
4386
4387/*
4388 *
4389 * tabWidth = textWidth + gap + (2 * (pad + outerBW));
4390 *
4391 * tabHeight = textHeight + 2 * (pad + outerBW) + topMargin;
4392 *
4393 */
4394static void
4395ComputeLayout(nbPtr)
4396 Notebook *nbPtr;
4397{
4398 int width;
4399 Blt_ChainLink *linkPtr;
4400 Tab *tabPtr;
4401 int x, extra;
4402 int nTiers, nTabs;
4403
4404 nbPtr->nTiers = 0;
4405 nbPtr->pageTop = 0;
4406 nbPtr->worldWidth = 1;
4407 nbPtr->yPad = 0;
4408
4409 nTabs = CountTabs(nbPtr);
4410 if (nTabs == 0) {
4411 return;
4412 }
4413 /* Reset the pointers to the selected and starting tab. */
4414 if (nbPtr->selectPtr == NULL) {
4415 linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr);
4416 if (linkPtr != NULL) {
4417 nbPtr->selectPtr = Blt_ChainGetValue(linkPtr);
4418 }
4419 }
4420 if (nbPtr->startPtr == NULL) {
4421 nbPtr->startPtr = nbPtr->selectPtr;
4422 }
4423 if (nbPtr->focusPtr == NULL) {
4424 nbPtr->focusPtr = nbPtr->selectPtr;
4425 Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr, NULL);
4426 }
4427
4428 if (nbPtr->side & SIDE_VERTICAL) {
4429 width = Tk_Height(nbPtr->tkwin) - 2 *
4430 (nbPtr->corner + nbPtr->xSelectPad);
4431 } else {
4432 width = Tk_Width(nbPtr->tkwin) - (2 * nbPtr->inset) -
4433 nbPtr->xSelectPad - nbPtr->corner;
4434 }
4435 nbPtr->flags |= TNB_STATIC;
4436 if (nbPtr->reqTiers > 1) {
4437 int total, maxWidth;
4438
4439 /* Static multiple tier mode. */
4440
4441 /* Sum tab widths and determine the number of tiers needed. */
4442 nTiers = 1;
4443 total = x = 0;
4444 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
4445 linkPtr = Blt_ChainNextLink(linkPtr)) {
4446 tabPtr = Blt_ChainGetValue(linkPtr);
4447 if ((x + tabPtr->worldWidth) > width) {
4448 nTiers++;
4449 x = 0;
4450 }
4451 tabPtr->worldX = x;
4452 tabPtr->tier = nTiers;
4453 extra = tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
4454 total += extra, x += extra;
4455 }
4456 maxWidth = width;
4457
4458 if (nTiers > nbPtr->reqTiers) {
4459 /*
4460 * The tabs do not fit into the requested number of tiers.
4461 * Go into scrolling mode.
4462 */
4463 width = ((total + nbPtr->tabWidth) / nbPtr->reqTiers);
4464 x = 0;
4465 nTiers = 1;
4466 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr);
4467 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
4468 tabPtr = Blt_ChainGetValue(linkPtr);
4469 tabPtr->tier = nTiers;
4470 /*
4471 * Keep adding tabs to a tier until we overfill it.
4472 */
4473 tabPtr->worldX = x;
4474 x += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
4475 if (x > width) {
4476 nTiers++;
4477 if (x > maxWidth) {
4478 maxWidth = x;
4479 }
4480 x = 0;
4481 }
4482 }
4483 nbPtr->flags &= ~TNB_STATIC;
4484 }
4485 nbPtr->worldWidth = maxWidth;
4486 nbPtr->nTiers = nTiers;
4487
4488 if (nTiers > 1) {
4489 AdjustTabSizes(nbPtr, nTabs);
4490 }
4491 if (nbPtr->flags & TNB_STATIC) {
4492 nbPtr->worldWidth = VPORTWIDTH(nbPtr);
4493 } else {
4494 /* Do you add an offset ? */
4495 nbPtr->worldWidth += (nbPtr->xSelectPad + nbPtr->corner);
4496 }
4497 nbPtr->worldWidth += nbPtr->overlap;
4498 if (nbPtr->selectPtr != NULL) {
4499 RenumberTiers(nbPtr, nbPtr->selectPtr);
4500 }
4501 } else {
4502 /*
4503 * Scrollable single tier mode.
4504 */
4505 nTiers = 1;
4506 x = 0;
4507 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
4508 linkPtr = Blt_ChainNextLink(linkPtr)) {
4509 tabPtr = Blt_ChainGetValue(linkPtr);
4510 tabPtr->tier = nTiers;
4511 tabPtr->worldX = x;
4512 tabPtr->worldY = 0;
4513 x += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
4514 }
4515 nbPtr->worldWidth = x + nbPtr->corner - nbPtr->gap +
4516 nbPtr->xSelectPad + nbPtr->overlap;
4517 nbPtr->flags &= ~TNB_STATIC;
4518 }
4519 if (nTiers == 1) {
4520 nbPtr->yPad = nbPtr->ySelectPad;
4521 }
4522 nbPtr->nTiers = nTiers;
4523 nbPtr->pageTop = nbPtr->inset + nbPtr->yPad /* + 4 */ +
4524 (nbPtr->nTiers * nbPtr->tabHeight) + nbPtr->inset2;
4525
4526 if (nbPtr->side & SIDE_VERTICAL) {
4527 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
4528 linkPtr = Blt_ChainNextLink(linkPtr)) {
4529 tabPtr = Blt_ChainGetValue(linkPtr);
4530 tabPtr->screenWidth = (short int)nbPtr->tabHeight;
4531 tabPtr->screenHeight = (short int)tabPtr->worldWidth;
4532 }
4533 } else {
4534 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
4535 linkPtr = Blt_ChainNextLink(linkPtr)) {
4536 tabPtr = Blt_ChainGetValue(linkPtr);
4537 tabPtr->screenWidth = (short int)tabPtr->worldWidth;
4538 tabPtr->screenHeight = (short int)nbPtr->tabHeight;
4539 }
4540 }
4541}
4542
4543static void
4544ComputeVisibleTabs(nbPtr)
4545 Notebook *nbPtr;
4546{
4547 int nVisibleTabs;
4548 register Tab *tabPtr;
4549 Blt_ChainLink *linkPtr;
4550
4551 nbPtr->nVisible = 0;
4552 if (Blt_ChainGetLength(nbPtr->chainPtr) == 0) {
4553 return;
4554 }
4555 nVisibleTabs = 0;
4556 if (nbPtr->flags & TNB_STATIC) {
4557
4558 /* Static multiple tier mode. */
4559
4560 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
4561 linkPtr = Blt_ChainNextLink(linkPtr)) {
4562 tabPtr = Blt_ChainGetValue(linkPtr);
4563 tabPtr->flags |= TAB_VISIBLE;
4564 nVisibleTabs++;
4565 }
4566 } else {
4567 int width, offset;
4568 /*
4569 * Scrollable (single or multiple) tier mode.
4570 */
4571 offset = nbPtr->scrollOffset - (nbPtr->outerPad + nbPtr->xSelectPad);
4572 width = VPORTWIDTH(nbPtr) + nbPtr->scrollOffset +
4573 2 * nbPtr->outerPad;
4574 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
4575 linkPtr = Blt_ChainNextLink(linkPtr)) {
4576 tabPtr = Blt_ChainGetValue(linkPtr);
4577 if ((tabPtr->worldX >= width) ||
4578 ((tabPtr->worldX + tabPtr->worldWidth) < offset)) {
4579 tabPtr->flags &= ~TAB_VISIBLE;
4580 } else {
4581 tabPtr->flags |= TAB_VISIBLE;
4582 nVisibleTabs++;
4583 }
4584 }
4585 }
4586 for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
4587 linkPtr = Blt_ChainNextLink(linkPtr)) {
4588 tabPtr = Blt_ChainGetValue(linkPtr);
4589 tabPtr->screenX = tabPtr->screenY = -1000;
4590 if (tabPtr->flags & TAB_VISIBLE) {
4591 WorldToScreen(nbPtr, tabPtr->worldX, tabPtr->worldY,
4592 &(tabPtr->screenX), &(tabPtr->screenY));
4593 switch (nbPtr->side) {
4594 case SIDE_RIGHT:
4595 tabPtr->screenX -= nbPtr->tabHeight;
4596 break;
4597
4598 case SIDE_BOTTOM:
4599 tabPtr->screenY -= nbPtr->tabHeight;
4600 break;
4601 }
4602 }
4603 }
4604 nbPtr->nVisible = nVisibleTabs;
4605 Blt_PickCurrentItem(nbPtr->bindTable);
4606}
4607
4608
4609static void
4610Draw3DFolder(nbPtr, tabPtr, drawable, side, pointArr, nPoints)
4611 Notebook *nbPtr;
4612 Tab *tabPtr;
4613 Drawable drawable;
4614 int side;
4615 XPoint pointArr[];
4616 int nPoints;
4617{
4618 GC gc;
4619 int relief, borderWidth;
4620 Tk_3DBorder border;
4621
4622 if (tabPtr == nbPtr->selectPtr) {
4623 border = GETATTR(tabPtr, selBorder);
4624 } else if (tabPtr->border != NULL) {
4625 border = tabPtr->border;
4626 } else {
4627 border = nbPtr->defTabStyle.border;
4628 }
4629 relief = nbPtr->defTabStyle.relief;
4630 if ((side == SIDE_RIGHT) || (side == SIDE_TOP)) {
4631 borderWidth = -nbPtr->defTabStyle.borderWidth;
4632 if (relief == TK_RELIEF_SUNKEN) {
4633 relief = TK_RELIEF_RAISED;
4634 } else if (relief == TK_RELIEF_RAISED) {
4635 relief = TK_RELIEF_SUNKEN;
4636 }
4637 } else {
4638 borderWidth = nbPtr->defTabStyle.borderWidth;
4639 }
4640 {
4641 int i;
4642
4643#ifndef notdef
4644 int dx, dy;
4645 int oldType, newType;
4646 int start;
4647
4648 dx = pointArr[0].x - pointArr[1].x;
4649 dy = pointArr[0].y - pointArr[1].y;
4650 oldType = ((dy < 0) || (dx > 0));
4651 start = 0;
4652 for (i = 1; i < nPoints; i++) {
4653 dx = pointArr[i - 1].x - pointArr[i].x;
4654 dy = pointArr[i - 1].y - pointArr[i].y;
4655 newType = ((dy < 0) || (dx > 0));
4656 if (newType != oldType) {
4657 if (oldType) {
4658 gc = Tk_GCForColor(nbPtr->shadowColor, drawable);
4659 } else {
4660 gc = Tk_3DBorderGC(nbPtr->tkwin, border, TK_3D_FLAT_GC);
4661 }
4662 XDrawLines(nbPtr->display, drawable, gc, pointArr + start,
4663 i - start, CoordModeOrigin);
4664 start = i - 1;
4665 oldType = newType;
4666 }
4667 }
4668 if (start != i) {
4669 if (oldType) {
4670 gc = Tk_GCForColor(nbPtr->shadowColor, drawable);
4671 } else {
4672 gc = Tk_3DBorderGC(nbPtr->tkwin, border, TK_3D_FLAT_GC);
4673 }
4674 XDrawLines(nbPtr->display, drawable, gc, pointArr + start,
4675 i - start, CoordModeOrigin);
4676 }
4677#else
4678 /* Draw the outline of the folder. */
4679 gc = Tk_GCForColor(nbPtr->shadowColor, drawable);
4680 XDrawLines(nbPtr->display, drawable, gc, pointArr, nPoints,
4681 CoordModeOrigin);
4682#endif
4683 }
4684 /* And the folder itself. */
4685 if (tabPtr->tile != NULL) {
4686#ifdef notdef
4687 Tk_Fill3DPolygon(nbPtr->tkwin, drawable, border, pointArr, nPoints,
4688 borderWidth, relief);
4689#endif
4690 Blt_TilePolygon(nbPtr->tkwin, drawable, tabPtr->tile, pointArr,
4691 nPoints);
4692#ifdef notdef
4693 Tk_Draw3DPolygon(nbPtr->tkwin, drawable, border, pointArr, nPoints,
4694 borderWidth, relief);
4695#endif
4696 } else {
4697 Tk_Fill3DPolygon(nbPtr->tkwin, drawable, border, pointArr, nPoints,
4698 borderWidth, relief);
4699 }
4700}
4701
4702/*
4703 * x,y
4704 * |1|2|3| 4 |3|2|1|
4705 *
4706 * 1. tab border width
4707 * 2. corner offset
4708 * 3. label pad
4709 * 4. label width
4710 *
4711 *
4712 */
4713static void
4714DrawLabel(nbPtr, tabPtr, drawable)
4715 Notebook *nbPtr;
4716 Tab *tabPtr;
4717 Drawable drawable;
4718{
4719 int x, y, dx, dy;
4720 int tx, ty, ix, iy;
4721 int imgWidth, imgHeight;
4722 int active, selected;
4723 XColor *fgColor, *bgColor;
4724 Tk_3DBorder border;
4725 GC gc;
4726
4727 if (!(tabPtr->flags & TAB_VISIBLE)) {
4728 return;
4729 }
4730 x = tabPtr->screenX;
4731 y = tabPtr->screenY;
4732
4733 active = (nbPtr->activePtr == tabPtr);
4734 selected = (nbPtr->selectPtr == tabPtr);
4735
4736 fgColor = GETATTR(tabPtr, textColor);
4737 border = GETATTR(tabPtr, border);
4738 if (selected) {
4739 border = GETATTR(tabPtr, selBorder);
4740 }
4741 bgColor = Tk_3DBorderColor(border);
4742 if (active) {
4743 Tk_3DBorder activeBorder;
4744
4745 activeBorder = GETATTR(tabPtr, activeBorder);
4746 bgColor = Tk_3DBorderColor(activeBorder);
4747 }
4748 dx = (tabPtr->screenWidth - tabPtr->labelWidth) / 2;
4749 dy = (tabPtr->screenHeight - tabPtr->labelHeight) / 2;
4750
4751
4752 /*
4753 * The label position is computed with screen coordinates. This
4754 * is because both text and image components are oriented in
4755 * screen space, and not according to the orientation of the tabs
4756 * themselves. That's why we have to consider the side when
4757 * correcting for left/right slants.
4758 */
4759 switch (nbPtr->side) {
4760 case SIDE_TOP:
4761 case SIDE_BOTTOM:
4762 if (nbPtr->slant == SLANT_LEFT) {
4763 x += nbPtr->overlap;
4764 } else if (nbPtr->slant == SLANT_RIGHT) {
4765 x -= nbPtr->overlap;
4766 }
4767 break;
4768 case SIDE_LEFT:
4769 case SIDE_RIGHT:
4770 if (nbPtr->slant == SLANT_LEFT) {
4771 y += nbPtr->overlap;
4772 } else if (nbPtr->slant == SLANT_RIGHT) {
4773 y -= nbPtr->overlap;
4774 }
4775 break;
4776 }
4777
4778 /*
4779 * Draw the active or normal background color over the entire
4780 * label area. This includes both the tab's text and image.
4781 * The rectangle should be 2 pixels wider/taller than this
4782 * area. So if the label consists of just an image, we get an
4783 * halo around the image when the tab is active.
4784 */
4785 gc = Tk_GCForColor(bgColor, drawable);
4786 XFillRectangle(nbPtr->display, drawable, gc, x + dx, y + dy,
4787 tabPtr->labelWidth, tabPtr->labelHeight);
4788
4789 if ((nbPtr->flags & TNB_FOCUS) && (nbPtr->focusPtr == tabPtr)) {
4790 XDrawRectangle(nbPtr->display, drawable, nbPtr->defTabStyle.activeGC,
4791 x + dx, y + dy, tabPtr->labelWidth - 1, tabPtr->labelHeight - 1);
4792 }
4793 tx = ty = ix = iy = 0; /* Suppress compiler warning. */
4794
4795 imgWidth = imgHeight = 0;
4796 if (tabPtr->image != NULL) {
4797 imgWidth = ImageWidth(tabPtr->image);
4798 imgHeight = ImageHeight(tabPtr->image);
4799 }
4800 switch (nbPtr->defTabStyle.textSide) {
4801 case SIDE_LEFT:
4802 tx = x + dx + tabPtr->iPadX.side1;
4803 ty = y + (tabPtr->screenHeight - tabPtr->textHeight) / 2;
4804 ix = tx + tabPtr->textWidth + IMAGE_PAD;
4805 iy = y + (tabPtr->screenHeight - imgHeight) / 2;
4806 break;
4807 case SIDE_RIGHT:
4808 ix = x + dx + tabPtr->iPadX.side1 + IMAGE_PAD;
4809 iy = y + (tabPtr->screenHeight - imgHeight) / 2;
4810 tx = ix + imgWidth;
4811 ty = y + (tabPtr->screenHeight - tabPtr->textHeight) / 2;
4812 break;
4813 case SIDE_BOTTOM:
4814 iy = y + dy + tabPtr->iPadY.side1 + IMAGE_PAD;
4815 ix = x + (tabPtr->screenWidth - imgWidth) / 2;
4816 ty = iy + imgHeight;
4817 tx = x + (tabPtr->screenWidth - tabPtr->textWidth) / 2;
4818 break;
4819 case SIDE_TOP:
4820 tx = x + (tabPtr->screenWidth - tabPtr->textWidth) / 2;
4821 ty = y + dy + tabPtr->iPadY.side1 + IMAGE_PAD;
4822 ix = x + (tabPtr->screenWidth - imgWidth) / 2;
4823 iy = ty + tabPtr->textHeight;
4824 break;
4825 }
4826 if (tabPtr->image != NULL) {
4827 Tk_RedrawImage(ImageBits(tabPtr->image), 0, 0, imgWidth, imgHeight,
4828 drawable, ix, iy);
4829 }
4830 if (tabPtr->text != NULL) {
4831 TextStyle ts;
4832 XColor *activeColor;
4833
4834 activeColor = fgColor;
4835 if (selected) {
4836 activeColor = GETATTR(tabPtr, selColor);
4837 } else if (active) {
4838 activeColor = GETATTR(tabPtr, activeFgColor);
4839 }
4840 Blt_SetDrawTextStyle(&ts, GETATTR(tabPtr, font), tabPtr->textGC,
4841 fgColor, activeColor, tabPtr->shadow.color,
4842 nbPtr->defTabStyle.rotate, TK_ANCHOR_NW, TK_JUSTIFY_LEFT,
4843 0, tabPtr->shadow.offset);
4844 ts.state = tabPtr->state;
4845 ts.border = border;
4846 ts.padX.side1 = ts.padX.side2 = 2;
4847 if (selected || active) {
4848 ts.state |= STATE_ACTIVE;
4849 }
4850 Blt_DrawText(nbPtr->tkwin, drawable, tabPtr->text, &ts, tx, ty);
4851 }
4852}
4853
4854
4855static void
4856DrawPerforation(nbPtr, tabPtr, drawable)
4857 Notebook *nbPtr;
4858 Tab *tabPtr;
4859 Drawable drawable;
4860{
4861 XPoint pointArr[2];
4862 int x, y;
4863 int segmentWidth, max;
4864 Tk_3DBorder border, perfBorder;
4865
4866 if ((tabPtr->container != NULL) || (tabPtr->tkwin == NULL)) {
4867 return;
4868 }
4869 WorldToScreen(nbPtr, tabPtr->worldX + 2,
4870 tabPtr->worldY + tabPtr->worldHeight + 2, &x, &y);
4871 border = GETATTR(tabPtr, selBorder);
4872 segmentWidth = 3;
4873 if (nbPtr->flags & PERFORATION_ACTIVE) {
4874 perfBorder = GETATTR(tabPtr, activeBorder);
4875 } else {
4876 perfBorder = GETATTR(tabPtr, selBorder);
4877 }
4878 if (nbPtr->side & SIDE_HORIZONTAL) {
4879 pointArr[0].x = x;
4880 pointArr[0].y = pointArr[1].y = y;
4881 max = tabPtr->screenX + tabPtr->screenWidth - 2;
4882 Blt_Fill3DRectangle(nbPtr->tkwin, drawable, perfBorder,
4883 x - 2, y - 4, tabPtr->screenWidth, 8, 0, TK_RELIEF_FLAT);
4884 while (pointArr[0].x < max) {
4885 pointArr[1].x = pointArr[0].x + segmentWidth;
4886 if (pointArr[1].x > max) {
4887 pointArr[1].x = max;
4888 }
4889 Tk_Draw3DPolygon(nbPtr->tkwin, drawable, border, pointArr, 2, 1,
4890 TK_RELIEF_RAISED);
4891 pointArr[0].x += 2 * segmentWidth;
4892 }
4893 } else {
4894 pointArr[0].x = pointArr[1].x = x;
4895 pointArr[0].y = y;
4896 max = tabPtr->screenY + tabPtr->screenHeight - 2;
4897 Blt_Fill3DRectangle(nbPtr->tkwin, drawable, perfBorder,
4898 x - 4, y - 2, 8, tabPtr->screenHeight, 0, TK_RELIEF_FLAT);
4899 while (pointArr[0].y < max) {
4900 pointArr[1].y = pointArr[0].y + segmentWidth;
4901 if (pointArr[1].y > max) {
4902 pointArr[1].y = max;
4903 }
4904 Tk_Draw3DPolygon(nbPtr->tkwin, drawable, border, pointArr, 2, 1,
4905 TK_RELIEF_RAISED);
4906 pointArr[0].y += 2 * segmentWidth;
4907 }
4908 }
4909}
4910
4911#define NextPoint(px, py) \
4912 pointPtr->x = (px), pointPtr->y = (py), pointPtr++, nPoints++
4913#define EndPoint(px, py) \
4914 pointPtr->x = (px), pointPtr->y = (py), nPoints++
4915
4916#define BottomLeft(px, py) \
4917 NextPoint((px) + nbPtr->corner, (py)), \
4918 NextPoint((px), (py) - nbPtr->corner)
4919
4920#define TopLeft(px, py) \
4921 NextPoint((px), (py) + nbPtr->corner), \
4922 NextPoint((px) + nbPtr->corner, (py))
4923
4924#define TopRight(px, py) \
4925 NextPoint((px) - nbPtr->corner, (py)), \
4926 NextPoint((px), (py) + nbPtr->corner)
4927
4928#define BottomRight(px, py) \
4929 NextPoint((px), (py) - nbPtr->corner), \
4930 NextPoint((px) - nbPtr->corner, (py))
4931
4932
4933/*
4934 * From the left edge:
4935 *
4936 * |a|b|c|d|e| f |d|e|g|h| i |h|g|e|d|f| j |e|d|c|b|a|
4937 *
4938 * a. highlight ring
4939 * b. notebook 3D border
4940 * c. outer gap
4941 * d. page border
4942 * e. page corner
4943 * f. gap + select pad
4944 * g. label pad x (worldX)
4945 * h. internal pad x
4946 * i. label width
4947 * j. rest of page width
4948 *
4949 * worldX, worldY
4950 * |
4951 * |
4952 * * 4+ . . +5
4953 * 3+ +6
4954 * . .
4955 * . .
4956 * 1+. . .2+ +7 . . . .+8
4957 * 0+ +9
4958 * . .
4959 * . .
4960 *13+ +10
4961 * 12+-------------------------+11
4962 *
4963 */
4964static void
4965DrawFolder(nbPtr, tabPtr, drawable)
4966 Notebook *nbPtr;
4967 Tab *tabPtr;
4968 Drawable drawable;
4969{
4970 XPoint pointArr[16];
4971 XPoint *pointPtr;
4972 int width, height;
4973 int left, bottom, right, top, yBot, yTop;
4974 int x, y;
4975 register int i;
4976 int nPoints;
4977
4978 width = VPORTWIDTH(nbPtr);
4979 height = VPORTHEIGHT(nbPtr);
4980
4981 x = tabPtr->worldX;
4982 y = tabPtr->worldY;
4983
4984 nPoints = 0;
4985 pointPtr = pointArr;
4986
4987 /* Remember these are all world coordinates. */
4988 /*
4989 * x Left side of tab.
4990 * y Top of tab.
4991 * yTop Top of folder.
4992 * yBot Bottom of the tab.
4993 * left Left side of the folder.
4994 * right Right side of the folder.
4995 * top Top of folder.
4996 * bottom Bottom of folder.
4997 */
4998 left = nbPtr->scrollOffset - nbPtr->xSelectPad;
4999 right = left + width;
5000 yTop = y + tabPtr->worldHeight;
5001 yBot = nbPtr->pageTop - (nbPtr->inset + nbPtr->yPad);
5002 top = yBot - nbPtr->inset2 /* - 4 */;
5003
5004 if (nbPtr->pageHeight == 0) {
5005 bottom = yBot + 2 * nbPtr->corner;
5006 } else {
5007 bottom = height - nbPtr->yPad - 1;
5008 }
5009 if (tabPtr != nbPtr->selectPtr) {
5010
5011 /*
5012 * Case 1: Unselected tab
5013 *
5014 * * 3+ . . +4
5015 * 2+ +5
5016 * . .
5017 * 1+ +6
5018 * 0+ . . +7
5019 *
5020 */
5021
5022 if (nbPtr->slant & SLANT_LEFT) {
5023 NextPoint(x, yBot);
5024 NextPoint(x, yTop);
5025 NextPoint(x + nbPtr->tabHeight, y);
5026 } else {
5027 BottomLeft(x, yBot);
5028 TopLeft(x, y);
5029 }
5030 x += tabPtr->worldWidth;
5031 if (nbPtr->slant & SLANT_RIGHT) {
5032 NextPoint(x - nbPtr->tabHeight, y);
5033 NextPoint(x, yTop);
5034 NextPoint(x, yBot);
5035 } else {
5036 TopRight(x, y);
5037 BottomRight(x, yBot);
5038 }
5039 } else if (!(tabPtr->flags & TAB_VISIBLE)) {
5040
5041 /*
5042 * Case 2: Selected tab not visible in viewport. Draw folder only.
5043 *
5044 * * 3+ . . +4
5045 * 2+ +5
5046 * . .
5047 * 1+ +6
5048 * 0+------+7
5049 *
5050 */
5051
5052 TopLeft(left, top);
5053 TopRight(right, top);
5054 BottomRight(right, bottom);
5055 BottomLeft(left, bottom);
5056 } else {
5057 int flags;
5058 int tabWidth;
5059
5060 x -= nbPtr->xSelectPad;
5061 y -= nbPtr->yPad;
5062 tabWidth = tabPtr->worldWidth + 2 * nbPtr->xSelectPad;
5063
5064#define CLIP_NONE 0
5065#define CLIP_LEFT (1<<0)
5066#define CLIP_RIGHT (1<<1)
5067 flags = 0;
5068 if (x < left) {
5069 flags |= CLIP_LEFT;
5070 }
5071 if ((x + tabWidth) > right) {
5072 flags |= CLIP_RIGHT;
5073 }
5074 switch (flags) {
5075 case CLIP_NONE:
5076
5077 /*
5078 * worldX, worldY
5079 * |
5080 * * 4+ . . +5
5081 * 3+ +6
5082 * . .
5083 * . .
5084 * 1+. . .2+---------+7 . . . .+8
5085 * 0+ +9
5086 * . .
5087 * . .
5088 *13+ +10
5089 * 12+ . . . . . . . . . . . . +11
5090 */
5091
5092 if (x < (left + nbPtr->corner)) {
5093 NextPoint(left, top);
5094 } else {
5095 TopLeft(left, top);
5096 }
5097 if (nbPtr->slant & SLANT_LEFT) {
5098 NextPoint(x, yTop);
5099 NextPoint(x + nbPtr->tabHeight + nbPtr->yPad, y);
5100 } else {
5101 NextPoint(x, top);
5102 TopLeft(x, y);
5103 }
5104 x += tabWidth;
5105 if (nbPtr->slant & SLANT_RIGHT) {
5106 NextPoint(x - nbPtr->tabHeight - nbPtr->yPad, y);
5107 NextPoint(x, yTop);
5108 } else {
5109 TopRight(x, y);
5110 NextPoint(x, top);
5111 }
5112 if (x > (right - nbPtr->corner)) {
5113 NextPoint(right, top + nbPtr->corner);
5114 } else {
5115 TopRight(right, top);
5116 }
5117 BottomRight(right, bottom);
5118 BottomLeft(left, bottom);
5119 break;
5120
5121 case CLIP_LEFT:
5122
5123 /*
5124 * worldX, worldY
5125 * |
5126 * * 4+ . . +5
5127 * 3+ +6
5128 * . .
5129 * . .
5130 * 2+--------+7 . . . .+8
5131 * 1+ . . . +0 +9
5132 * . .
5133 * . .
5134 * 13+ +10
5135 * 12+ . . . .+11
5136 */
5137
5138 NextPoint(left, yBot);
5139 if (nbPtr->slant & SLANT_LEFT) {
5140 NextPoint(x, yBot);
5141 NextPoint(x, yTop);
5142 NextPoint(x + nbPtr->tabHeight + nbPtr->yPad, y);
5143 } else {
5144 BottomLeft(x, yBot);
5145 TopLeft(x, y);
5146 }
5147
5148 x += tabWidth;
5149 if (nbPtr->slant & SLANT_RIGHT) {
5150 NextPoint(x - nbPtr->tabHeight - nbPtr->yPad, y);
5151 NextPoint(x, yTop);
5152 NextPoint(x, top);
5153 } else {
5154 TopRight(x, y);
5155 NextPoint(x, top);
5156 }
5157 if (x > (right - nbPtr->corner)) {
5158 NextPoint(right, top + nbPtr->corner);
5159 } else {
5160 TopRight(right, top);
5161 }
5162 BottomRight(right, bottom);
5163 BottomLeft(left, bottom);
5164 break;
5165
5166 case CLIP_RIGHT:
5167
5168 /*
5169 * worldX, worldY
5170 * |
5171 * * 9+ . . +10
5172 * 8+ +11
5173 * . .
5174 * . .
5175 * 6+ . . . .7+---------+12
5176 * 5+ 0+ . . . +13
5177 * . .
5178 * . .
5179 * 4+ +1
5180 * 3+ . . . +2
5181 */
5182
5183 NextPoint(right, yBot);
5184 BottomRight(right, bottom);
5185 BottomLeft(left, bottom);
5186 if (x < (left + nbPtr->corner)) {
5187 NextPoint(left, top);
5188 } else {
5189 TopLeft(left, top);
5190 }
5191 NextPoint(x, top);
5192
5193 if (nbPtr->slant & SLANT_LEFT) {
5194 NextPoint(x, yTop);
5195 NextPoint(x + nbPtr->tabHeight + nbPtr->yPad, y);
5196 } else {
5197 TopLeft(x, y);
5198 }
5199 x += tabWidth;
5200 if (nbPtr->slant & SLANT_RIGHT) {
5201 NextPoint(x - nbPtr->tabHeight - nbPtr->yPad, y);
5202 NextPoint(x, yTop);
5203 NextPoint(x, yBot);
5204 } else {
5205 TopRight(x, y);
5206 BottomRight(x, yBot);
5207 }
5208 break;
5209
5210 case (CLIP_LEFT | CLIP_RIGHT):
5211
5212 /*
5213 * worldX, worldY
5214 * |
5215 * * 4+ . . . . . . . . +5
5216 * 3+ +6
5217 * . .
5218 * . .
5219 * 1+---------------------+7
5220 * 2+ 0+ +9 .+8
5221 * . .
5222 * . .
5223 * 13+ +10
5224 * 12+ . . . +11
5225 */
5226
5227 NextPoint(left, yBot);
5228 if (nbPtr->slant & SLANT_LEFT) {
5229 NextPoint(x, yBot);
5230 NextPoint(x, yTop);
5231 NextPoint(x + nbPtr->tabHeight + nbPtr->yPad, y);
5232 } else {
5233 BottomLeft(x, yBot);
5234 TopLeft(x, y);
5235 }
5236 x += tabPtr->worldWidth;
5237 if (nbPtr->slant & SLANT_RIGHT) {
5238 NextPoint(x - nbPtr->tabHeight - nbPtr->yPad, y);
5239 NextPoint(x, yTop);
5240 NextPoint(x, yBot);
5241 } else {
5242 TopRight(x, y);
5243 BottomRight(x, yBot);
5244 }
5245 NextPoint(right, yBot);
5246 BottomRight(right, bottom);
5247 BottomLeft(left, bottom);
5248 break;
5249 }
5250 }
5251 EndPoint(pointArr[0].x, pointArr[0].y);
5252 for (i = 0; i < nPoints; i++) {
5253 WorldToScreen(nbPtr, pointArr[i].x, pointArr[i].y, &x, &y);
5254 pointArr[i].x = x;
5255 pointArr[i].y = y;
5256 }
5257 Draw3DFolder(nbPtr, tabPtr, drawable, nbPtr->side, pointArr, nPoints);
5258 DrawLabel(nbPtr, tabPtr, drawable);
5259 if (tabPtr->container != NULL) {
5260 XRectangle rect;
5261
5262 /* Draw a rectangle covering the spot representing the window */
5263 GetWindowRectangle(tabPtr, nbPtr->tkwin, FALSE, &rect);
5264 XFillRectangles(nbPtr->display, drawable, tabPtr->backGC,
5265 &rect, 1);
5266 }
5267}
5268
5269static void
5270DrawOuterBorders(nbPtr, drawable)
5271 Notebook *nbPtr;
5272 Drawable drawable;
5273{
5274 /*
5275 * Draw 3D border just inside of the focus highlight ring. We
5276 * draw the border even if the relief is flat so that any tabs
5277 * that hang over the edge will be clipped.
5278 */
5279 if (nbPtr->borderWidth > 0) {
5280 Blt_Draw3DRectangle(nbPtr->tkwin, drawable, nbPtr->border,
5281 nbPtr->highlightWidth, nbPtr->highlightWidth,
5282 Tk_Width(nbPtr->tkwin) - 2 * nbPtr->highlightWidth,
5283 Tk_Height(nbPtr->tkwin) - 2 * nbPtr->highlightWidth,
5284 nbPtr->borderWidth, nbPtr->relief);
5285 }
5286 /* Draw focus highlight ring. */
5287 if (nbPtr->highlightWidth > 0) {
5288 XColor *color;
5289 GC gc;
5290
5291 color = (nbPtr->flags & TNB_FOCUS)
5292 ? nbPtr->highlightColor : nbPtr->highlightBgColor;
5293 gc = Tk_GCForColor(color, drawable);
5294 Tk_DrawFocusHighlight(nbPtr->tkwin, gc, nbPtr->highlightWidth,
5295 drawable);
5296 }
5297}
5298
5299/*
5300 * ----------------------------------------------------------------------
5301 *
5302 * DisplayNotebook --
5303 *
5304 * This procedure is invoked to display the widget.
5305 *
5306 * Recomputes the layout of the widget if necessary. This is
5307 * necessary if the world coordinate system has changed.
5308 * Sets the vertical and horizontal scrollbars. This is done
5309 * here since the window width and height are needed for the
5310 * scrollbar calculations.
5311 *
5312 * Results:
5313 * None.
5314 *
5315 * Side effects:
5316 * The widget is redisplayed.
5317 *
5318 * ----------------------------------------------------------------------
5319 */
5320static void
5321DisplayNotebook(clientData)
5322 ClientData clientData; /* Information about widget. */
5323{
5324 Notebook *nbPtr = clientData;
5325 Pixmap drawable;
5326 int width, height;
5327
5328 nbPtr->flags &= ~TNB_REDRAW;
5329 if (nbPtr->tkwin == NULL) {
5330 return; /* Window has been destroyed. */
5331 }
5332 if (nbPtr->flags & TNB_LAYOUT) {
5333 ComputeLayout(nbPtr);
5334 nbPtr->flags &= ~TNB_LAYOUT;
5335 }
5336 if ((nbPtr->reqHeight == 0) || (nbPtr->reqWidth == 0)) {
5337 width = height = 0;
5338 if (nbPtr->side & SIDE_VERTICAL) {
5339 height = nbPtr->worldWidth;
5340 } else {
5341 width = nbPtr->worldWidth;
5342 }
5343 if (nbPtr->reqWidth > 0) {
5344 width = nbPtr->reqWidth;
5345 } else if (nbPtr->pageWidth > 0) {
5346 width = nbPtr->pageWidth;
5347 }
5348 if (nbPtr->reqHeight > 0) {
5349 height = nbPtr->reqHeight;
5350 } else if (nbPtr->pageHeight > 0) {
5351 height = nbPtr->pageHeight;
5352 }
5353 if (nbPtr->side & SIDE_VERTICAL) {
5354 width += nbPtr->pageTop + nbPtr->inset + nbPtr->inset2;
5355 height += nbPtr->inset + nbPtr->inset2;
5356 } else {
5357 height += nbPtr->pageTop + nbPtr->inset + nbPtr->inset2;
5358 width += nbPtr->inset + nbPtr->inset2;
5359 }
5360 if ((Tk_ReqWidth(nbPtr->tkwin) != width) ||
5361 (Tk_ReqHeight(nbPtr->tkwin) != height)) {
5362 Tk_GeometryRequest(nbPtr->tkwin, width, height);
5363 }
5364 }
5365 if (nbPtr->flags & TNB_SCROLL) {
5366 width = VPORTWIDTH(nbPtr);
5367 nbPtr->scrollOffset = Blt_AdjustViewport(nbPtr->scrollOffset,
5368 nbPtr->worldWidth, width, nbPtr->scrollUnits,
5369 BLT_SCROLL_MODE_CANVAS);
5370 if (nbPtr->scrollCmdPrefix != NULL) {
5371 Blt_UpdateScrollbar(nbPtr->interp, nbPtr->scrollCmdPrefix,
5372 (double)nbPtr->scrollOffset / nbPtr->worldWidth,
5373 (double)(nbPtr->scrollOffset + width) / nbPtr->worldWidth);
5374 }
5375 ComputeVisibleTabs(nbPtr);
5376 nbPtr->flags &= ~TNB_SCROLL;
5377 }
5378 if (!Tk_IsMapped(nbPtr->tkwin)) {
5379 return;
5380 }
5381 height = Tk_Height(nbPtr->tkwin);
5382 drawable = Tk_GetPixmap(nbPtr->display, Tk_WindowId(nbPtr->tkwin),
5383 Tk_Width(nbPtr->tkwin), Tk_Height(nbPtr->tkwin),
5384 Tk_Depth(nbPtr->tkwin));
5385
5386 /*
5387 * Clear the background either by tiling a pixmap or filling with
5388 * a solid color. Tiling takes precedence.
5389 */
5390 if (nbPtr->tile != NULL) {
5391 Blt_SetTileOrigin(nbPtr->tkwin, nbPtr->tile, 0, 0);
5392 Blt_TileRectangle(nbPtr->tkwin, drawable, nbPtr->tile, 0, 0,
5393 Tk_Width(nbPtr->tkwin), height);
5394 } else {
5395 Blt_Fill3DRectangle(nbPtr->tkwin, drawable, nbPtr->border, 0, 0,
5396 Tk_Width(nbPtr->tkwin), height, 0, TK_RELIEF_FLAT);
5397 }
5398
5399 if (nbPtr->nVisible > 0) {
5400 register int i;
5401 register Tab *tabPtr;
5402 Blt_ChainLink *linkPtr;
5403
5404 linkPtr = nbPtr->startPtr->linkPtr;
5405 for (i = 0; i < Blt_ChainGetLength(nbPtr->chainPtr); i++) {
5406 linkPtr = Blt_ChainPrevLink(linkPtr);
5407 if (linkPtr == NULL) {
5408 linkPtr = Blt_ChainLastLink(nbPtr->chainPtr);
5409 }
5410 tabPtr = Blt_ChainGetValue(linkPtr);
5411 if ((tabPtr != nbPtr->selectPtr) &&
5412 (tabPtr->flags & TAB_VISIBLE)) {
5413 DrawFolder(nbPtr, tabPtr, drawable);
5414 }
5415 }
5416 DrawFolder(nbPtr, nbPtr->selectPtr, drawable);
5417 if (nbPtr->tearoff) {
5418 DrawPerforation(nbPtr, nbPtr->selectPtr, drawable);
5419 }
5420
5421 if ((nbPtr->selectPtr->tkwin != NULL) &&
5422 (nbPtr->selectPtr->container == NULL)) {
5423 XRectangle rect;
5424
5425 GetWindowRectangle(nbPtr->selectPtr, nbPtr->tkwin, FALSE, &rect);
5426 ArrangeWindow(nbPtr->selectPtr->tkwin, &rect, 0);
5427 }
5428 }
5429 DrawOuterBorders(nbPtr, drawable);
5430 XCopyArea(nbPtr->display, drawable, Tk_WindowId(nbPtr->tkwin),
5431 nbPtr->highlightGC, 0, 0, Tk_Width(nbPtr->tkwin), height, 0, 0);
5432 Tk_FreePixmap(nbPtr->display, drawable);
5433}
5434
5435/*
5436 * From the left edge:
5437 *
5438 * |a|b|c|d|e| f |d|e|g|h| i |h|g|e|d|f| j |e|d|c|b|a|
5439 *
5440 * a. highlight ring
5441 * b. notebook 3D border
5442 * c. outer gap
5443 * d. page border
5444 * e. page corner
5445 * f. gap + select pad
5446 * g. label pad x (worldX)
5447 * h. internal pad x
5448 * i. label width
5449 * j. rest of page width
5450 *
5451 * worldX, worldY
5452 * |
5453 * |
5454 * * 4+ . . +5
5455 * 3+ +6
5456 * . .
5457 * . .
5458 * 1+. . .2+ +7 . . . .+8
5459 * 0+ +9
5460 * . .
5461 * . .
5462 *13+ +10
5463 * 12+-------------------------+11
5464 *
5465 */
5466static void
5467DisplayTearoff(clientData)
5468 ClientData clientData;
5469{
5470 Notebook *nbPtr;
5471 Tab *tabPtr;
5472 Drawable drawable;
5473 XPoint pointArr[16];
5474 XPoint *pointPtr;
5475 int width, height;
5476 int left, bottom, right, top;
5477 int x, y;
5478 int nPoints;
5479 Tk_Window tkwin;
5480 Tk_Window parent;
5481 XRectangle rect;
5482
5483 tabPtr = clientData;
5484 if (tabPtr == NULL) {
5485 return;
5486 }
5487 tabPtr->flags &= ~TAB_REDRAW;
5488 nbPtr = tabPtr->nbPtr;
5489 if (nbPtr->tkwin == NULL) {
5490 return;
5491 }
5492 tkwin = tabPtr->container;
5493 drawable = Tk_WindowId(tkwin);
5494
5495 /*
5496 * Clear the background either by tiling a pixmap or filling with
5497 * a solid color. Tiling takes precedence.
5498 */
5499 if (nbPtr->tile != NULL) {
5500 Blt_SetTileOrigin(tkwin, nbPtr->tile, 0, 0);
5501 Blt_TileRectangle(tkwin, drawable, nbPtr->tile, 0, 0, Tk_Width(tkwin),
5502 Tk_Height(tkwin));
5503 } else {
5504 Blt_Fill3DRectangle(tkwin, drawable, nbPtr->border, 0, 0,
5505 Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
5506 }
5507
5508 width = Tk_Width(tkwin) - 2 * nbPtr->inset;
5509 height = Tk_Height(tkwin) - 2 * nbPtr->inset;
5510 x = nbPtr->inset + nbPtr->gap + nbPtr->corner;
5511 y = nbPtr->inset;
5512
5513 left = nbPtr->inset;
5514 right = nbPtr->inset + width;
5515 top = nbPtr->inset + nbPtr->corner + nbPtr->xSelectPad;
5516 bottom = nbPtr->inset + height;
5517
5518 /*
5519 * worldX, worldY
5520 * |
5521 * * 4+ . . +5
5522 * 3+ +6
5523 * . .
5524 * . .
5525 * 1+. . .2+ +7 . . . .+8
5526 * 0+ +9
5527 * . .
5528 * . .
5529 *13+ +10
5530 * 12+-------------------------+11
5531 */
5532
5533 nPoints = 0;
5534 pointPtr = pointArr;
5535
5536 TopLeft(left, top);
5537 NextPoint(x, top);
5538 TopLeft(x, y);
5539 x += tabPtr->worldWidth;
5540 TopRight(x, y);
5541 NextPoint(x, top);
5542 TopRight(right, top);
5543 BottomRight(right, bottom);
5544 BottomLeft(left, bottom);
5545 EndPoint(pointArr[0].x, pointArr[0].y);
5546 Draw3DFolder(nbPtr, tabPtr, drawable, SIDE_TOP, pointArr, nPoints);
5547
5548 parent = (tabPtr->container == NULL) ? nbPtr->tkwin : tabPtr->container;
5549 GetWindowRectangle(tabPtr, parent, TRUE, &rect);
5550 ArrangeWindow(tabPtr->tkwin, &rect, TRUE);
5551
5552 /* Draw 3D border. */
5553 if ((nbPtr->borderWidth > 0) && (nbPtr->relief != TK_RELIEF_FLAT)) {
5554 Blt_Draw3DRectangle(tkwin, drawable, nbPtr->border, 0, 0,
5555 Tk_Width(tkwin), Tk_Height(tkwin), nbPtr->borderWidth,
5556 nbPtr->relief);
5557 }
5558}
5559
5560/*
5561 * --------------------------------------------------------------
5562 *
5563 * NotebookCmd --
5564 *
5565 * This procedure is invoked to process the "notebook" command.
5566 * See the user documentation for details on what it does.
5567 *
5568 * Results:
5569 * A standard Tcl result.
5570 *
5571 * Side effects:
5572 * See the user documentation.
5573 *
5574 * --------------------------------------------------------------
5575 */
5576static Blt_OpSpec notebookOps[] =
5577{
5578 {"activate", 1, (Blt_Op)ActivateOp, 3, 3, "index",},
5579 {"bind", 1, (Blt_Op)BindOp, 2, 5, "index ?sequence command?",},
5580 {"cget", 2, (Blt_Op)CgetOp, 3, 3, "option",},
5581 {"configure", 2, (Blt_Op)ConfigureOp, 2, 0, "?option value?...",},
5582 {"delete", 1, (Blt_Op)DeleteOp, 2, 0, "first ?last?",},
5583 {"focus", 1, (Blt_Op)FocusOp, 3, 3, "index",},
5584 {"highlight", 1, (Blt_Op)ActivateOp, 3, 3, "index",},
5585 {"id", 2, (Blt_Op)IdOp, 3, 3, "index",},
5586 {"index", 3, (Blt_Op)IndexOp, 3, 5, "string",},
5587 {"insert", 3, (Blt_Op)InsertOp, 3, 0, "index ?option value?",},
5588 {"invoke", 3, (Blt_Op)InvokeOp, 3, 3, "index",},
5589 {"move", 1, (Blt_Op)MoveOp, 5, 5, "name after|before index",},
5590 {"nearest", 1, (Blt_Op)NearestOp, 4, 4, "x y",},
5591 {"perforation", 1, (Blt_Op)PerforationOp, 2, 0, "args",},
5592 {"scan", 2, (Blt_Op)ScanOp, 5, 5, "dragto|mark x y",},
5593 {"see", 3, (Blt_Op)SeeOp, 3, 3, "index",},
5594 {"select", 3, (Blt_Op)SelectOp, 3, 3, "index",},
5595 {"size", 2, (Blt_Op)SizeOp, 2, 2, "",},
5596 {"tab", 1, (Blt_Op)TabOp, 2, 0, "oper args",},
5597 {"view", 1, (Blt_Op)ViewOp, 2, 5,
5598 "?moveto fract? ?scroll number what?",},
5599};
5600
5601static int nNotebookOps = sizeof(notebookOps) / sizeof(Blt_OpSpec);
5602
5603static int
5604NotebookInstCmd(clientData, interp, argc, argv)
5605 ClientData clientData; /* Information about the widget. */
5606 Tcl_Interp *interp; /* Interpreter to report errors back to. */
5607 int argc; /* Number of arguments. */
5608 char **argv; /* Vector of argument strings. */
5609{
5610 Blt_Op proc;
5611 Notebook *nbPtr = clientData;
5612 int result;
5613
5614 proc = Blt_GetOp(interp, nNotebookOps, notebookOps, BLT_OP_ARG1, argc,
5615 argv, 0);
5616 if (proc == NULL) {
5617 return TCL_ERROR;
5618 }
5619 Tcl_Preserve(nbPtr);
5620 result = (*proc) (nbPtr, interp, argc, argv);
5621 Tcl_Release(nbPtr);
5622 return result;
5623}
5624
5625/*
5626 *----------------------------------------------------------------------
5627 *
5628 * NotebookInstDeletedCmd --
5629 *
5630 * This procedure can be called if the window was destroyed
5631 * (tkwin will be NULL) and the command was deleted
5632 * automatically. In this case, we need to do nothing.
5633 *
5634 * Otherwise this routine was called because the command was
5635 * deleted. Then we need to clean-up and destroy the widget.
5636 *
5637 * Results:
5638 * None.
5639 *
5640 * Side Effects:
5641 * The widget is destroyed.
5642 *
5643 *----------------------------------------------------------------------
5644 */
5645static void
5646NotebookInstDeletedCmd(clientData)
5647 ClientData clientData; /* Pointer to widget record for widget. */
5648{
5649 Notebook *nbPtr = clientData;
5650
5651 if (nbPtr->tkwin != NULL) {
5652 Tk_Window tkwin;
5653
5654 tkwin = nbPtr->tkwin;
5655 nbPtr->tkwin = NULL;
5656 Tk_DestroyWindow(tkwin);
5657#ifdef ITCL_NAMESPACES
5658 Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
5659#endif /* ITCL_NAMESPACES */
5660 }
5661}
5662
5663/*
5664 * ------------------------------------------------------------------------
5665 *
5666 * NotebookCmd --
5667 *
5668 * This procedure is invoked to process the Tcl command that
5669 * corresponds to a widget managed by this module. See the user
5670 * documentation for details on what it does.
5671 *
5672 * Results:
5673 * A standard Tcl result.
5674 *
5675 * Side Effects:
5676 * See the user documentation.
5677 *
5678 * -----------------------------------------------------------------------
5679 */
5680/* ARGSUSED */
5681static int
5682NotebookCmd(clientData, interp, argc, argv)
5683 ClientData clientData; /* Main window associated with interpreter. */
5684 Tcl_Interp *interp; /* Current interpreter. */
5685 int argc; /* Number of arguments. */
5686 char **argv; /* Argument strings. */
5687{
5688 Notebook *nbPtr;
5689 Tk_Window tkwin;
5690 unsigned int mask;
5691 Tcl_CmdInfo cmdInfo;
5692
5693 if (argc < 2) {
5694 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
5695 " pathName ?option value?...\"", (char *)NULL);
5696 return TCL_ERROR;
5697 }
5698 tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1],
5699 (char *)NULL);
5700 if (tkwin == NULL) {
5701 return TCL_ERROR;
5702 }
5703 nbPtr = CreateNotebook(interp, tkwin);
5704 if (ConfigureNotebook(interp, nbPtr, argc - 2, argv + 2, 0) != TCL_OK) {
5705 Tk_DestroyWindow(nbPtr->tkwin);
5706 return TCL_ERROR;
5707 }
5708 mask = (ExposureMask | StructureNotifyMask | FocusChangeMask);
5709 Tk_CreateEventHandler(tkwin, mask, NotebookEventProc, nbPtr);
5710 nbPtr->cmdToken = Tcl_CreateCommand(interp, argv[1], NotebookInstCmd,
5711 nbPtr, NotebookInstDeletedCmd);
5712#ifdef ITCL_NAMESPACES
5713 Itk_SetWidgetCommand(nbPtr->tkwin, nbPtr->cmdToken);
5714#endif
5715
5716 /*
5717 * Try to invoke a procedure to initialize various bindings on
5718 * tabs. Source the file containing the procedure now if the
5719 * procedure isn't currently defined. We deferred this to now so
5720 * that the user could set the variable "blt_library" within the
5721 * script.
5722 */
5723 if (!Tcl_GetCommandInfo(interp, "blt::TabnotebookInit", &cmdInfo)) {
5724 static char initCmd[] =
5725 "source [file join $blt_library tabnotebook.tcl]";
5726
5727 if (Tcl_GlobalEval(interp, initCmd) != TCL_OK) {
5728 char info[200];
5729
5730 sprintf(info, "\n (while loading bindings for %s)", argv[0]);
5731 Tcl_AddErrorInfo(interp, info);
5732 Tk_DestroyWindow(nbPtr->tkwin);
5733 return TCL_ERROR;
5734 }
5735 }
5736 if (Tcl_VarEval(interp, "blt::TabnotebookInit ", argv[1], (char *)NULL)
5737 != TCL_OK) {
5738 Tk_DestroyWindow(nbPtr->tkwin);
5739 return TCL_ERROR;
5740 }
5741 Tcl_SetResult(interp, Tk_PathName(nbPtr->tkwin), TCL_VOLATILE);
5742 return TCL_OK;
5743}
5744
5745int
5746Blt_TabnotebookInit(interp)
5747 Tcl_Interp *interp;
5748{
5749 static Blt_CmdSpec cmdSpec =
5750 {
5751 "tabnotebook", NotebookCmd,
5752 };
5753
5754 if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
5755 return TCL_ERROR;
5756 }
5757 return TCL_OK;
5758}
5759
5760#endif /* NO_TABNOTEBOOK */
Note: See TracBrowser for help on using the repository browser.