/* * bltTabnotebook.c -- * * This module implements a tab notebook widget for the BLT toolkit. * * Copyright 1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies or any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. * * Tabnotebook widget created by George A. Howlett (gah@bell-labs.com) * */ #include "bltInt.h" #ifndef NO_TABNOTEBOOK #include "bltBind.h" #include "bltChain.h" #include "bltHash.h" #include "bltTile.h" #if (TK_MAJOR_VERSION == 4) #define TK_REPARENTED 0x2000 #endif #define INVALID_FAIL 0 #define INVALID_OK 1 /* * The macro below is used to modify a "char" value (e.g. by casting * it to an unsigned character) so that it can be used safely with * macros such as isspace. */ #define CLAMP(val,low,hi) \ (((val) < (low)) ? (low) : ((val) > (hi)) ? (hi) : (val)) #define GAP 3 #define SELECT_PADX 4 #define SELECT_PADY 2 #define OUTER_PAD 2 #define LABEL_PAD 1 #define LABEL_PADX 2 #define LABEL_PADY 2 #define IMAGE_PAD 1 #define CORNER_OFFSET 3 #define TAB_SCROLL_OFFSET 10 #define SLANT_NONE 0 #define SLANT_LEFT 1 #define SLANT_RIGHT 2 #define SLANT_BOTH (SLANT_LEFT | SLANT_RIGHT) #define END (-1) #define ODD(x) ((x) | 0x01) #define TABWIDTH(s, t) \ ((s)->side & SIDE_VERTICAL) ? (t)->height : (t)->width) #define TABHEIGHT(s, t) \ ((s)->side & SIDE_VERTICAL) ? (t)->height : (t)->width) #define VPORTWIDTH(s) \ (((s)->side & SIDE_HORIZONTAL) ? (Tk_Width((s)->tkwin) - 2 * (s)->inset) : \ (Tk_Height((s)->tkwin) - 2 * (s)->inset)) #define VPORTHEIGHT(s) \ (((s)->side & SIDE_VERTICAL) ? (Tk_Width((s)->tkwin) - 2 * (s)->inset) : \ (Tk_Height((s)->tkwin) - 2 * (s)->inset)) #define GETATTR(t,attr) \ (((t)->attr != NULL) ? (t)->attr : (t)->nbPtr->defTabStyle.attr) /* * ---------------------------------------------------------------------------- * * Internal widget flags: * * TNB_LAYOUT The layout of the widget needs to be * recomputed. * * TNB_REDRAW A redraw request is pending for the widget. * * TNB_SCROLL A scroll request is pending. * * TNB_FOCUS The widget is receiving keyboard events. * Draw the focus highlight border around the * widget. * * TNB_MULTIPLE_TIER Notebook is using multiple tiers. * * TNB_STATIC Notebook does not scroll. * * --------------------------------------------------------------------------- */ #define TNB_LAYOUT (1<<0) #define TNB_REDRAW (1<<1) #define TNB_SCROLL (1<<2) #define TNB_FOCUS (1<<4) #define TNB_STATIC (1<<8) #define TNB_MULTIPLE_TIER (1<<9) #define PERFORATION_ACTIVE (1<<10) #define SIDE_TOP (1<<0) #define SIDE_RIGHT (1<<1) #define SIDE_LEFT (1<<2) #define SIDE_BOTTOM (1<<3) #define SIDE_VERTICAL (SIDE_LEFT | SIDE_RIGHT) #define SIDE_HORIZONTAL (SIDE_TOP | SIDE_BOTTOM) #define TAB_LABEL (ClientData)0 #define TAB_PERFORATION (ClientData)1 #define DEF_TNB_ACTIVE_BACKGROUND RGB_GREY90 #define DEF_TNB_ACTIVE_BG_MONO STD_ACTIVE_BG_MONO #define DEF_TNB_ACTIVE_FOREGROUND STD_ACTIVE_FOREGROUND #define DEF_TNB_ACTIVE_FG_MONO STD_ACTIVE_FG_MONO #define DEF_TNB_BG_MONO STD_NORMAL_BG_MONO #define DEF_TNB_BACKGROUND STD_NORMAL_BACKGROUND #define DEF_TNB_BORDERWIDTH "1" #define DEF_TNB_COMMAND (char *)NULL #define DEF_TNB_CURSOR (char *)NULL #define DEF_TNB_DASHES "1" #define DEF_TNB_FOREGROUND STD_NORMAL_FOREGROUND #define DEF_TNB_FG_MONO STD_NORMAL_FG_MONO #define DEF_TNB_FONT STD_FONT #define DEF_TNB_GAP "3" #define DEF_TNB_HEIGHT "0" #define DEF_TNB_HIGHLIGHT_BACKGROUND STD_NORMAL_BACKGROUND #define DEF_TNB_HIGHLIGHT_BG_MONO STD_NORMAL_BG_MONO #define DEF_TNB_HIGHLIGHT_COLOR RGB_BLACK #define DEF_TNB_HIGHLIGHT_WIDTH "2" #define DEF_TNB_NORMAL_BACKGROUND STD_NORMAL_BACKGROUND #define DEF_TNB_NORMAL_FG_MONO STD_ACTIVE_FG_MONO #define DEF_TNB_OUTER_PAD "3" #define DEF_TNB_RELIEF "sunken" #define DEF_TNB_ROTATE "0.0" #define DEF_TNB_SCROLL_INCREMENT "0" #define DEF_TNB_SELECT_BACKGROUND STD_NORMAL_BACKGROUND #define DEF_TNB_SELECT_BG_MONO STD_SELECT_BG_MONO #define DEF_TNB_SELECT_BORDERWIDTH "1" #define DEF_TNB_SELECT_CMD (char *)NULL #define DEF_TNB_SELECT_FOREGROUND STD_SELECT_FOREGROUND #define DEF_TNB_SELECT_FG_MONO STD_SELECT_FG_MONO #define DEF_TNB_SELECT_MODE "multiple" #define DEF_TNB_SELECT_RELIEF "raised" #define DEF_TNB_SELECT_PAD "5" #define DEF_TNB_SHADOW_COLOR RGB_BLACK #define DEF_TNB_SIDE "top" #define DEF_TNB_SLANT "none" #define DEF_TNB_TAB_BACKGROUND RGB_GREY82 #define DEF_TNB_TAB_BG_MONO STD_SELECT_BG_MONO #define DEF_TNB_TAB_RELIEF "raised" #define DEF_TNB_TAKE_FOCUS "1" #define DEF_TNB_TEXT_COLOR STD_NORMAL_FOREGROUND #define DEF_TNB_TEXT_MONO STD_NORMAL_FG_MONO #define DEF_TNB_TEXT_SIDE "left" #define DEF_TNB_TIERS "1" #define DEF_TNB_TILE (char *)NULL #define DEF_TNB_WIDTH "0" #define DEF_TNB_SAME_WIDTH "yes" #define DEF_TNB_TEAROFF "yes" #define DEF_TNB_PAGE_WIDTH "0" #define DEF_TNB_PAGE_HEIGHT "0" #define DEF_TAB_ACTIVE_BG (char *)NULL #define DEF_TAB_ACTIVE_FG (char *)NULL #define DEF_TAB_ANCHOR "center" #define DEF_TAB_BG (char *)NULL #define DEF_TAB_COMMAND (char *)NULL #define DEF_TAB_DATA (char *)NULL #define DEF_TAB_FG (char *)NULL #define DEF_TAB_FILL "none" #define DEF_TAB_FONT (char *)NULL #define DEF_TAB_HEIGHT "0" #define DEF_TAB_IMAGE (char *)NULL #define DEF_TAB_IPAD "0" #define DEF_TAB_PAD "3" #define DEF_TAB_PERF_COMMAND (char *)NULL #define DEF_TAB_SELECT_BG (char *)NULL #define DEF_TAB_SELECT_BORDERWIDTH "1" #define DEF_TAB_SELECT_CMD (char *)NULL #define DEF_TAB_SELECT_FG (char *)NULL #define DEF_TAB_SHADOW (char *)NULL #define DEF_TAB_STATE "normal" #define DEF_TAB_STIPPLE "BLT" #define DEF_TAB_BIND_TAGS "all" #define DEF_TAB_TEXT (char *)NULL #define DEF_TAB_VISUAL (char *)NULL #define DEF_TAB_WIDTH "0" #define DEF_TAB_WINDOW (char *)NULL typedef struct NotebookStruct Notebook; static void EmbeddedWidgetGeometryProc _ANSI_ARGS_((ClientData, Tk_Window)); static void EmbeddedWidgetCustodyProc _ANSI_ARGS_((ClientData, Tk_Window)); static Tk_GeomMgr tabMgrInfo = { "notebook", /* Name of geometry manager used by winfo */ EmbeddedWidgetGeometryProc, /* Procedure to for new geometry requests */ EmbeddedWidgetCustodyProc, /* Procedure when window is taken away */ }; extern Tk_CustomOption bltDashesOption; extern Tk_CustomOption bltFillOption; extern Tk_CustomOption bltDistanceOption; extern Tk_CustomOption bltPositiveDistanceOption; extern Tk_CustomOption bltPositiveCountOption; extern Tk_CustomOption bltListOption; extern Tk_CustomOption bltPadOption; extern Tk_CustomOption bltShadowOption; extern Tk_CustomOption bltStateOption; extern Tk_CustomOption bltTileOption; extern Tk_CustomOption bltUidOption; static int StringToImage _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int offset)); static char *ImageToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtrPtr)); static int StringToWindow _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int offset)); static char *WindowToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtrPtr)); static int StringToSide _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int offset)); static char *SideToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtrPtr)); static int StringToSlant _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int offset)); static char *SlantToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtrPtr)); /* * Contains a pointer to the widget that's currently being configured. * This is used in the custom configuration parse routine for images. */ static Notebook *lastNotebookInstance; static Tk_CustomOption imageOption = { StringToImage, ImageToString, (ClientData)&lastNotebookInstance, }; static Tk_CustomOption sideOption = { StringToSide, SideToString, (ClientData)0, }; static Tk_CustomOption windowOption = { StringToWindow, WindowToString, (ClientData)0, }; static Tk_CustomOption slantOption = { StringToSlant, SlantToString, (ClientData)0, }; /* * TabImage -- * * When multiple instances of an image are displayed in the * same widget, this can be inefficient in terms of both memory * and time. We only need one instance of each image, regardless * of number of times we use it. And searching/deleting instances * can be very slow as the list gets large. * * The workaround, employed below, is to maintain a hash table of * images that maintains a reference count for each image. */ typedef struct TabImageStruct { int refCount; /* Reference counter for this image. */ Tk_Image tkImage; /* The Tk image being cached. */ int width, height; /* Dimensions of the cached image. */ Blt_HashEntry *hashPtr; /* Hash table pointer to the image. */ } *TabImage; #define ImageHeight(image) ((image)->height) #define ImageWidth(image) ((image)->width) #define ImageBits(image) ((image)->tkImage) #define TAB_VISIBLE (1<<0) #define TAB_REDRAW (1<<2) typedef struct { char *name; /* Identifier for tab entry */ int state; /* State of the tab: Disabled, active, or * normal. */ unsigned int flags; int tier; /* Index of tier [1..numTiers] containing * this tab. */ int worldX, worldY; /* Position of the tab in world coordinates. */ int worldWidth, worldHeight;/* Dimensions of the tab, corrected for * orientation (-side). It includes the * border, padding, label, etc. */ int screenX, screenY; short int screenWidth, screenHeight; /* */ Notebook *nbPtr; /* Notebook that includes this * tab. Needed for callbacks can pass * only a tab pointer. */ Blt_Uid tags; /* * Tab label: */ Blt_Uid text; /* String displayed as the tab's label. */ TabImage image; /* Image displayed as the label. */ short int textWidth, textHeight; short int labelWidth, labelHeight; Blt_Pad iPadX, iPadY; /* Internal padding around the text */ Tk_Font font; /* * Normal: */ XColor *textColor; /* Text color */ Tk_3DBorder border; /* Background color and border for tab.*/ /* * Selected: Tab is currently selected. */ XColor *selColor; /* Selected text color */ Tk_3DBorder selBorder; /* 3D border of selected folder. */ /* * Active: Mouse passes over the tab. */ Tk_3DBorder activeBorder; /* Active background color. */ XColor *activeFgColor; /* Active text color */ Shadow shadow; Pixmap stipple; /* Stipple for outline of embedded window * when torn off. */ /* * Embedded widget information: */ Tk_Window tkwin; /* Widget to be mapped when the tab is * selected. If NULL, don't make * space for the page. */ int reqWidth, reqHeight; /* If non-zero, overrides the * requested dimensions of the * embedded widget. */ Tk_Window container; /* The window containing the embedded * widget. Does not necessarily have * to be the parent. */ Tk_Anchor anchor; /* Anchor: indicates how the embedded * widget is positioned within the * extra space on the page. */ Blt_Pad padX, padY; /* Padding around embedded widget */ int fill; /* Indicates how the window should * fill the page. */ /* * Auxillary information: */ Blt_Uid command; /* Command (malloc-ed) invoked when the tab * is selected */ Blt_Uid data; /* This value isn't used in C code. * It may be used by clients in Tcl bindings * to associate extra data (other than the * label or name) with the tab. */ Blt_ChainLink *linkPtr; /* Pointer to where the tab resides in the * list of tabs. */ Blt_Uid perfCommand; /* Command (malloc-ed) invoked when the tab * is selected */ GC textGC; GC backGC; Blt_Tile tile; } Tab; static Tk_ConfigSpec tabConfigSpecs[] = { {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "ActiveBackground", DEF_TAB_ACTIVE_BG, Tk_Offset(Tab, activeBorder), TK_CONFIG_NULL_OK}, {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "ActiveForeground", DEF_TAB_ACTIVE_FG, Tk_Offset(Tab, activeFgColor), TK_CONFIG_NULL_OK}, {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", DEF_TAB_ANCHOR, Tk_Offset(Tab, anchor), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_BORDER, "-background", "background", "Background", DEF_TAB_BG, Tk_Offset(Tab, border), TK_CONFIG_NULL_OK}, {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0}, {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags", DEF_TAB_BIND_TAGS, Tk_Offset(Tab, tags), TK_CONFIG_NULL_OK, &bltUidOption}, {TK_CONFIG_CUSTOM, "-command", "command", "Command", DEF_TAB_COMMAND, Tk_Offset(Tab, command), TK_CONFIG_NULL_OK, &bltUidOption}, {TK_CONFIG_CUSTOM, "-data", "data", "data", DEF_TAB_DATA, Tk_Offset(Tab, data), TK_CONFIG_NULL_OK, &bltUidOption}, {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0}, {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill", DEF_TAB_FILL, Tk_Offset(Tab, fill), TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption}, {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground", DEF_TAB_FG, Tk_Offset(Tab, textColor), TK_CONFIG_NULL_OK}, {TK_CONFIG_FONT, "-font", "font", "Font", DEF_TAB_FONT, Tk_Offset(Tab, font), 0}, {TK_CONFIG_CUSTOM, "-image", "image", "image", DEF_TAB_IMAGE, Tk_Offset(Tab, image), TK_CONFIG_NULL_OK, &imageOption}, {TK_CONFIG_CUSTOM, "-ipadx", "iPadX", "PadX", DEF_TAB_IPAD, Tk_Offset(Tab, iPadX), TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption}, {TK_CONFIG_CUSTOM, "-ipady", "iPadY", "PadY", DEF_TAB_IPAD, Tk_Offset(Tab, iPadY), TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption}, {TK_CONFIG_CUSTOM, "-padx", "padX", "PadX", DEF_TAB_PAD, Tk_Offset(Tab, padX), 0, &bltPadOption}, {TK_CONFIG_CUSTOM, "-pady", "padY", "PadY", DEF_TAB_PAD, Tk_Offset(Tab, padY), 0, &bltPadOption}, {TK_CONFIG_CUSTOM, "-perforationcommand", "perforationcommand", "PerforationCommand", DEF_TAB_PERF_COMMAND, Tk_Offset(Tab, perfCommand), TK_CONFIG_NULL_OK, &bltUidOption}, {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Background", DEF_TAB_SELECT_BG, Tk_Offset(Tab, selBorder), TK_CONFIG_NULL_OK}, {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground", DEF_TAB_SELECT_FG, Tk_Offset(Tab, selColor), TK_CONFIG_NULL_OK}, {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow", DEF_TAB_SHADOW, Tk_Offset(Tab, shadow), TK_CONFIG_NULL_OK, &bltShadowOption}, {TK_CONFIG_CUSTOM, "-state", "state", "State", DEF_TAB_STATE, Tk_Offset(Tab, state), TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption}, {TK_CONFIG_BITMAP, "-stipple", "stipple", "Stipple", DEF_TAB_STIPPLE, Tk_Offset(Tab, stipple), 0}, {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile", (char *)NULL, Tk_Offset(Tab, tile), TK_CONFIG_NULL_OK, &bltTileOption}, {TK_CONFIG_CUSTOM, "-text", "Text", "Text", DEF_TAB_TEXT, Tk_Offset(Tab, text), TK_CONFIG_NULL_OK, &bltUidOption}, {TK_CONFIG_CUSTOM, "-window", "window", "Window", DEF_TAB_WINDOW, Tk_Offset(Tab, tkwin), TK_CONFIG_NULL_OK, &windowOption}, {TK_CONFIG_CUSTOM, "-windowheight", "windowHeight", "WindowHeight", DEF_TAB_HEIGHT, Tk_Offset(Tab, reqHeight), TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_CUSTOM, "-windowwidth", "windowWidth", "WindowWidth", DEF_TAB_WIDTH, Tk_Offset(Tab, reqWidth), TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL, 0, 0} }; /* * TabAttributes -- */ typedef struct { Tk_Window tkwin; /* Default window to map pages. */ int reqWidth, reqHeight; /* Requested tab size. */ int constWidth; int borderWidth; /* Width of 3D border around the tab's * label. */ int pad; /* Extra padding of a tab entry */ XColor *activeFgColor; /* Active foreground. */ Tk_3DBorder activeBorder; /* Active background. */ XColor *selColor; /* Selected foreground. */ Tk_Font font; XColor *textColor; Tk_3DBorder border; /* Normal background. */ Tk_3DBorder selBorder; /* Selected background. */ Blt_Dashes dashes; GC normalGC, activeGC; int relief; char *command; char *perfCommand; /* Command (malloc-ed) invoked when the tab * is selected */ double rotate; int textSide; } TabAttributes; struct NotebookStruct { Tk_Window tkwin; /* Window that embodies the widget. * NULL means that the window has been * destroyed but the data structures * haven't yet been cleaned up.*/ Display *display; /* Display containing widget; needed, * among other things, to release * resources after tkwin has already * gone away. */ Tcl_Interp *interp; /* Interpreter associated with widget. */ Tcl_Command cmdToken; /* Token for widget's command. */ unsigned int flags; /* For bitfield definitions, see below */ int inset; /* Total width of all borders, including * traversal highlight and 3-D border. * Indicates how much interior stuff must * be offset from outside edges to leave * room for borders. */ int inset2; /* Total width of 3-D folder border + corner, * Indicates how much interior stuff must * be offset from outside edges of folder.*/ int yPad; /* Extra offset for selected tab. Only * for single tiers. */ int pageTop; /* Offset from top of notebook to the * start of the page. */ Tk_Cursor cursor; /* X Cursor */ Tk_3DBorder border; /* 3D border surrounding the window. */ int borderWidth; /* Width of 3D border. */ int relief; /* 3D border relief. */ XColor *shadowColor; /* Shadow color around folder. */ /* * Focus highlight ring */ int highlightWidth; /* Width in pixels of highlight to draw * around widget when it has the focus. * <= 0 means don't draw a highlight. */ XColor *highlightBgColor; /* Color for drawing traversal highlight * area when highlight is off. */ XColor *highlightColor; /* Color for drawing traversal highlight. */ GC highlightGC; /* GC for focus highlight. */ char *takeFocus; /* Says whether to select this widget during * tab traveral operations. This value isn't * used in C code, but for the widget's Tcl * bindings. */ int side; /* Orientation of the notebook: either * SIDE_LEFT, SIDE_RIGHT, SIDE_TOP, or * SIDE_BOTTOM. */ int slant; int overlap; int gap; int tabWidth, tabHeight; int xSelectPad, ySelectPad; /* Padding around label of the selected tab. */ int outerPad; /* Padding around the exterior of the notebook * and folder. */ TabAttributes defTabStyle; /* Global attribute information specific to * tabs. */ Blt_Tile tile; int reqWidth, reqHeight; /* Requested dimensions of the notebook * window. */ int pageWidth, pageHeight; /* Dimensions of a page in the folder. */ int reqPageWidth, reqPageHeight; /* Requested dimensions of a page. */ int lastX, lastY; /* * Scrolling information: */ int worldWidth; int scrollOffset; /* Offset of viewport in world coordinates. */ char *scrollCmdPrefix; /* Command strings to control scrollbar.*/ int scrollUnits; /* Smallest unit of scrolling for tabs. */ /* * Scanning information: */ int scanAnchor; /* Scan anchor in screen coordinates. */ int scanOffset; /* Offset of the start of the scan in world * coordinates.*/ int corner; /* Number of pixels to offset next point * when drawing corners of the folder. */ int reqTiers; /* Requested number of tiers. Zero means to * dynamically scroll if there are too many * tabs to be display on a single tier. */ int nTiers; /* Actual number of tiers. */ Blt_HashTable imageTable; Tab *selectPtr; /* The currently selected tab. * (i.e. its page is displayed). */ Tab *activePtr; /* Tab last located under the pointer. * It is displayed with its active * foreground/background colors. */ Tab *focusPtr; /* Tab currently receiving focus. */ Tab *startPtr; /* The first tab on the first tier. */ Blt_Chain *chainPtr; /* List of tab entries. Used to * arrange placement of tabs. */ Blt_HashTable tabTable; /* Hash table of tab entries. Used for * lookups of tabs by name. */ int nextId; int nVisible; /* Number of tabs that are currently visible * in the view port. */ Blt_BindTable bindTable; /* Tab binding information */ Blt_HashTable tagTable; /* Table of bind tags. */ int tearoff; }; static Tk_ConfigSpec configSpecs[] = { {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "activeBackground", DEF_TNB_ACTIVE_BACKGROUND, Tk_Offset(Notebook, defTabStyle.activeBorder), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "activeBackground", DEF_TNB_ACTIVE_BG_MONO, Tk_Offset(Notebook, defTabStyle.activeBorder), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "activeForeground", DEF_TNB_ACTIVE_FOREGROUND, Tk_Offset(Notebook, defTabStyle.activeFgColor), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "activeForeground", DEF_TNB_ACTIVE_FG_MONO, Tk_Offset(Notebook, defTabStyle.activeFgColor), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_BORDER, "-background", "background", "Background", DEF_TNB_BG_MONO, Tk_Offset(Notebook, border), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_BORDER, "-background", "background", "Background", DEF_TNB_BACKGROUND, Tk_Offset(Notebook, border), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0}, {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0}, {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor", DEF_TNB_CURSOR, Tk_Offset(Notebook, cursor), TK_CONFIG_NULL_OK}, {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth", DEF_TNB_BORDERWIDTH, Tk_Offset(Notebook, borderWidth), TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes", DEF_TNB_DASHES, Tk_Offset(Notebook, defTabStyle.dashes), TK_CONFIG_NULL_OK, &bltDashesOption}, {TK_CONFIG_SYNONYM, "-fg", "tabForeground", (char *)NULL, (char *)NULL, 0, 0}, {TK_CONFIG_FONT, "-font", "font", "Font", DEF_TNB_FONT, Tk_Offset(Notebook, defTabStyle.font), 0}, {TK_CONFIG_SYNONYM, "-foreground", "tabForeground", (char *)NULL, (char *)NULL, 0, 0}, {TK_CONFIG_PIXELS, "-gap", "gap", "Gap", DEF_TNB_GAP, Tk_Offset(Notebook, gap), TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_CUSTOM, "-height", "height", "Height", DEF_TNB_HEIGHT, Tk_Offset(Notebook, reqHeight), TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground", "HighlightBackground", DEF_TNB_HIGHLIGHT_BACKGROUND, Tk_Offset(Notebook, highlightBgColor), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground", "HighlightBackground", DEF_TNB_HIGHLIGHT_BG_MONO, Tk_Offset(Notebook, highlightBgColor), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", DEF_TNB_HIGHLIGHT_COLOR, Tk_Offset(Notebook, highlightColor), 0}, {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness", "HighlightThickness", DEF_TNB_HIGHLIGHT_WIDTH, Tk_Offset(Notebook, highlightWidth), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-outerpad", "outerPad", "OuterPad", DEF_TNB_OUTER_PAD, Tk_Offset(Notebook, outerPad), TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_CUSTOM, "-pageheight", "pageHeight", "PageHeight", DEF_TNB_PAGE_HEIGHT, Tk_Offset(Notebook, reqPageHeight), TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_CUSTOM, "-pagewidth", "pageWidth", "PageWidth", DEF_TNB_PAGE_WIDTH, Tk_Offset(Notebook, reqPageWidth), TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_STRING, "-perforationcommand", "perforationcommand", "PerforationCommand", DEF_TAB_PERF_COMMAND, Tk_Offset(Notebook, defTabStyle.perfCommand), TK_CONFIG_NULL_OK, &bltUidOption}, {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", DEF_TNB_RELIEF, Tk_Offset(Notebook, relief), 0}, {TK_CONFIG_DOUBLE, "-rotate", "rotate", "Rotate", DEF_TNB_ROTATE, Tk_Offset(Notebook, defTabStyle.rotate), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_BOOLEAN, "-samewidth", "sameWidth", "SameWidth", DEF_TNB_SAME_WIDTH, Tk_Offset(Notebook, defTabStyle.constWidth), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_STRING, "-scrollcommand", "scrollCommand", "ScrollCommand", (char *)NULL, Tk_Offset(Notebook, scrollCmdPrefix), TK_CONFIG_NULL_OK}, {TK_CONFIG_CUSTOM, "-scrollincrement", "scrollIncrement", "ScrollIncrement", DEF_TNB_SCROLL_INCREMENT, Tk_Offset(Notebook, scrollUnits), TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveDistanceOption}, {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground", DEF_TNB_SELECT_BG_MONO, Tk_Offset(Notebook, defTabStyle.selBorder), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground", DEF_TNB_SELECT_BACKGROUND, Tk_Offset(Notebook, defTabStyle.selBorder), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_STRING, "-selectcommand", "selectCommand", "SelectCommand", DEF_TNB_SELECT_CMD, Tk_Offset(Notebook, defTabStyle.command), TK_CONFIG_NULL_OK}, {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background", DEF_TNB_SELECT_FG_MONO, Tk_Offset(Notebook, defTabStyle.selColor), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background", DEF_TNB_SELECT_FOREGROUND, Tk_Offset(Notebook, defTabStyle.selColor), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_CUSTOM, "-selectpad", "selectPad", "SelectPad", DEF_TNB_SELECT_PAD, Tk_Offset(Notebook, xSelectPad), TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_COLOR, "-shadowcolor", "shadowColor", "ShadowColor", DEF_TNB_SHADOW_COLOR, Tk_Offset(Notebook, shadowColor), 0}, {TK_CONFIG_CUSTOM, "-side", "side", "side", DEF_TNB_SIDE, Tk_Offset(Notebook, side), TK_CONFIG_DONT_SET_DEFAULT, &sideOption}, {TK_CONFIG_CUSTOM, "-slant", "slant", "Slant", DEF_TNB_SLANT, Tk_Offset(Notebook, slant), TK_CONFIG_DONT_SET_DEFAULT, &slantOption}, {TK_CONFIG_BORDER, "-tabbackground", "tabBackground", "Background", DEF_TNB_TAB_BG_MONO, Tk_Offset(Notebook, defTabStyle.border), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_BORDER, "-tabbackground", "tabBackground", "Background", DEF_TNB_TAB_BACKGROUND, Tk_Offset(Notebook, defTabStyle.border), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_CUSTOM, "-tabborderwidth", "tabBorderWidth", "BorderWidth", DEF_TNB_BORDERWIDTH, Tk_Offset(Notebook, defTabStyle.borderWidth), TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_COLOR, "-tabforeground", "tabForeground", "Foreground", DEF_TNB_TEXT_COLOR, Tk_Offset(Notebook, defTabStyle.textColor), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_COLOR, "-tabforeground", "tabForeground", "Foreground", DEF_TNB_TEXT_MONO, Tk_Offset(Notebook, defTabStyle.textColor), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_RELIEF, "-tabrelief", "tabRelief", "TabRelief", DEF_TNB_TAB_RELIEF, Tk_Offset(Notebook, defTabStyle.relief), 0}, {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus", DEF_TNB_TAKE_FOCUS, Tk_Offset(Notebook, takeFocus), TK_CONFIG_NULL_OK}, {TK_CONFIG_BOOLEAN, "-tearoff", "tearoff", "Tearoff", DEF_TNB_TEAROFF, Tk_Offset(Notebook, tearoff), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-textside", "textSide", "TextSide", DEF_TNB_TEXT_SIDE, Tk_Offset(Notebook, defTabStyle.textSide), TK_CONFIG_DONT_SET_DEFAULT, &sideOption}, {TK_CONFIG_CUSTOM, "-tiers", "tiers", "Tiers", DEF_TNB_TIERS, Tk_Offset(Notebook, reqTiers), TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveCountOption}, {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile", (char *)NULL, Tk_Offset(Notebook, tile), TK_CONFIG_NULL_OK, &bltTileOption}, {TK_CONFIG_CUSTOM, "-width", "width", "Width", DEF_TNB_WIDTH, Tk_Offset(Notebook, reqWidth), TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL, 0, 0} }; /* Forward Declarations */ static void DestroyNotebook _ANSI_ARGS_((DestroyData dataPtr)); static void DestroyTearoff _ANSI_ARGS_((DestroyData dataPtr)); static void EmbeddedWidgetEventProc _ANSI_ARGS_((ClientData clientdata, XEvent *eventPtr)); static void TearoffEventProc _ANSI_ARGS_((ClientData clientdata, XEvent *eventPtr)); static void NotebookEventProc _ANSI_ARGS_((ClientData clientdata, XEvent *eventPtr)); static void DrawLabel _ANSI_ARGS_((Notebook *nbPtr, Tab *tabPtr, Drawable drawable)); static void DrawFolder _ANSI_ARGS_((Notebook *nbPtr, Tab *tabPtr, Drawable drawable)); static void DisplayNotebook _ANSI_ARGS_((ClientData clientData)); static void DisplayTearoff _ANSI_ARGS_((ClientData clientData)); static void NotebookInstDeletedCmd _ANSI_ARGS_((ClientData clientdata)); static int NotebookInstCmd _ANSI_ARGS_((ClientData clientdata, Tcl_Interp *interp, int argc, char **argv)); static void GetWindowRectangle _ANSI_ARGS_((Tab *tabPtr, Tk_Window parent, int tearOff, XRectangle *rectPtr)); static void ArrangeWindow _ANSI_ARGS_((Tk_Window tkwin, XRectangle *rectPtr, int force)); static void EventuallyRedraw _ANSI_ARGS_((Notebook *nbPtr)); static void EventuallyRedrawTearoff _ANSI_ARGS_((Tab *tabPtr)); static void ComputeLayout _ANSI_ARGS_((Notebook *nbPtr)); static void DrawOuterBorders _ANSI_ARGS_((Notebook *nbPtr, Drawable drawable)); static Tk_ImageChangedProc ImageChangedProc; static Blt_TileChangedProc TileChangedProc; static Blt_BindTagProc GetTags; static Blt_BindPickProc PickTab; static Tcl_IdleProc AdoptWindow; static Tcl_CmdProc NotebookCmd; static ClientData MakeTag(nbPtr, tagName) Notebook *nbPtr; char *tagName; { Blt_HashEntry *hPtr; int isNew; hPtr = Blt_CreateHashEntry(&(nbPtr->tagTable), tagName, &isNew); assert(hPtr); return Blt_GetHashKey(&(nbPtr->tagTable), hPtr); } /* *---------------------------------------------------------------------- * * WorldToScreen -- * * Converts world coordinates to screen coordinates. Note that * the world view is always tabs up. * * Results: * The screen coordinates are returned via *xScreenPtr and *yScreenPtr. * *---------------------------------------------------------------------- */ static void WorldToScreen(nbPtr, x, y, xScreenPtr, yScreenPtr) Notebook *nbPtr; int x, y; int *xScreenPtr, *yScreenPtr; { int sx, sy; sx = sy = 0; /* Suppress compiler warning. */ /* Translate world X-Y to screen coordinates */ /* * Note that the world X-coordinate is translated by the selected label's * X padding. This is done only to keep the scroll range is between * 0.0 and 1.0, rather adding/subtracting the pad in various locations. * It may be changed back in the future. */ x += (nbPtr->inset + nbPtr->xSelectPad - nbPtr->scrollOffset); y += nbPtr->inset + nbPtr->yPad; switch (nbPtr->side) { case SIDE_TOP: sx = x, sy = y; /* Do nothing */ break; case SIDE_RIGHT: sx = Tk_Width(nbPtr->tkwin) - y; sy = x; break; case SIDE_LEFT: sx = y, sy = x; /* Flip coordinates */ break; case SIDE_BOTTOM: sx = x; sy = Tk_Height(nbPtr->tkwin) - y; break; } *xScreenPtr = sx; *yScreenPtr = sy; } /* *---------------------------------------------------------------------- * * EventuallyRedraw -- * * Queues a request to redraw the widget at the next idle point. * * Results: * None. * * Side effects: * Information gets redisplayed. Right now we don't do selective * redisplays: the whole window will be redrawn. * *---------------------------------------------------------------------- */ static void EventuallyRedraw(nbPtr) Notebook *nbPtr; { if ((nbPtr->tkwin != NULL) && !(nbPtr->flags & TNB_REDRAW)) { nbPtr->flags |= TNB_REDRAW; Tcl_DoWhenIdle(DisplayNotebook, nbPtr); } } /* *---------------------------------------------------------------------- * * EventuallyRedrawTearoff -- * * Queues a request to redraw the tearoff at the next idle point. * * Results: * None. * * Side effects: * Information gets redisplayed. Right now we don't do selective * redisplays: the whole window will be redrawn. * *---------------------------------------------------------------------- */ static void EventuallyRedrawTearoff(tabPtr) Tab *tabPtr; { if ((tabPtr->tkwin != NULL) && !(tabPtr->flags & TAB_REDRAW)) { tabPtr->flags |= TAB_REDRAW; Tcl_DoWhenIdle(DisplayTearoff, tabPtr); } } /* *---------------------------------------------------------------------- * * ImageChangedProc * * This routine is called whenever an image displayed in a tab * changes. In this case, we assume that everything will change * and queue a request to re-layout and redraw the entire notebook. * * Results: * None. * *---------------------------------------------------------------------- */ /* ARGSUSED */ static void ImageChangedProc(clientData, x, y, width, height, imageWidth, imageHeight) ClientData clientData; int x, y, width, height; /* Not used. */ int imageWidth, imageHeight;/* Not used. */ { Notebook *nbPtr = clientData; nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL); EventuallyRedraw(nbPtr); } /* *---------------------------------------------------------------------- * * GetImage -- * * This is a wrapper procedure for Tk_GetImage. The problem is * that if the same image is used repeatedly in the same widget, * the separate instances are saved in a linked list. This makes * it especially slow to destroy the widget. As a workaround, * this routine hashes the image and maintains a reference count * for it. * * Results: * Returns a pointer to the new image. * *---------------------------------------------------------------------- */ static TabImage GetImage(nbPtr, interp, tkwin, name) Notebook *nbPtr; Tcl_Interp *interp; Tk_Window tkwin; char *name; { struct TabImageStruct *imagePtr; int isNew; Blt_HashEntry *hPtr; hPtr = Blt_CreateHashEntry(&(nbPtr->imageTable), name, &isNew); if (isNew) { Tk_Image tkImage; int width, height; tkImage = Tk_GetImage(interp, tkwin, name, ImageChangedProc, nbPtr); if (tkImage == NULL) { Blt_DeleteHashEntry(&(nbPtr->imageTable), hPtr); return NULL; } Tk_SizeOfImage(tkImage, &width, &height); imagePtr = Blt_Malloc(sizeof(struct TabImageStruct)); imagePtr->tkImage = tkImage; imagePtr->hashPtr = hPtr; imagePtr->refCount = 1; imagePtr->width = width; imagePtr->height = height; Blt_SetHashValue(hPtr, imagePtr); } else { imagePtr = (struct TabImageStruct *)Blt_GetHashValue(hPtr); imagePtr->refCount++; } return imagePtr; } /* *---------------------------------------------------------------------- * * FreeImage -- * * Releases the image if it's not being used anymore by this * widget. Note there may be several uses of the same image * by many tabs. * * Results: * None. * * Side Effects: * The reference count is decremented and the image is freed * is it's not being used anymore. * *---------------------------------------------------------------------- */ static void FreeImage(nbPtr, imagePtr) Notebook *nbPtr; struct TabImageStruct *imagePtr; { imagePtr->refCount--; if (imagePtr->refCount == 0) { Blt_DeleteHashEntry(&(nbPtr->imageTable), imagePtr->hashPtr); Tk_FreeImage(imagePtr->tkImage); Blt_Free(imagePtr); } } /* *---------------------------------------------------------------------- * * StringToImage -- * * Converts an image name into a Tk image token. * * Results: * If the string is successfully converted, TCL_OK is returned. * Otherwise, TCL_ERROR is returned and an error message is left * in interpreter's result field. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToImage(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Contains a pointer to the notebook containing * this image. */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Window associated with the notebook. */ char *string; /* String representation */ char *widgRec; /* Widget record */ int offset; /* Offset to field in structure */ { Notebook *nbPtr = *(Notebook **)clientData; TabImage *imagePtr = (TabImage *) (widgRec + offset); TabImage image; image = NULL; if ((string != NULL) && (*string != '\0')) { image = GetImage(nbPtr, interp, tkwin, string); if (image == NULL) { return TCL_ERROR; } } if (*imagePtr != NULL) { FreeImage(nbPtr, *imagePtr); } *imagePtr = image; return TCL_OK; } /* *---------------------------------------------------------------------- * * ImageToString -- * * Converts the Tk image back to its string representation (i.e. * its name). * * Results: * The name of the image is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * ImageToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Pointer to notebook containing image. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Widget record */ int offset; /* Offset of field in record */ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */ { Notebook *nbPtr = *(Notebook **)clientData; TabImage *imagePtr = (TabImage *) (widgRec + offset); if (*imagePtr == NULL) { return ""; } return Blt_GetHashKey(&(nbPtr->imageTable), (*imagePtr)->hashPtr); } /* *---------------------------------------------------------------------- * * StringToWindow -- * * Converts a window name into Tk window. * * Results: * If the string is successfully converted, TCL_OK is returned. * Otherwise, TCL_ERROR is returned and an error message is left * in interpreter's result field. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToWindow(clientData, interp, parent, string, widgRec, offset) ClientData clientData; /* Not used. */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window parent; /* Parent window */ char *string; /* String representation. */ char *widgRec; /* Widget record */ int offset; /* Offset to field in structure */ { Tab *tabPtr = (Tab *)widgRec; Tk_Window *tkwinPtr = (Tk_Window *)(widgRec + offset); Tk_Window old, tkwin; Notebook *nbPtr; old = *tkwinPtr; tkwin = NULL; nbPtr = tabPtr->nbPtr; if ((string != NULL) && (*string != '\0')) { tkwin = Tk_NameToWindow(interp, string, parent); if (tkwin == NULL) { return TCL_ERROR; } if (tkwin == old) { return TCL_OK; } /* * Allow only widgets that are children of the notebook to be * embedded into the page. This way we can make assumptions about * the window based upon its parent; either it's the notebook window * or it has been torn off. */ parent = Tk_Parent(tkwin); if (parent != nbPtr->tkwin) { Tcl_AppendResult(interp, "can't manage \"", Tk_PathName(tkwin), "\" in notebook \"", Tk_PathName(nbPtr->tkwin), "\"", (char *)NULL); return TCL_ERROR; } Tk_ManageGeometry(tkwin, &tabMgrInfo, tabPtr); Tk_CreateEventHandler(tkwin, StructureNotifyMask, EmbeddedWidgetEventProc, tabPtr); /* * We need to make the window to exist immediately. If the * window is torn off (placed into another container window), * the timing between the container and the its new child * (this window) gets tricky. This should work for Tk 4.2. */ Tk_MakeWindowExist(tkwin); } if (old != NULL) { if (tabPtr->container != NULL) { Tcl_EventuallyFree(tabPtr, DestroyTearoff); } Tk_DeleteEventHandler(old, StructureNotifyMask, EmbeddedWidgetEventProc, tabPtr); Tk_ManageGeometry(old, (Tk_GeomMgr *) NULL, tabPtr); Tk_UnmapWindow(old); } *tkwinPtr = tkwin; return TCL_OK; } /* *---------------------------------------------------------------------- * * WindowToString -- * * Converts the Tk window back to its string representation (i.e. * its name). * * Results: * The name of the window is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * WindowToString(clientData, parent, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window parent; /* Not used. */ char *widgRec; /* Widget record */ int offset; /* Offset of field in record */ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */ { Tk_Window tkwin = *(Tk_Window *)(widgRec + offset); if (tkwin == NULL) { return ""; } return Tk_PathName(tkwin); } /* *---------------------------------------------------------------------- * * StringToSide -- * * Converts "left", "right", "top", "bottom", into a numeric token * designating the side of the notebook which to display tabs. * * Results: * If the string is successfully converted, TCL_OK is returned. * Otherwise, TCL_ERROR is returned and an error message is left * in interpreter's result field. * *---------------------------------------------------------------------- */ /*ARGSUSED */ static int StringToSide(clientData, interp, parent, string, widgRec, offset) ClientData clientData; /* Not used. */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window parent; /* Parent window */ char *string; /* Option value string */ char *widgRec; /* Widget record */ int offset; /* offset to field in structure */ { int *sidePtr = (int *)(widgRec + offset); char c; unsigned int length; c = string[0]; length = strlen(string); if ((c == 'l') && (strncmp(string, "left", length) == 0)) { *sidePtr = SIDE_LEFT; } else if ((c == 'r') && (strncmp(string, "right", length) == 0)) { *sidePtr = SIDE_RIGHT; } else if ((c == 't') && (strncmp(string, "top", length) == 0)) { *sidePtr = SIDE_TOP; } else if ((c == 'b') && (strncmp(string, "bottom", length) == 0)) { *sidePtr = SIDE_BOTTOM; } else { Tcl_AppendResult(interp, "bad side \"", string, "\": should be left, right, top, or bottom", (char *)NULL); return TCL_ERROR; } return TCL_OK; } /* *---------------------------------------------------------------------- * * SideToString -- * * Converts the window into its string representation (its name). * * Results: * The name of the window is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * SideToString(clientData, parent, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window parent; /* Not used. */ char *widgRec; /* Widget record */ int offset; /* offset of windows array in record */ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */ { int side = *(int *)(widgRec + offset); switch (side) { case SIDE_LEFT: return "left"; case SIDE_RIGHT: return "right"; case SIDE_BOTTOM: return "bottom"; case SIDE_TOP: return "top"; } return "unknown side value"; } /* *---------------------------------------------------------------------- * * StringToSlant -- * * Converts the slant style string into its numeric representation. * * Valid style strings are: * * "none" Both sides are straight. * "left" Left side is slanted. * "right" Right side is slanted. * "both" Both sides are slanted. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToSlant(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Not used. */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Not used. */ char *string; /* String representation of attribute. */ char *widgRec; /* Widget record */ int offset; /* Offset of field in widget record. */ { int *slantPtr = (int *)(widgRec + offset); unsigned int length; char c; c = string[0]; length = strlen(string); if ((c == 'n') && (strncmp(string, "none", length) == 0)) { *slantPtr = SLANT_NONE; } else if ((c == 'l') && (strncmp(string, "left", length) == 0)) { *slantPtr = SLANT_LEFT; } else if ((c == 'r') && (strncmp(string, "right", length) == 0)) { *slantPtr = SLANT_RIGHT; } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) { *slantPtr = SLANT_BOTH; } else { Tcl_AppendResult(interp, "bad argument \"", string, "\": should be \"none\", \"left\", \"right\", or \"both\"", (char *)NULL); return TCL_ERROR; } return TCL_OK; } /* *---------------------------------------------------------------------- * * SlantToString -- * * Returns the slant style string based upon the slant flags. * * Results: * The slant style string is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * SlantToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Widget structure record. */ int offset; /* Offset of field in widget record. */ Tcl_FreeProc **freeProcPtr; /* Not used. */ { int slant = *(int *)(widgRec + offset); switch (slant) { case SLANT_LEFT: return "left"; case SLANT_RIGHT: return "right"; case SLANT_NONE: return "none"; case SLANT_BOTH: return "both"; default: return "unknown value"; } } static int WorldY(tabPtr) Tab *tabPtr; { int tier; tier = tabPtr->nbPtr->nTiers - tabPtr->tier; return tier * tabPtr->nbPtr->tabHeight; } static int TabIndex(nbPtr, tabPtr) Notebook *nbPtr; Tab *tabPtr; { Tab *t2Ptr; int count; Blt_ChainLink *linkPtr; count = 0; for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { t2Ptr = Blt_ChainGetValue(linkPtr); if (t2Ptr == tabPtr) { return count; } count++; } return -1; } /* * ---------------------------------------------------------------------- * * RenumberTiers -- * * In multi-tier mode, we need to find the start of the tier * containing the newly selected tab. * * Tiers are draw from the last tier to the first, so that * the the lower-tiered tabs will partially cover the bottoms * of tab directly above it. This simplifies the drawing of * tabs because we don't worry how tabs are clipped by their * neighbors. * * In addition, tabs are re-marked with the correct tier number. * * Results: * None. * * Side Effects: * Renumbering the tab's tier will change the vertical placement * of the tab (i.e. shift tiers). * * ---------------------------------------------------------------------- */ static void RenumberTiers(nbPtr, tabPtr) Notebook *nbPtr; Tab *tabPtr; { int tier; Tab *prevPtr; Blt_ChainLink *linkPtr, *lastPtr; nbPtr->focusPtr = nbPtr->selectPtr = tabPtr; Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr, NULL); tier = tabPtr->tier; for (linkPtr = Blt_ChainPrevLink(tabPtr->linkPtr); linkPtr != NULL; linkPtr = lastPtr) { lastPtr = Blt_ChainPrevLink(linkPtr); prevPtr = Blt_ChainGetValue(linkPtr); if ((prevPtr == NULL) || (prevPtr->tier != tier)) { break; } tabPtr = prevPtr; } nbPtr->startPtr = tabPtr; for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); tabPtr->tier = (tabPtr->tier - tier + 1); if (tabPtr->tier < 1) { tabPtr->tier += nbPtr->nTiers; } tabPtr->worldY = WorldY(tabPtr); } } /* *---------------------------------------------------------------------- * * PickTab -- * * Searches the tab located within the given screen X-Y coordinates * in the viewport. Note that tabs overlap slightly, so that its * important to search from the innermost tier out. * * Results: * Returns the pointer to the tab. If the pointer isn't contained * by any tab, NULL is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static ClientData PickTab(clientData, x, y, contextPtr) ClientData clientData; int x, y; /* Screen coordinates to test. */ ClientData *contextPtr; { Notebook *nbPtr = clientData; Tab *tabPtr; Blt_ChainLink *linkPtr; if (contextPtr != NULL) { *contextPtr = NULL; } tabPtr = nbPtr->selectPtr; if ((nbPtr->tearoff) && (tabPtr != NULL) && (tabPtr->container == NULL) && (tabPtr->tkwin != NULL)) { int top, bottom, left, right; int sx, sy; /* Check first for perforation on the selected tab. */ WorldToScreen(nbPtr, tabPtr->worldX + 2, tabPtr->worldY + tabPtr->worldHeight + 4, &sx, &sy); if (nbPtr->side & SIDE_HORIZONTAL) { left = sx - 2; right = left + tabPtr->screenWidth; top = sy - 4; bottom = sy + 4; } else { left = sx - 4; right = sx + 4; top = sy - 2; bottom = top + tabPtr->screenHeight; } if ((x >= left) && (y >= top) && (x < right) && (y < bottom)) { if (contextPtr != NULL) { *contextPtr = TAB_PERFORATION; } return nbPtr->selectPtr; } } for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); if (!(tabPtr->flags & TAB_VISIBLE)) { continue; } if ((x >= tabPtr->screenX) && (y >= tabPtr->screenY) && (x <= (tabPtr->screenX + tabPtr->screenWidth)) && (y < (tabPtr->screenY + tabPtr->screenHeight))) { if (contextPtr != NULL) { *contextPtr = TAB_LABEL; } return tabPtr; } } return NULL; } static Tab * TabLeft(tabPtr) Tab *tabPtr; { Blt_ChainLink *linkPtr; linkPtr = Blt_ChainPrevLink(tabPtr->linkPtr); if (linkPtr != NULL) { Tab *newPtr; newPtr = Blt_ChainGetValue(linkPtr); /* Move only if the next tab is on another tier. */ if (newPtr->tier == tabPtr->tier) { tabPtr = newPtr; } } return tabPtr; } static Tab * TabRight(tabPtr) Tab *tabPtr; { Blt_ChainLink *linkPtr; linkPtr = Blt_ChainNextLink(tabPtr->linkPtr); if (linkPtr != NULL) { Tab *newPtr; newPtr = Blt_ChainGetValue(linkPtr); /* Move only if the next tab is on another tier. */ if (newPtr->tier == tabPtr->tier) { tabPtr = newPtr; } } return tabPtr; } static Tab * TabUp(tabPtr) Tab *tabPtr; { if (tabPtr != NULL) { Notebook *nbPtr; int x, y; int worldX, worldY; nbPtr = tabPtr->nbPtr; worldX = tabPtr->worldX + (tabPtr->worldWidth / 2); worldY = tabPtr->worldY - (nbPtr->tabHeight / 2); WorldToScreen(nbPtr, worldX, worldY, &x, &y); tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL); if (tabPtr == NULL) { /* * We might have inadvertly picked the gap between two tabs, * so if the first pick fails, try again a little to the left. */ WorldToScreen(nbPtr, worldX + nbPtr->gap, worldY, &x, &y); tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL); } if ((tabPtr == NULL) && (nbPtr->focusPtr->tier < (nbPtr->nTiers - 1))) { worldY -= nbPtr->tabHeight; WorldToScreen(nbPtr, worldX, worldY, &x, &y); tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL); } if (tabPtr == NULL) { tabPtr = nbPtr->focusPtr; } } return tabPtr; } static Tab * TabDown(tabPtr) Tab *tabPtr; { if (tabPtr != NULL) { Notebook *nbPtr; int x, y; int worldX, worldY; nbPtr = tabPtr->nbPtr; worldX = tabPtr->worldX + (tabPtr->worldWidth / 2); worldY = tabPtr->worldY + (3 * nbPtr->tabHeight) / 2; WorldToScreen(nbPtr, worldX, worldY, &x, &y); tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL); if (tabPtr == NULL) { /* * We might have inadvertly picked the gap between two tabs, * so if the first pick fails, try again a little to the left. */ WorldToScreen(nbPtr, worldX - nbPtr->gap, worldY, &x, &y); tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL); } if ((tabPtr == NULL) && (nbPtr->focusPtr->tier > 2)) { worldY += nbPtr->tabHeight; WorldToScreen(nbPtr, worldX, worldY, &x, &y); tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL); } if (tabPtr == NULL) { tabPtr = nbPtr->focusPtr; } } return tabPtr; } /* *---------------------------------------------------------------------- * * GetTab -- * * Converts a string representing a tab index into a tab pointer. * The index may be in one of the following forms: * * number Tab at position in the list of tabs. * @x,y Tab closest to the specified X-Y screen coordinates. * "active" Tab mouse is located over. * "focus" Tab is the widget's focus. * "select" Currently selected tab. * "right" Next tab from the focus tab. * "left" Previous tab from the focus tab. * "up" Next tab from the focus tab. * "down" Previous tab from the focus tab. * "end" Last tab in list. * * Results: * If the string is successfully converted, TCL_OK is returned. * The pointer to the node is returned via tabPtrPtr. * Otherwise, TCL_ERROR is returned and an error message is left * in interpreter's result field. * *---------------------------------------------------------------------- */ static int GetTab(nbPtr, string, tabPtrPtr, allowNull) Notebook *nbPtr; char *string; Tab **tabPtrPtr; int allowNull; /* Allow NULL tabPtr */ { Tab *tabPtr; Blt_ChainLink *linkPtr; int position; char c; c = string[0]; tabPtr = NULL; if (nbPtr->focusPtr == NULL) { nbPtr->focusPtr = nbPtr->selectPtr; Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr, NULL); } if ((isdigit(UCHAR(c))) && (Tcl_GetInt(nbPtr->interp, string, &position) == TCL_OK)) { linkPtr = Blt_ChainGetNthLink(nbPtr->chainPtr, position); if (linkPtr == NULL) { Tcl_AppendResult(nbPtr->interp, "can't find tab \"", string, "\" in \"", Tk_PathName(nbPtr->tkwin), "\": no such index", (char *)NULL); return TCL_ERROR; } tabPtr = Blt_ChainGetValue(linkPtr); } else if ((c == 'a') && (strcmp(string, "active") == 0)) { tabPtr = nbPtr->activePtr; } else if ((c == 'c') && (strcmp(string, "current") == 0)) { tabPtr = (Tab *)Blt_GetCurrentItem(nbPtr->bindTable); } else if ((c == 's') && (strcmp(string, "select") == 0)) { tabPtr = nbPtr->selectPtr; } else if ((c == 'f') && (strcmp(string, "focus") == 0)) { tabPtr = nbPtr->focusPtr; } else if ((c == 'u') && (strcmp(string, "up") == 0)) { switch (nbPtr->side) { case SIDE_LEFT: case SIDE_RIGHT: tabPtr = TabLeft(nbPtr->focusPtr); break; case SIDE_BOTTOM: tabPtr = TabDown(nbPtr->focusPtr); break; case SIDE_TOP: tabPtr = TabUp(nbPtr->focusPtr); break; } } else if ((c == 'd') && (strcmp(string, "down") == 0)) { switch (nbPtr->side) { case SIDE_LEFT: case SIDE_RIGHT: tabPtr = TabRight(nbPtr->focusPtr); break; case SIDE_BOTTOM: tabPtr = TabUp(nbPtr->focusPtr); break; case SIDE_TOP: tabPtr = TabDown(nbPtr->focusPtr); break; } } else if ((c == 'l') && (strcmp(string, "left") == 0)) { switch (nbPtr->side) { case SIDE_LEFT: tabPtr = TabUp(nbPtr->focusPtr); break; case SIDE_RIGHT: tabPtr = TabDown(nbPtr->focusPtr); break; case SIDE_BOTTOM: case SIDE_TOP: tabPtr = TabLeft(nbPtr->focusPtr); break; } } else if ((c == 'r') && (strcmp(string, "right") == 0)) { switch (nbPtr->side) { case SIDE_LEFT: tabPtr = TabDown(nbPtr->focusPtr); break; case SIDE_RIGHT: tabPtr = TabUp(nbPtr->focusPtr); break; case SIDE_BOTTOM: case SIDE_TOP: tabPtr = TabRight(nbPtr->focusPtr); break; } } else if ((c == 'e') && (strcmp(string, "end") == 0)) { linkPtr = Blt_ChainLastLink(nbPtr->chainPtr); if (linkPtr != NULL) { tabPtr = Blt_ChainGetValue(linkPtr); } } else if (c == '@') { int x, y; if (Blt_GetXY(nbPtr->interp, nbPtr->tkwin, string, &x, &y) != TCL_OK) { return TCL_ERROR; } tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL); } else { Blt_HashEntry *hPtr; hPtr = Blt_FindHashEntry(&(nbPtr->tabTable), string); if (hPtr != NULL) { tabPtr = (Tab *)Blt_GetHashValue(hPtr); } } *tabPtrPtr = tabPtr; Tcl_ResetResult(nbPtr->interp); if ((!allowNull) && (tabPtr == NULL)) { Tcl_AppendResult(nbPtr->interp, "can't find tab \"", string, "\" in \"", Tk_PathName(nbPtr->tkwin), "\"", (char *)NULL); return TCL_ERROR; } return TCL_OK; } static Tab * NextOrLastTab(tabPtr) Tab *tabPtr; { if (tabPtr->linkPtr != NULL) { Blt_ChainLink *linkPtr; linkPtr = Blt_ChainNextLink(tabPtr->linkPtr); if (linkPtr == NULL) { linkPtr = Blt_ChainPrevLink(tabPtr->linkPtr); } if (linkPtr != NULL) { return Blt_ChainGetValue(linkPtr); } } return NULL; } /* * -------------------------------------------------------------- * * EmbeddedWidgetEventProc -- * * This procedure is invoked by the Tk dispatcher for various * events on embedded widgets contained in the notebook. * * Results: * None. * * Side effects: * When an embedded widget gets deleted, internal structures get * cleaned up. When it gets resized, the notebook is redisplayed. * * -------------------------------------------------------------- */ static void EmbeddedWidgetEventProc(clientData, eventPtr) ClientData clientData; /* Information about the tab window. */ XEvent *eventPtr; /* Information about event. */ { Tab *tabPtr = clientData; if ((tabPtr == NULL) || (tabPtr->tkwin == NULL)) { return; } switch (eventPtr->type) { case ConfigureNotify: /* * If the window's requested size changes, redraw the window. * But only if it's currently the selected page. */ if ((tabPtr->container == NULL) && (Tk_IsMapped(tabPtr->tkwin)) && (tabPtr->nbPtr->selectPtr == tabPtr)) { EventuallyRedraw(tabPtr->nbPtr); } break; case DestroyNotify: /* * Mark the tab as deleted by dereferencing the Tk window * pointer. Redraw the window only if the tab is currently * visible. */ if ((Tk_IsMapped(tabPtr->tkwin)) && (tabPtr->nbPtr->selectPtr == tabPtr)) { EventuallyRedraw(tabPtr->nbPtr); } Tk_DeleteEventHandler(tabPtr->tkwin, StructureNotifyMask, EmbeddedWidgetEventProc, tabPtr); tabPtr->tkwin = NULL; break; } } /* * ---------------------------------------------------------------------- * * EmbeddedWidgetCustodyProc -- * * This procedure is invoked when a tab window has been * stolen by another geometry manager. The information and * memory associated with the tab window is released. * * Results: * None. * * Side effects: * Arranges for the widget formerly associated with the tab * window to have its layout re-computed and arranged at the * next idle point. * * --------------------------------------------------------------------- */ /* ARGSUSED */ static void EmbeddedWidgetCustodyProc(clientData, tkwin) ClientData clientData; /* Information about the former tab window. */ Tk_Window tkwin; /* Not used. */ { Tab *tabPtr = clientData; Notebook *nbPtr; if ((tabPtr == NULL) || (tabPtr->tkwin == NULL)) { return; } nbPtr = tabPtr->nbPtr; if (tabPtr->container != NULL) { Tcl_EventuallyFree(tabPtr, DestroyTearoff); } /* * Mark the tab as deleted by dereferencing the Tk window * pointer. Redraw the window only if the tab is currently * visible. */ if (tabPtr->tkwin != NULL) { if (Tk_IsMapped(tabPtr->tkwin) && (nbPtr->selectPtr == tabPtr)) { nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL); EventuallyRedraw(nbPtr); } Tk_DeleteEventHandler(tabPtr->tkwin, StructureNotifyMask, EmbeddedWidgetEventProc, tabPtr); tabPtr->tkwin = NULL; } } /* * ------------------------------------------------------------------------- * * EmbeddedWidgetGeometryProc -- * * This procedure is invoked by Tk_GeometryRequest for tab * windows managed by the widget. * * Results: * None. * * Side effects: * Arranges for tkwin, and all its managed siblings, to be * repacked and drawn at the next idle point. * * ------------------------------------------------------------------------ */ /* ARGSUSED */ static void EmbeddedWidgetGeometryProc(clientData, tkwin) ClientData clientData; /* Information about window that got new * preferred geometry. */ Tk_Window tkwin; /* Other Tk-related information about the * window. */ { Tab *tabPtr = clientData; if ((tabPtr == NULL) || (tabPtr->tkwin == NULL)) { fprintf(stderr, "%s: line %d \"tkwin is null\"", __FILE__, __LINE__); return; } tabPtr->nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL); EventuallyRedraw(tabPtr->nbPtr); } /* * ---------------------------------------------------------------------- * * DestroyTab -- * * ---------------------------------------------------------------------- */ static void DestroyTab(nbPtr, tabPtr) Notebook *nbPtr; Tab *tabPtr; { Blt_HashEntry *hPtr; if (tabPtr->flags & TAB_REDRAW) { Tcl_CancelIdleCall(DisplayTearoff, tabPtr); } if (tabPtr->container != NULL) { Tk_DestroyWindow(tabPtr->container); } if (tabPtr->tkwin != NULL) { Tk_ManageGeometry(tabPtr->tkwin, (Tk_GeomMgr *)NULL, tabPtr); Tk_DeleteEventHandler(tabPtr->tkwin, StructureNotifyMask, EmbeddedWidgetEventProc, tabPtr); if (Tk_IsMapped(tabPtr->tkwin)) { Tk_UnmapWindow(tabPtr->tkwin); } } if (tabPtr == nbPtr->activePtr) { nbPtr->activePtr = NULL; } if (tabPtr == nbPtr->selectPtr) { nbPtr->selectPtr = NextOrLastTab(tabPtr); } if (tabPtr == nbPtr->focusPtr) { nbPtr->focusPtr = nbPtr->selectPtr; Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr, NULL); } if (tabPtr == nbPtr->startPtr) { nbPtr->startPtr = NULL; } Tk_FreeOptions(tabConfigSpecs, (char *)tabPtr, nbPtr->display, 0); if (tabPtr->text != NULL) { Blt_FreeUid(tabPtr->text); } hPtr = Blt_FindHashEntry(&(nbPtr->tabTable), tabPtr->name); assert(hPtr); Blt_DeleteHashEntry(&(nbPtr->tabTable), hPtr); if (tabPtr->image != NULL) { FreeImage(nbPtr, tabPtr->image); } if (tabPtr->name != NULL) { Blt_Free(tabPtr->name); } if (tabPtr->textGC != NULL) { Tk_FreeGC(nbPtr->display, tabPtr->textGC); } if (tabPtr->backGC != NULL) { Tk_FreeGC(nbPtr->display, tabPtr->backGC); } if (tabPtr->command != NULL) { Blt_FreeUid(tabPtr->command); } if (tabPtr->linkPtr != NULL) { Blt_ChainDeleteLink(nbPtr->chainPtr, tabPtr->linkPtr); } if (tabPtr->tags != NULL) { Blt_FreeUid(tabPtr->tags); } Blt_DeleteBindings(nbPtr->bindTable, tabPtr); Blt_Free(tabPtr); } /* * ---------------------------------------------------------------------- * * CreateTab -- * * Creates a new tab structure. A tab contains information about * the state of the tab and its embedded window. * * Results: * Returns a pointer to the new tab structure. * * ---------------------------------------------------------------------- */ static Tab * CreateTab(nbPtr) Notebook *nbPtr; { Tab *tabPtr; Blt_HashEntry *hPtr; int isNew; char string[200]; tabPtr = Blt_Calloc(1, sizeof(Tab)); assert(tabPtr); tabPtr->nbPtr = nbPtr; sprintf(string, "tab%d", nbPtr->nextId++); tabPtr->name = Blt_Strdup(string); tabPtr->text = Blt_GetUid(string); tabPtr->fill = FILL_NONE; tabPtr->anchor = TK_ANCHOR_CENTER; tabPtr->container = NULL; tabPtr->state = STATE_NORMAL; hPtr = Blt_CreateHashEntry(&(nbPtr->tabTable), string, &isNew); Blt_SetHashValue(hPtr, tabPtr); return tabPtr; } /* *---------------------------------------------------------------------- * * TileChangedProc * * Stub for image change notifications. Since we immediately draw * the image into a pixmap, we don't really care about image changes. * * It would be better if Tk checked for NULL proc pointers. * * Results: * None. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static void TileChangedProc(clientData, tile) ClientData clientData; Blt_Tile tile; /* Not used. */ { Notebook *nbPtr = clientData; if (nbPtr->tkwin != NULL) { EventuallyRedraw(nbPtr); } } static int ConfigureTab(nbPtr, tabPtr) Notebook *nbPtr; Tab *tabPtr; { GC newGC; XGCValues gcValues; unsigned long gcMask; int labelWidth, labelHeight; Tk_Font font; Tk_3DBorder border; font = GETATTR(tabPtr, font); labelWidth = labelHeight = 0; if (tabPtr->text != NULL) { TextStyle ts; double rotWidth, rotHeight; Blt_InitTextStyle(&ts); ts.font = font; ts.shadow.offset = tabPtr->shadow.offset; ts.padX.side1 = ts.padX.side2 = 2; Blt_GetTextExtents(&ts, tabPtr->text, &labelWidth, &labelHeight); Blt_GetBoundingBox(labelWidth, labelHeight, nbPtr->defTabStyle.rotate, &rotWidth, &rotHeight, (Point2D *)NULL); labelWidth = ROUND(rotWidth); labelHeight = ROUND(rotHeight); } tabPtr->textWidth = (short int)labelWidth; tabPtr->textHeight = (short int)labelHeight; if (tabPtr->image != NULL) { int width, height; width = ImageWidth(tabPtr->image) + 2 * IMAGE_PAD; height = ImageHeight(tabPtr->image) + 2 * IMAGE_PAD; if (nbPtr->defTabStyle.textSide & SIDE_VERTICAL) { labelWidth += width; labelHeight = MAX(labelHeight, height); } else { labelHeight += height; labelWidth = MAX(labelWidth, width); } } labelWidth += PADDING(tabPtr->iPadX); labelHeight += PADDING(tabPtr->iPadY); tabPtr->labelWidth = ODD(labelWidth); tabPtr->labelHeight = ODD(labelHeight); newGC = NULL; if (tabPtr->text != NULL) { XColor *colorPtr; gcMask = GCForeground | GCFont; colorPtr = GETATTR(tabPtr, textColor); gcValues.foreground = colorPtr->pixel; gcValues.font = Tk_FontId(font); newGC = Tk_GetGC(nbPtr->tkwin, gcMask, &gcValues); } if (tabPtr->textGC != NULL) { Tk_FreeGC(nbPtr->display, tabPtr->textGC); } tabPtr->textGC = newGC; gcMask = GCForeground | GCStipple | GCFillStyle; gcValues.fill_style = FillStippled; border = GETATTR(tabPtr, border); gcValues.foreground = Tk_3DBorderColor(border)->pixel; gcValues.stipple = tabPtr->stipple; newGC = Tk_GetGC(nbPtr->tkwin, gcMask, &gcValues); if (tabPtr->backGC != NULL) { Tk_FreeGC(nbPtr->display, tabPtr->backGC); } tabPtr->backGC = newGC; /* * GC for tiled background. */ if (tabPtr->tile != NULL) { Blt_SetTileChangedProc(tabPtr->tile, TileChangedProc, nbPtr); } if (tabPtr->flags & TAB_VISIBLE) { EventuallyRedraw(nbPtr); } return TCL_OK; } /* * -------------------------------------------------------------- * * TearoffEventProc -- * * This procedure is invoked by the Tk dispatcher for various * events on the tearoff widget. * * Results: * None. * * Side effects: * When the tearoff gets deleted, internal structures get * cleaned up. When it gets resized or exposed, it's redisplayed. * * -------------------------------------------------------------- */ static void TearoffEventProc(clientData, eventPtr) ClientData clientData; /* Information about the tab window. */ XEvent *eventPtr; /* Information about event. */ { Tab *tabPtr = clientData; if ((tabPtr == NULL) || (tabPtr->tkwin == NULL) || (tabPtr->container == NULL)) { return; } switch (eventPtr->type) { case Expose: if (eventPtr->xexpose.count == 0) { EventuallyRedrawTearoff(tabPtr); } break; case ConfigureNotify: EventuallyRedrawTearoff(tabPtr); break; case DestroyNotify: if (tabPtr->flags & TAB_REDRAW) { tabPtr->flags &= ~TAB_REDRAW; Tcl_CancelIdleCall(DisplayTearoff, clientData); } Tk_DestroyWindow(tabPtr->container); tabPtr->container = NULL; break; } } /* * ---------------------------------------------------------------------------- * * GetReqWidth -- * * Returns the width requested by the embedded tab window and * any requested padding around it. This represents the requested * width of the page. * * Results: * Returns the requested width of the page. * * ---------------------------------------------------------------------------- */ static int GetReqWidth(tabPtr) Tab *tabPtr; { int width; if (tabPtr->reqWidth > 0) { width = tabPtr->reqWidth; } else { width = Tk_ReqWidth(tabPtr->tkwin); } width += PADDING(tabPtr->padX) + 2 * Tk_Changes(tabPtr->tkwin)->border_width; if (width < 1) { width = 1; } return width; } /* * ---------------------------------------------------------------------------- * * GetReqHeight -- * * Returns the height requested by the window and padding around * the window. This represents the requested height of the page. * * Results: * Returns the requested height of the page. * * ---------------------------------------------------------------------------- */ static int GetReqHeight(tabPtr) Tab *tabPtr; { int height; if (tabPtr->reqHeight > 0) { height = tabPtr->reqHeight; } else { height = Tk_ReqHeight(tabPtr->tkwin); } height += PADDING(tabPtr->padY) + 2 * Tk_Changes(tabPtr->tkwin)->border_width; if (height < 1) { height = 1; } return height; } /* * ---------------------------------------------------------------------------- * * TranslateAnchor -- * * Translate the coordinates of a given bounding box based upon the * anchor specified. The anchor indicates where the given xy position * is in relation to the bounding box. * * nw --- n --- ne * | | x,y ---+ * w center e | | * | | +-----+ * sw --- s --- se * * Results: * The translated coordinates of the bounding box are returned. * * ---------------------------------------------------------------------------- */ static void TranslateAnchor(dx, dy, anchor, xPtr, yPtr) int dx, dy; /* Difference between outer and inner regions */ Tk_Anchor anchor; /* Direction of the anchor */ int *xPtr, *yPtr; { int x, y; x = y = 0; switch (anchor) { case TK_ANCHOR_NW: /* Upper left corner */ break; case TK_ANCHOR_W: /* Left center */ y = (dy / 2); break; case TK_ANCHOR_SW: /* Lower left corner */ y = dy; break; case TK_ANCHOR_N: /* Top center */ x = (dx / 2); break; case TK_ANCHOR_CENTER: /* Centered */ x = (dx / 2); y = (dy / 2); break; case TK_ANCHOR_S: /* Bottom center */ x = (dx / 2); y = dy; break; case TK_ANCHOR_NE: /* Upper right corner */ x = dx; break; case TK_ANCHOR_E: /* Right center */ x = dx; y = (dy / 2); break; case TK_ANCHOR_SE: /* Lower right corner */ x = dx; y = dy; break; } *xPtr = (*xPtr) + x; *yPtr = (*yPtr) + y; } static void GetWindowRectangle(tabPtr, parent, tearoff, rectPtr) Tab *tabPtr; Tk_Window parent; int tearoff; XRectangle *rectPtr; { int pad; Notebook *nbPtr; int cavityWidth, cavityHeight; int width, height; int dx, dy; int x, y; nbPtr = tabPtr->nbPtr; pad = nbPtr->inset + nbPtr->inset2; if (!tearoff) { switch (nbPtr->side) { case SIDE_RIGHT: case SIDE_BOTTOM: x = nbPtr->inset + nbPtr->inset2; y = nbPtr->inset + nbPtr->inset2; break; case SIDE_LEFT: x = nbPtr->pageTop; y = nbPtr->inset + nbPtr->inset2; break; case SIDE_TOP: x = nbPtr->inset + nbPtr->inset2; y = nbPtr->pageTop; break; } if (nbPtr->side & SIDE_VERTICAL) { cavityWidth = Tk_Width(nbPtr->tkwin) - (nbPtr->pageTop + pad); cavityHeight = Tk_Height(nbPtr->tkwin) - (2 * pad); } else { cavityWidth = Tk_Width(nbPtr->tkwin) - (2 * pad); cavityHeight = Tk_Height(nbPtr->tkwin) - (nbPtr->pageTop + pad); } } else { x = nbPtr->inset + nbPtr->inset2; #define TEAR_OFF_TAB_SIZE 5 y = nbPtr->inset + nbPtr->inset2 + nbPtr->yPad + nbPtr->outerPad + TEAR_OFF_TAB_SIZE; cavityWidth = Tk_Width(parent) - (2 * pad); cavityHeight = Tk_Height(parent) - (y + pad); } cavityWidth -= PADDING(tabPtr->padX); cavityHeight -= PADDING(tabPtr->padY); if (cavityWidth < 1) { cavityWidth = 1; } if (cavityHeight < 1) { cavityHeight = 1; } width = GetReqWidth(tabPtr); height = GetReqHeight(tabPtr); /* * Resize the embedded window is of the following is true: * * 1) It's been torn off. * 2) The -fill option (horizontal or vertical) is set. * 3) the window is bigger than the cavity. */ if ((tearoff) || (cavityWidth < width) || (tabPtr->fill & FILL_X)) { width = cavityWidth; } if ((tearoff) || (cavityHeight < height) || (tabPtr->fill & FILL_Y)) { height = cavityHeight; } dx = (cavityWidth - width); dy = (cavityHeight - height); if ((dx > 0) || (dy > 0)) { TranslateAnchor(dx, dy, tabPtr->anchor, &x, &y); } /* Remember that X11 windows must be at least 1 pixel. */ if (width < 1) { width = 1; } if (height < 1) { height = 1; } rectPtr->x = (short)(x + tabPtr->padLeft); rectPtr->y = (short)(y + tabPtr->padTop); rectPtr->width = (short)width; rectPtr->height = (short)height; } static void ArrangeWindow(tkwin, rectPtr, force) Tk_Window tkwin; XRectangle *rectPtr; int force; { if ((force) || (rectPtr->x != Tk_X(tkwin)) || (rectPtr->y != Tk_Y(tkwin)) || (rectPtr->width != Tk_Width(tkwin)) || (rectPtr->height != Tk_Height(tkwin))) { Tk_MoveResizeWindow(tkwin, rectPtr->x, rectPtr->y, rectPtr->width, rectPtr->height); } if (!Tk_IsMapped(tkwin)) { Tk_MapWindow(tkwin); } } /*ARGSUSED*/ static void GetTags(table, object, context, list) Blt_BindTable table; ClientData object; ClientData context; Blt_List list; { Tab *tabPtr = (Tab *)object; Notebook *nbPtr; nbPtr = (Notebook *)table->clientData; if (context == TAB_PERFORATION) { Blt_ListAppend(list, MakeTag(nbPtr, "Perforation"), 0); } else if (context == TAB_LABEL) { Blt_ListAppend(list, MakeTag(nbPtr, tabPtr->name), 0); if (tabPtr->tags != NULL) { int nNames; char **names; register char **p; /* * This is a space/time trade-off in favor of space. The tags * are stored as character strings in a hash table. That way, * tabs can share the strings. It's likely that they will. The * down side is that the same string is split over and over again. */ if (Tcl_SplitList((Tcl_Interp *)NULL, tabPtr->tags, &nNames, &names) == TCL_OK) { for (p = names; *p != NULL; p++) { Blt_ListAppend(list, MakeTag(nbPtr, *p), 0); } Blt_Free(names); } } } } /* * -------------------------------------------------------------- * * NotebookEventProc -- * * This procedure is invoked by the Tk dispatcher for various * events on notebook widgets. * * Results: * None. * * Side Effects: * When the window gets deleted, internal structures get * cleaned up. When it gets exposed, it is redisplayed. * * -------------------------------------------------------------- */ static void NotebookEventProc(clientData, eventPtr) ClientData clientData; /* Information about window. */ XEvent *eventPtr; /* Information about event. */ { Notebook *nbPtr = clientData; switch (eventPtr->type) { case Expose: if (eventPtr->xexpose.count == 0) { EventuallyRedraw(nbPtr); } break; case ConfigureNotify: nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL); EventuallyRedraw(nbPtr); break; case FocusIn: case FocusOut: if (eventPtr->xfocus.detail != NotifyInferior) { if (eventPtr->type == FocusIn) { nbPtr->flags |= TNB_FOCUS; } else { nbPtr->flags &= ~TNB_FOCUS; } EventuallyRedraw(nbPtr); } break; case DestroyNotify: if (nbPtr->tkwin != NULL) { nbPtr->tkwin = NULL; Tcl_DeleteCommandFromToken(nbPtr->interp, nbPtr->cmdToken); } if (nbPtr->flags & TNB_REDRAW) { Tcl_CancelIdleCall(DisplayNotebook, nbPtr); } Tcl_EventuallyFree(nbPtr, DestroyNotebook); break; } } /* * ---------------------------------------------------------------------- * * DestroyNotebook -- * * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release * to clean up the internal structure of the widget at a safe * time (when no-one is using it anymore). * * Results: * None. * * Side Effects: * Everything associated with the widget is freed up. * * ---------------------------------------------------------------------- */ static void DestroyNotebook(dataPtr) DestroyData dataPtr; /* Pointer to the widget record. */ { Notebook *nbPtr = (Notebook *)dataPtr; Tab *tabPtr; Blt_ChainLink *linkPtr; if (nbPtr->highlightGC != NULL) { Tk_FreeGC(nbPtr->display, nbPtr->highlightGC); } if (nbPtr->tile != NULL) { Blt_FreeTile(nbPtr->tile); } if (nbPtr->defTabStyle.activeGC != NULL) { Blt_FreePrivateGC(nbPtr->display, nbPtr->defTabStyle.activeGC); } for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); tabPtr->linkPtr = NULL; DestroyTab(nbPtr, tabPtr); } Blt_ChainDestroy(nbPtr->chainPtr); Blt_DestroyBindingTable(nbPtr->bindTable); Blt_DeleteHashTable(&(nbPtr->tabTable)); Blt_DeleteHashTable(&(nbPtr->tagTable)); Tk_FreeOptions(configSpecs, (char *)nbPtr, nbPtr->display, 0); Blt_Free(nbPtr); } /* * ---------------------------------------------------------------------- * * CreateNotebook -- * * ---------------------------------------------------------------------- */ static Notebook * CreateNotebook(interp, tkwin) Tcl_Interp *interp; Tk_Window tkwin; { Notebook *nbPtr; nbPtr = Blt_Calloc(1, sizeof(Notebook)); assert(nbPtr); Tk_SetClass(tkwin, "Tabnotebook"); nbPtr->tkwin = tkwin; nbPtr->display = Tk_Display(tkwin); nbPtr->interp = interp; nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL); nbPtr->side = SIDE_TOP; nbPtr->borderWidth = nbPtr->highlightWidth = 2; nbPtr->ySelectPad = SELECT_PADY; nbPtr->xSelectPad = SELECT_PADX; nbPtr->relief = TK_RELIEF_SUNKEN; nbPtr->defTabStyle.relief = TK_RELIEF_RAISED; nbPtr->defTabStyle.borderWidth = 1; nbPtr->defTabStyle.constWidth = TRUE; nbPtr->defTabStyle.textSide = SIDE_LEFT; nbPtr->scrollUnits = 2; nbPtr->corner = CORNER_OFFSET; nbPtr->gap = GAP; nbPtr->outerPad = OUTER_PAD; nbPtr->slant = SLANT_NONE; nbPtr->overlap = 0; nbPtr->tearoff = TRUE; nbPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, nbPtr, PickTab, GetTags); nbPtr->chainPtr = Blt_ChainCreate(); Blt_InitHashTable(&(nbPtr->tabTable), BLT_STRING_KEYS); Blt_InitHashTable(&(nbPtr->imageTable), BLT_STRING_KEYS); Blt_InitHashTable(&(nbPtr->tagTable), BLT_STRING_KEYS); #if (TK_MAJOR_VERSION > 4) Blt_SetWindowInstanceData(tkwin, nbPtr); #endif return nbPtr; } /* * ---------------------------------------------------------------------- * * ConfigureNotebook -- * * This procedure is called to process an argv/argc list, plus * the Tk option database, in order to configure (or reconfigure) * the widget. * * Results: * The return value is a standard Tcl result. If TCL_ERROR is * returned, then interp->result contains an error message. * * Side Effects: * Configuration information, such as text string, colors, font, * etc. get set for nbPtr; old resources get freed, if there * were any. The widget is redisplayed. * * ---------------------------------------------------------------------- */ static int ConfigureNotebook(interp, nbPtr, argc, argv, flags) Tcl_Interp *interp; /* Interpreter to report errors. */ Notebook *nbPtr; /* Information about widget; may or * may not already have values for * some fields. */ int argc; char **argv; int flags; { XGCValues gcValues; unsigned long gcMask; GC newGC; lastNotebookInstance = nbPtr; if (Tk_ConfigureWidget(interp, nbPtr->tkwin, configSpecs, argc, argv, (char *)nbPtr, flags) != TCL_OK) { return TCL_ERROR; } if (Blt_ConfigModified(configSpecs, "-width", "-height", "-side", "-gap", "-slant", (char *)NULL)) { nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL); } if ((nbPtr->reqHeight > 0) && (nbPtr->reqWidth > 0)) { Tk_GeometryRequest(nbPtr->tkwin, nbPtr->reqWidth, nbPtr->reqHeight); } /* * GC for focus highlight. */ gcMask = GCForeground; gcValues.foreground = nbPtr->highlightColor->pixel; newGC = Tk_GetGC(nbPtr->tkwin, gcMask, &gcValues); if (nbPtr->highlightGC != NULL) { Tk_FreeGC(nbPtr->display, nbPtr->highlightGC); } nbPtr->highlightGC = newGC; /* * GC for tiled background. */ if (nbPtr->tile != NULL) { Blt_SetTileChangedProc(nbPtr->tile, TileChangedProc, nbPtr); } /* * GC for active line. */ gcMask = GCForeground | GCLineWidth | GCLineStyle | GCCapStyle; gcValues.foreground = nbPtr->defTabStyle.activeFgColor->pixel; gcValues.line_width = 0; gcValues.cap_style = CapProjecting; gcValues.line_style = (LineIsDashed(nbPtr->defTabStyle.dashes)) ? LineOnOffDash : LineSolid; newGC = Blt_GetPrivateGC(nbPtr->tkwin, gcMask, &gcValues); if (LineIsDashed(nbPtr->defTabStyle.dashes)) { nbPtr->defTabStyle.dashes.offset = 2; Blt_SetDashes(nbPtr->display, newGC, &(nbPtr->defTabStyle.dashes)); } if (nbPtr->defTabStyle.activeGC != NULL) { Blt_FreePrivateGC(nbPtr->display, nbPtr->defTabStyle.activeGC); } nbPtr->defTabStyle.activeGC = newGC; nbPtr->defTabStyle.rotate = FMOD(nbPtr->defTabStyle.rotate, 360.0); if (nbPtr->defTabStyle.rotate < 0.0) { nbPtr->defTabStyle.rotate += 360.0; } nbPtr->inset = nbPtr->highlightWidth + nbPtr->borderWidth + nbPtr->outerPad; if (Blt_ConfigModified(configSpecs, "-font", "-*foreground", "-rotate", "-*background", "-side", (char *)NULL)) { Blt_ChainLink *linkPtr; Tab *tabPtr; for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); ConfigureTab(nbPtr, tabPtr); } nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL); } nbPtr->inset2 = nbPtr->defTabStyle.borderWidth + nbPtr->corner; EventuallyRedraw(nbPtr); return TCL_OK; } /* * -------------------------------------------------------------- * * Notebook operations * * -------------------------------------------------------------- */ /* *---------------------------------------------------------------------- * * ActivateOp -- * * Selects the tab to appear active. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int ActivateOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; /* Not used. */ char **argv; { Tab *tabPtr; if (argv[2][0] == '\0') { tabPtr = NULL; } else if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) { return TCL_ERROR; } if ((tabPtr != NULL) && (tabPtr->state == STATE_DISABLED)) { tabPtr = NULL; } if (tabPtr != nbPtr->activePtr) { nbPtr->activePtr = tabPtr; EventuallyRedraw(nbPtr); } return TCL_OK; } /* *---------------------------------------------------------------------- * * BindOp -- * * .t bind index sequence command * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int BindOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; /* Not used. */ char **argv; { if (argc == 2) { Blt_HashEntry *hPtr; Blt_HashSearch cursor; char *tagName; for (hPtr = Blt_FirstHashEntry(&(nbPtr->tagTable), &cursor); hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { tagName = Blt_GetHashKey(&(nbPtr->tagTable), hPtr); Tcl_AppendElement(interp, tagName); } return TCL_OK; } return Blt_ConfigureBindings(interp, nbPtr->bindTable, MakeTag(nbPtr, argv[2]), argc - 3, argv + 3); } /* *---------------------------------------------------------------------- * * CgetOp -- * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int CgetOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; /* Not used. */ char **argv; { lastNotebookInstance = nbPtr; return Tk_ConfigureValue(interp, nbPtr->tkwin, configSpecs, (char *)nbPtr, argv[2], 0); } /* *---------------------------------------------------------------------- * * ConfigureOp -- * * This procedure is called to process an argv/argc list, plus * the Tk option database, in order to configure (or reconfigure) * the widget. * * Results: * A standard Tcl result. If TCL_ERROR is returned, then * interp->result contains an error message. * * Side Effects: * Configuration information, such as text string, colors, font, * etc. get set for nbPtr; old resources get freed, if there * were any. The widget is redisplayed. * *---------------------------------------------------------------------- */ static int ConfigureOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; char **argv; { lastNotebookInstance = nbPtr; if (argc == 2) { return Tk_ConfigureInfo(interp, nbPtr->tkwin, configSpecs, (char *)nbPtr, (char *)NULL, 0); } else if (argc == 3) { return Tk_ConfigureInfo(interp, nbPtr->tkwin, configSpecs, (char *)nbPtr, argv[2], 0); } if (ConfigureNotebook(interp, nbPtr, argc - 2, argv + 2, TK_CONFIG_ARGV_ONLY) != TCL_OK) { return TCL_ERROR; } EventuallyRedraw(nbPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * DeleteOp -- * * Deletes tab from the set. Deletes either a range of * tabs or a single node. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int DeleteOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; /* Not used. */ char **argv; { Tab *firstPtr, *lastPtr; lastPtr = NULL; if (GetTab(nbPtr, argv[2], &firstPtr, INVALID_FAIL) != TCL_OK) { return TCL_ERROR; } if ((argc == 4) && (GetTab(nbPtr, argv[3], &lastPtr, INVALID_FAIL) != TCL_OK)) { return TCL_ERROR; } if (lastPtr == NULL) { DestroyTab(nbPtr, firstPtr); } else { Tab *tabPtr; Blt_ChainLink *linkPtr, *nextLinkPtr; tabPtr = NULL; /* Suppress compiler warning. */ /* Make sure that the first tab is before the last. */ for (linkPtr = firstPtr->linkPtr; linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); if (tabPtr == lastPtr) { break; } } if (tabPtr != lastPtr) { return TCL_OK; } linkPtr = firstPtr->linkPtr; while (linkPtr != NULL) { nextLinkPtr = Blt_ChainNextLink(linkPtr); tabPtr = Blt_ChainGetValue(linkPtr); DestroyTab(nbPtr, tabPtr); linkPtr = nextLinkPtr; if (tabPtr == lastPtr) { break; } } } nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL); EventuallyRedraw(nbPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * FocusOp -- * * Selects the tab to get focus. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int FocusOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; /* Not used. */ char **argv; { Tab *tabPtr; if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_FAIL) != TCL_OK) { return TCL_ERROR; } if (tabPtr != NULL) { nbPtr->focusPtr = tabPtr; Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr, NULL); EventuallyRedraw(nbPtr); } return TCL_OK; } /* *---------------------------------------------------------------------- * * IndexOp -- * * Converts a string representing a tab index. * * Results: * A standard Tcl result. Interp->result will contain the * identifier of each index found. If an index could not be found, * then the serial identifier will be the empty string. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int IndexOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; char **argv; { Tab *tabPtr; if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) { return TCL_ERROR; } if (tabPtr != NULL) { Tcl_SetResult(interp, Blt_Itoa(TabIndex(nbPtr, tabPtr)), TCL_VOLATILE); } return TCL_OK; } /* *---------------------------------------------------------------------- * * IdOp -- * * Converts a tab index into the tab identifier. * * Results: * A standard Tcl result. Interp->result will contain the * identifier of each index found. If an index could not be found, * then the serial identifier will be the empty string. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int IdOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; /* Not used. */ char **argv; { Tab *tabPtr; if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) { return TCL_ERROR; } if (tabPtr == NULL) { Tcl_SetResult(interp, "", TCL_STATIC); } else { Tcl_SetResult(interp, tabPtr->name, TCL_VOLATILE); } return TCL_OK; } /* *---------------------------------------------------------------------- * * InsertOp -- * * Add new entries into a tab set. * * .t insert end label option-value label option-value... * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int InsertOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; /* Not used. */ char **argv; { Tab *tabPtr; Blt_ChainLink *linkPtr, *beforeLinkPtr; char c; c = argv[2][0]; if ((c == 'e') && (strcmp(argv[2], "end") == 0)) { beforeLinkPtr = NULL; } else if (isdigit(UCHAR(c))) { int position; if (Tcl_GetInt(interp, argv[2], &position) != TCL_OK) { return TCL_ERROR; } if (position < 0) { beforeLinkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); } else if (position > Blt_ChainGetLength(nbPtr->chainPtr)) { beforeLinkPtr = NULL; } else { beforeLinkPtr = Blt_ChainGetNthLink(nbPtr->chainPtr, position); } } else { Tab *beforePtr; if (GetTab(nbPtr, argv[2], &beforePtr, INVALID_FAIL) != TCL_OK) { return TCL_ERROR; } beforeLinkPtr = beforePtr->linkPtr; } nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL); EventuallyRedraw(nbPtr); tabPtr = CreateTab(nbPtr); if (tabPtr == NULL) { return TCL_ERROR; } lastNotebookInstance = nbPtr; if (Blt_ConfigureWidgetComponent(interp, nbPtr->tkwin, tabPtr->name, "Tab", tabConfigSpecs, argc - 3, argv + 3, (char *)tabPtr, 0) != TCL_OK) { DestroyTab(nbPtr, tabPtr); return TCL_ERROR; } if (ConfigureTab(nbPtr, tabPtr) != TCL_OK) { DestroyTab(nbPtr, tabPtr); return TCL_ERROR; } linkPtr = Blt_ChainNewLink(); if (beforeLinkPtr == NULL) { Blt_ChainAppendLink(nbPtr->chainPtr, linkPtr); } else { Blt_ChainLinkBefore(nbPtr->chainPtr, linkPtr, beforeLinkPtr); } tabPtr->linkPtr = linkPtr; Blt_ChainSetValue(linkPtr, tabPtr); Tcl_SetResult(interp, tabPtr->name, TCL_VOLATILE); return TCL_OK; } /* * Preprocess the command string for percent substitution. */ static void PercentSubst(nbPtr, tabPtr, command, resultPtr) Notebook *nbPtr; Tab *tabPtr; char *command; Tcl_DString *resultPtr; { register char *last, *p; /* * Get the full path name of the node, in case we need to * substitute for it. */ Tcl_DStringInit(resultPtr); for (last = p = command; *p != '\0'; p++) { if (*p == '%') { char *string; char buf[3]; if (p > last) { *p = '\0'; Tcl_DStringAppend(resultPtr, last, -1); *p = '%'; } switch (*(p + 1)) { case '%': /* Percent sign */ string = "%"; break; case 'W': /* Widget name */ string = Tk_PathName(nbPtr->tkwin); break; case 'i': /* Tab Index */ string = Blt_Itoa(TabIndex(nbPtr, tabPtr)); break; case 'n': /* Tab name */ string = tabPtr->name; break; default: if (*(p + 1) == '\0') { p--; } buf[0] = *p, buf[1] = *(p + 1), buf[2] = '\0'; string = buf; break; } Tcl_DStringAppend(resultPtr, string, -1); p++; last = p + 1; } } if (p > last) { *p = '\0'; Tcl_DStringAppend(resultPtr, last, -1); } } /* *---------------------------------------------------------------------- * * InvokeOp -- * * This procedure is called to invoke a selection command. * * .h invoke index * * Results: * A standard Tcl result. If TCL_ERROR is returned, then * interp->result contains an error message. * * Side Effects: * Configuration information, such as text string, colors, font, * etc. get set; old resources get freed, if there were any. * The widget is redisplayed if needed. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int InvokeOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; /* Not used. */ int argc; char **argv; { Tab *tabPtr; char *command; if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) { return TCL_ERROR; } if ((tabPtr == NULL) || (tabPtr->state == STATE_DISABLED)) { return TCL_OK; } Tcl_Preserve(tabPtr); command = GETATTR(tabPtr, command); if (command != NULL) { Tcl_DString dString; int result; PercentSubst(nbPtr, tabPtr, command, &dString); result = Tcl_GlobalEval(nbPtr->interp, Tcl_DStringValue(&dString)); Tcl_DStringFree(&dString); if (result != TCL_OK) { return TCL_ERROR; } } Tcl_Release(tabPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * MoveOp -- * * Moves a tab to a new location. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int MoveOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; /* Not used. */ char **argv; { Tab *tabPtr, *linkPtr; int before; if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) { return TCL_ERROR; } if ((tabPtr == NULL) || (tabPtr->state == STATE_DISABLED)) { return TCL_OK; } if ((argv[3][0] == 'b') && (strcmp(argv[3], "before") == 0)) { before = 1; } else if ((argv[3][0] == 'a') && (strcmp(argv[3], "after") == 0)) { before = 0; } else { Tcl_AppendResult(interp, "bad key word \"", argv[3], "\": should be \"after\" or \"before\"", (char *)NULL); return TCL_ERROR; } if (GetTab(nbPtr, argv[4], &linkPtr, INVALID_FAIL) != TCL_OK) { return TCL_ERROR; } if (tabPtr == linkPtr) { return TCL_OK; } Blt_ChainUnlinkLink(nbPtr->chainPtr, tabPtr->linkPtr); if (before) { Blt_ChainLinkBefore(nbPtr->chainPtr, tabPtr->linkPtr, linkPtr->linkPtr); } else { Blt_ChainLinkAfter(nbPtr->chainPtr, tabPtr->linkPtr, linkPtr->linkPtr); } nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL); EventuallyRedraw(nbPtr); return TCL_OK; } /*ARGSUSED*/ static int NearestOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; /* Not used. */ char **argv; { int x, y; /* Screen coordinates of the test point. */ Tab *tabPtr; if ((Tk_GetPixels(interp, nbPtr->tkwin, argv[2], &x) != TCL_OK) || (Tk_GetPixels(interp, nbPtr->tkwin, argv[3], &y) != TCL_OK)) { return TCL_ERROR; } if (nbPtr->nVisible > 0) { tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL); if (tabPtr != NULL) { Tcl_SetResult(interp, tabPtr->name, TCL_VOLATILE); } } return TCL_OK; } /* *---------------------------------------------------------------------- * * SelectOp -- * * This procedure is called to selecta tab. * * .h select index * * Results: * A standard Tcl result. If TCL_ERROR is returned, then * interp->result contains an error message. * * Side Effects: * Configuration information, such as text string, colors, font, * etc. get set; old resources get freed, if there were any. * The widget is redisplayed if needed. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int SelectOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; /* Not used. */ int argc; char **argv; { Tab *tabPtr; if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) { return TCL_ERROR; } if ((tabPtr == NULL) || (tabPtr->state == STATE_DISABLED)) { return TCL_OK; } if ((nbPtr->selectPtr != NULL) && (nbPtr->selectPtr != tabPtr) && (nbPtr->selectPtr->tkwin != NULL)) { if (nbPtr->selectPtr->container == NULL) { if (Tk_IsMapped(nbPtr->selectPtr->tkwin)) { Tk_UnmapWindow(nbPtr->selectPtr->tkwin); } } else { /* Redraw now unselected container. */ EventuallyRedrawTearoff(nbPtr->selectPtr); } } nbPtr->selectPtr = tabPtr; if ((nbPtr->nTiers > 1) && (tabPtr->tier != nbPtr->startPtr->tier)) { RenumberTiers(nbPtr, tabPtr); Blt_PickCurrentItem(nbPtr->bindTable); } nbPtr->flags |= (TNB_SCROLL); if (tabPtr->container != NULL) { EventuallyRedrawTearoff(tabPtr); } EventuallyRedraw(nbPtr); return TCL_OK; } static int ViewOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; char **argv; { int width; width = VPORTWIDTH(nbPtr); if (argc == 2) { double fract; /* * Note: we are bounding the fractions between 0.0 and 1.0 to * support the "canvas"-style of scrolling. */ fract = (double)nbPtr->scrollOffset / nbPtr->worldWidth; Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0))); fract = (double)(nbPtr->scrollOffset + width) / nbPtr->worldWidth; Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0))); return TCL_OK; } if (Blt_GetScrollInfo(interp, argc - 2, argv + 2, &(nbPtr->scrollOffset), nbPtr->worldWidth, width, nbPtr->scrollUnits, BLT_SCROLL_MODE_CANVAS) != TCL_OK) { return TCL_ERROR; } nbPtr->flags |= TNB_SCROLL; EventuallyRedraw(nbPtr); return TCL_OK; } static void AdoptWindow(clientData) ClientData clientData; { Tab *tabPtr = clientData; int x, y; Notebook *nbPtr = tabPtr->nbPtr; x = nbPtr->inset + nbPtr->inset2 + tabPtr->padLeft; #define TEAR_OFF_TAB_SIZE 5 y = nbPtr->inset + nbPtr->inset2 + nbPtr->yPad + nbPtr->outerPad + TEAR_OFF_TAB_SIZE + tabPtr->padTop; Blt_RelinkWindow(tabPtr->tkwin, tabPtr->container, x, y); Tk_MapWindow(tabPtr->tkwin); } static void DestroyTearoff(dataPtr) DestroyData dataPtr; { Tab *tabPtr = (Tab *)dataPtr; if (tabPtr->container != NULL) { Notebook *nbPtr; Tk_Window tkwin; nbPtr = tabPtr->nbPtr; tkwin = tabPtr->container; if (tabPtr->flags & TAB_REDRAW) { Tcl_CancelIdleCall(DisplayTearoff, tabPtr); } Tk_DeleteEventHandler(tkwin, StructureNotifyMask, TearoffEventProc, tabPtr); if (tabPtr->tkwin != NULL) { XRectangle rect; GetWindowRectangle(tabPtr, nbPtr->tkwin, FALSE, &rect); Blt_RelinkWindow(tabPtr->tkwin, nbPtr->tkwin, rect.x, rect.y); if (tabPtr == nbPtr->selectPtr) { ArrangeWindow(tabPtr->tkwin, &rect, TRUE); } else { Tk_UnmapWindow(tabPtr->tkwin); } } Tk_DestroyWindow(tkwin); tabPtr->container = NULL; } } static int CreateTearoff(nbPtr, name, tabPtr) Notebook *nbPtr; char *name; Tab *tabPtr; { Tk_Window tkwin; int width, height; tkwin = Tk_CreateWindowFromPath(nbPtr->interp, nbPtr->tkwin, name, (char *)NULL); if (tkwin == NULL) { return TCL_ERROR; } tabPtr->container = tkwin; if (Tk_WindowId(tkwin) == None) { Tk_MakeWindowExist(tkwin); } Tk_SetClass(tkwin, "Tearoff"); Tk_CreateEventHandler(tkwin, (ExposureMask | StructureNotifyMask), TearoffEventProc, tabPtr); if (Tk_WindowId(tabPtr->tkwin) == None) { Tk_MakeWindowExist(tabPtr->tkwin); } width = Tk_Width(tabPtr->tkwin); if (width < 2) { width = (tabPtr->reqWidth > 0) ? tabPtr->reqWidth : Tk_ReqWidth(tabPtr->tkwin); } width += PADDING(tabPtr->padX) + 2 * Tk_Changes(tabPtr->tkwin)->border_width; width += 2 * (nbPtr->inset2 + nbPtr->inset); #define TEAR_OFF_TAB_SIZE 5 height = Tk_Height(tabPtr->tkwin); if (height < 2) { height = (tabPtr->reqHeight > 0) ? tabPtr->reqHeight : Tk_ReqHeight(tabPtr->tkwin); } height += PADDING(tabPtr->padY) + 2 * Tk_Changes(tabPtr->tkwin)->border_width; height += nbPtr->inset + nbPtr->inset2 + nbPtr->yPad + TEAR_OFF_TAB_SIZE + nbPtr->outerPad; Tk_GeometryRequest(tkwin, width, height); Tk_UnmapWindow(tabPtr->tkwin); /* Tk_MoveWindow(tabPtr->tkwin, 0, 0); */ Tcl_SetResult(nbPtr->interp, Tk_PathName(tkwin), TCL_VOLATILE); #ifdef WIN32 AdoptWindow(tabPtr); #else Tcl_DoWhenIdle(AdoptWindow, tabPtr); #endif return TCL_OK; } /* *---------------------------------------------------------------------- * * TabCgetOp -- * * .h tab cget index option * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int TabCgetOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; /* Not used. */ char **argv; { Tab *tabPtr; if (GetTab(nbPtr, argv[3], &tabPtr, INVALID_FAIL) != TCL_OK) { return TCL_ERROR; } lastNotebookInstance = nbPtr; return Tk_ConfigureValue(interp, nbPtr->tkwin, tabConfigSpecs, (char *)tabPtr, argv[4], 0); } /* *---------------------------------------------------------------------- * * TabConfigureOp -- * * This procedure is called to process a list of configuration * options database, in order to reconfigure the options for * one or more tabs in the widget. * * .h tab configure index ?index...? ?option value?... * * Results: * A standard Tcl result. If TCL_ERROR is returned, then * interp->result contains an error message. * * Side Effects: * Configuration information, such as text string, colors, font, * etc. get set; old resources get freed, if there were any. * The widget is redisplayed if needed. * *---------------------------------------------------------------------- */ static int TabConfigureOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; char **argv; { int nTabs, nOpts, result; char **options; register int i; Tab *tabPtr; /* Figure out where the option value pairs begin */ argc -= 3; argv += 3; for (i = 0; i < argc; i++) { if (argv[i][0] == '-') { break; } if (GetTab(nbPtr, argv[i], &tabPtr, INVALID_FAIL) != TCL_OK) { return TCL_ERROR; /* Can't find node. */ } } nTabs = i; /* Number of tab indices specified */ nOpts = argc - i; /* Number of options specified */ options = argv + i; /* Start of options in argv */ for (i = 0; i < nTabs; i++) { GetTab(nbPtr, argv[i], &tabPtr, INVALID_FAIL); if (argc == 1) { return Tk_ConfigureInfo(interp, nbPtr->tkwin, tabConfigSpecs, (char *)tabPtr, (char *)NULL, 0); } else if (argc == 2) { return Tk_ConfigureInfo(interp, nbPtr->tkwin, tabConfigSpecs, (char *)tabPtr, argv[2], 0); } Tcl_Preserve(tabPtr); lastNotebookInstance = nbPtr; result = Tk_ConfigureWidget(interp, nbPtr->tkwin, tabConfigSpecs, nOpts, options, (char *)tabPtr, TK_CONFIG_ARGV_ONLY); if (result == TCL_OK) { result = ConfigureTab(nbPtr, tabPtr); } Tcl_Release(tabPtr); if (result == TCL_ERROR) { return TCL_ERROR; } if (tabPtr->flags & TAB_VISIBLE) { nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL); EventuallyRedraw(nbPtr); } } return TCL_OK; } /* *---------------------------------------------------------------------- * * TabDockallOp -- * * .h tab dockall * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int TabDockallOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; /* Not used. */ char **argv; /* Not used. */ { Tab *tabPtr; Blt_ChainLink *linkPtr; for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); if (tabPtr->container != NULL) { Tcl_EventuallyFree(tabPtr, DestroyTearoff); } } return TCL_OK; } /* *---------------------------------------------------------------------- * * TabNamesOp -- * * .h tab names pattern * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int TabNamesOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; /* Not used. */ char **argv; /* Not used. */ { Tab *tabPtr; Blt_ChainLink *linkPtr; if (argc == 3) { for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); Tcl_AppendElement(interp, tabPtr->name); } } else { register int i; for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); for (i = 3; i < argc; i++) { if (Tcl_StringMatch(tabPtr->name, argv[i])) { Tcl_AppendElement(interp, tabPtr->name); break; } } } } return TCL_OK; } /* *---------------------------------------------------------------------- * * TabTearoffOp -- * * .h tab tearoff index ?title? * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int TabTearoffOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; /* Not used. */ char **argv; { Tab *tabPtr; int result; Tk_Window tkwin; if (GetTab(nbPtr, argv[3], &tabPtr, INVALID_OK) != TCL_OK) { return TCL_ERROR; } if ((tabPtr == NULL) || (tabPtr->tkwin == NULL) || (tabPtr->state == STATE_DISABLED)) { return TCL_OK; /* No-op */ } if (argc == 4) { Tk_Window parent; parent = (tabPtr->container == NULL) ? nbPtr->tkwin : tabPtr->container; Tcl_SetResult(nbPtr->interp, Tk_PathName(parent), TCL_VOLATILE); return TCL_OK; } Tcl_Preserve(tabPtr); result = TCL_OK; tkwin = Tk_NameToWindow(interp, argv[4], nbPtr->tkwin); Tcl_ResetResult(interp); if (tabPtr->container != NULL) { Tcl_EventuallyFree(tabPtr, DestroyTearoff); } if ((tkwin != nbPtr->tkwin) && (tabPtr->container == NULL)) { result = CreateTearoff(nbPtr, argv[4], tabPtr); } Tcl_Release(tabPtr); EventuallyRedraw(nbPtr); return result; } /* *---------------------------------------------------------------------- * * TabOp -- * * This procedure handles tab operations. * * Results: * A standard Tcl result. * *---------------------------------------------------------------------- */ static Blt_OpSpec tabOps[] = { {"cget", 2, (Blt_Op)TabCgetOp, 5, 5, "nameOrIndex option",}, {"configure", 2, (Blt_Op)TabConfigureOp, 4, 0, "nameOrIndex ?option value?...",}, {"dockall", 1, (Blt_Op)TabDockallOp, 3, 3, "" }, {"names", 1, (Blt_Op)TabNamesOp, 3, 0, "?pattern...?",}, {"tearoff", 1, (Blt_Op)TabTearoffOp, 4, 5, "index ?parent?",}, }; static int nTabOps = sizeof(tabOps) / sizeof(Blt_OpSpec); static int TabOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; char **argv; { Blt_Op proc; int result; proc = Blt_GetOp(interp, nTabOps, tabOps, BLT_OP_ARG2, argc, argv, 0); if (proc == NULL) { return TCL_ERROR; } result = (*proc) (nbPtr, interp, argc, argv); return result; } /* *---------------------------------------------------------------------- * * PerforationActivateOp -- * * This procedure is called to highlight the perforation. * * .h perforation highlight boolean * * Results: * A standard Tcl result. If TCL_ERROR is returned, then * interp->result contains an error message. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int PerforationActivateOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; /* Not used. */ int argc; char **argv; { int bool; if (Tcl_GetBoolean(interp, argv[3], &bool) != TCL_OK) { return TCL_ERROR; } if (bool) { nbPtr->flags |= PERFORATION_ACTIVE; } else { nbPtr->flags &= ~PERFORATION_ACTIVE; } EventuallyRedraw(nbPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * PerforationInvokeOp -- * * This procedure is called to invoke a perforation command. * * .t perforation invoke * * Results: * A standard Tcl result. If TCL_ERROR is returned, then * interp->result contains an error message. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int PerforationInvokeOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; /* Not used. */ int argc; char **argv; { if (nbPtr->selectPtr != NULL) { char *cmd; cmd = GETATTR(nbPtr->selectPtr, perfCommand); if (cmd != NULL) { Tcl_DString dString; int result; PercentSubst(nbPtr, nbPtr->selectPtr, cmd, &dString); Tcl_Preserve(nbPtr); result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)); Tcl_Release(nbPtr); Tcl_DStringFree(&dString); if (result != TCL_OK) { return TCL_ERROR; } } } return TCL_OK; } /* *---------------------------------------------------------------------- * * PerforationOp -- * * This procedure handles tab operations. * * Results: * A standard Tcl result. * *---------------------------------------------------------------------- */ static Blt_OpSpec perforationOps[] = { {"activate", 1, (Blt_Op)PerforationActivateOp, 4, 4, "boolean" }, {"invoke", 1, (Blt_Op)PerforationInvokeOp, 3, 3, "",}, }; static int nPerforationOps = sizeof(perforationOps) / sizeof(Blt_OpSpec); static int PerforationOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; char **argv; { Blt_Op proc; int result; proc = Blt_GetOp(interp, nPerforationOps, perforationOps, BLT_OP_ARG2, argc, argv, 0); if (proc == NULL) { return TCL_ERROR; } result = (*proc) (nbPtr, interp, argc, argv); return result; } /* *---------------------------------------------------------------------- * * ScanOp -- * * Implements the quick scan. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int ScanOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; /* Not used. */ char **argv; { int x, y; char c; unsigned int length; int oper; #define SCAN_MARK 1 #define SCAN_DRAGTO 2 c = argv[2][0]; length = strlen(argv[2]); if ((c == 'm') && (strncmp(argv[2], "mark", length) == 0)) { oper = SCAN_MARK; } else if ((c == 'd') && (strncmp(argv[2], "dragto", length) == 0)) { oper = SCAN_DRAGTO; } else { Tcl_AppendResult(interp, "bad scan operation \"", argv[2], "\": should be either \"mark\" or \"dragto\"", (char *)NULL); return TCL_ERROR; } if ((Tk_GetPixels(interp, nbPtr->tkwin, argv[3], &x) != TCL_OK) || (Tk_GetPixels(interp, nbPtr->tkwin, argv[4], &y) != TCL_OK)) { return TCL_ERROR; } if (oper == SCAN_MARK) { if (nbPtr->side & SIDE_VERTICAL) { nbPtr->scanAnchor = y; } else { nbPtr->scanAnchor = x; } nbPtr->scanOffset = nbPtr->scrollOffset; } else { int offset, delta; if (nbPtr->side & SIDE_VERTICAL) { delta = nbPtr->scanAnchor - y; } else { delta = nbPtr->scanAnchor - x; } offset = nbPtr->scanOffset + (10 * delta); offset = Blt_AdjustViewport(offset, nbPtr->worldWidth, VPORTWIDTH(nbPtr), nbPtr->scrollUnits, BLT_SCROLL_MODE_CANVAS); nbPtr->scrollOffset = offset; nbPtr->flags |= TNB_SCROLL; EventuallyRedraw(nbPtr); } return TCL_OK; } /*ARGSUSED*/ static int SeeOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; /* Not used. */ int argc; char **argv; { Tab *tabPtr; if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) { return TCL_ERROR; } if (tabPtr != NULL) { int left, right, width; width = VPORTWIDTH(nbPtr); left = nbPtr->scrollOffset + nbPtr->xSelectPad; right = nbPtr->scrollOffset + width - nbPtr->xSelectPad; /* If the tab is partially obscured, scroll so that it's * entirely in view. */ if (tabPtr->worldX < left) { nbPtr->scrollOffset = tabPtr->worldX; if (TabIndex(nbPtr, tabPtr) > 0) { nbPtr->scrollOffset -= TAB_SCROLL_OFFSET; } } else if ((tabPtr->worldX + tabPtr->worldWidth) >= right) { Blt_ChainLink *linkPtr; nbPtr->scrollOffset = tabPtr->worldX + tabPtr->worldWidth - (width - 2 * nbPtr->xSelectPad); linkPtr = Blt_ChainNextLink(tabPtr->linkPtr); if (linkPtr != NULL) { Tab *nextPtr; nextPtr = Blt_ChainGetValue(linkPtr); if (nextPtr->tier == tabPtr->tier) { nbPtr->scrollOffset += TAB_SCROLL_OFFSET; } } } nbPtr->flags |= TNB_SCROLL; EventuallyRedraw(nbPtr); } return TCL_OK; } /*ARGSUSED*/ static int SizeOp(nbPtr, interp, argc, argv) Notebook *nbPtr; Tcl_Interp *interp; int argc; /* Not used. */ char **argv; /* Not used. */ { Tcl_SetResult(interp, Blt_Itoa(Blt_ChainGetLength(nbPtr->chainPtr)), TCL_VOLATILE); return TCL_OK; } static int CountTabs(nbPtr) Notebook *nbPtr; { int count; int width, height; Blt_ChainLink *linkPtr; register Tab *tabPtr; register int pageWidth, pageHeight; int labelWidth, labelHeight; int tabWidth, tabHeight; pageWidth = pageHeight = 0; count = 0; labelWidth = labelHeight = 0; /* * Pass 1: Figure out the maximum area needed for a label and a * page. Both the label and page dimensions are adjusted * for orientation. In addition, reset the visibility * flags and reorder the tabs. */ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); /* Reset visibility flag and order of tabs. */ tabPtr->flags &= ~TAB_VISIBLE; count++; if (tabPtr->tkwin != NULL) { width = GetReqWidth(tabPtr); if (pageWidth < width) { pageWidth = width; } height = GetReqHeight(tabPtr); if (pageHeight < height) { pageHeight = height; } } if (labelWidth < tabPtr->labelWidth) { labelWidth = tabPtr->labelWidth; } if (labelHeight < tabPtr->labelHeight) { labelHeight = tabPtr->labelHeight; } } nbPtr->overlap = 0; /* * Pass 2: Set the individual sizes of each tab. This is different * for constant and variable width tabs. Add the extra space * needed for slanted tabs, now that we know maximum tab * height. */ if (nbPtr->defTabStyle.constWidth) { int slant; tabWidth = 2 * nbPtr->inset2; tabHeight = nbPtr->inset2 /* + 4 */; if (nbPtr->side & SIDE_VERTICAL) { tabWidth += labelHeight; tabHeight += labelWidth; slant = labelWidth; } else { tabWidth += labelWidth; tabHeight += labelHeight; slant = labelHeight; } if (nbPtr->slant & SLANT_LEFT) { tabWidth += slant; nbPtr->overlap += tabHeight / 2; } if (nbPtr->slant & SLANT_RIGHT) { tabWidth += slant; nbPtr->overlap += tabHeight / 2; } for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); tabPtr->worldWidth = tabWidth; tabPtr->worldHeight = tabHeight; } } else { int slant; tabWidth = tabHeight = 0; for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); width = 2 * nbPtr->inset2; height = nbPtr->inset2 /* + 4 */; if (nbPtr->side & SIDE_VERTICAL) { width += tabPtr->labelHeight; height += labelWidth; slant = labelWidth; } else { width += tabPtr->labelWidth; height += labelHeight; slant = labelHeight; } width += (nbPtr->slant & SLANT_LEFT) ? slant : nbPtr->corner; width += (nbPtr->slant & SLANT_RIGHT) ? slant : nbPtr->corner; tabPtr->worldWidth = width; /* + 2 * (nbPtr->corner + nbPtr->xSelectPad) */ ; tabPtr->worldHeight = height; if (tabWidth < width) { tabWidth = width; } if (tabHeight < height) { tabHeight = height; } } if (nbPtr->slant & SLANT_LEFT) { nbPtr->overlap += tabHeight / 2; } if (nbPtr->slant & SLANT_RIGHT) { nbPtr->overlap += tabHeight / 2; } } nbPtr->tabWidth = tabWidth; nbPtr->tabHeight = tabHeight; /* * Let the user override any page dimension. */ nbPtr->pageWidth = pageWidth; nbPtr->pageHeight = pageHeight; if (nbPtr->reqPageWidth > 0) { nbPtr->pageWidth = nbPtr->reqPageWidth; } if (nbPtr->reqPageHeight > 0) { nbPtr->pageHeight = nbPtr->reqPageHeight; } return count; } static void WidenTabs(nbPtr, startPtr, nTabs, adjustment) Notebook *nbPtr; Tab *startPtr; int nTabs; int adjustment; { register Tab *tabPtr; register int i; int ration; Blt_ChainLink *linkPtr; int x; x = startPtr->tier; while (adjustment > 0) { ration = adjustment / nTabs; if (ration == 0) { ration = 1; } linkPtr = startPtr->linkPtr; for (i = 0; (linkPtr != NULL) && (i < nTabs) && (adjustment > 0); i++) { tabPtr = Blt_ChainGetValue(linkPtr); adjustment -= ration; tabPtr->worldWidth += ration; assert(x == tabPtr->tier); linkPtr = Blt_ChainNextLink(linkPtr); } } /* * Go back and reset the world X-coordinates of the tabs, * now that their widths have changed. */ x = 0; linkPtr = startPtr->linkPtr; for (i = 0; (i < nTabs) && (linkPtr != NULL); i++) { tabPtr = Blt_ChainGetValue(linkPtr); tabPtr->worldX = x; x += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap; linkPtr = Blt_ChainNextLink(linkPtr); } } static void AdjustTabSizes(nbPtr, nTabs) Notebook *nbPtr; int nTabs; { int tabsPerTier; int total, count, extra; Tab *startPtr, *nextPtr; Blt_ChainLink *linkPtr; register Tab *tabPtr; int x, maxWidth; tabsPerTier = (nTabs + (nbPtr->nTiers - 1)) / nbPtr->nTiers; x = 0; maxWidth = 0; if (nbPtr->defTabStyle.constWidth) { register int i; linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); count = 1; while (linkPtr != NULL) { for (i = 0; i < tabsPerTier; i++) { tabPtr = Blt_ChainGetValue(linkPtr); tabPtr->tier = count; tabPtr->worldX = x; x += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap; linkPtr = Blt_ChainNextLink(linkPtr); if (x > maxWidth) { maxWidth = x; } if (linkPtr == NULL) { goto done; } } count++; x = 0; } } done: /* Add to tab widths to fill out row. */ if (((nTabs % tabsPerTier) != 0) && (nbPtr->defTabStyle.constWidth)) { return; } startPtr = NULL; count = total = 0; for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; /*empty*/ ) { tabPtr = Blt_ChainGetValue(linkPtr); if (startPtr == NULL) { startPtr = tabPtr; } count++; total += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap; linkPtr = Blt_ChainNextLink(linkPtr); if (linkPtr != NULL) { nextPtr = Blt_ChainGetValue(linkPtr); if (tabPtr->tier == nextPtr->tier) { continue; } } total += nbPtr->overlap; extra = nbPtr->worldWidth - total; assert(count > 0); if (extra > 0) { WidenTabs(nbPtr, startPtr, count, extra); } count = total = 0; startPtr = NULL; } } /* * * tabWidth = textWidth + gap + (2 * (pad + outerBW)); * * tabHeight = textHeight + 2 * (pad + outerBW) + topMargin; * */ static void ComputeLayout(nbPtr) Notebook *nbPtr; { int width; Blt_ChainLink *linkPtr; Tab *tabPtr; int x, extra; int nTiers, nTabs; nbPtr->nTiers = 0; nbPtr->pageTop = 0; nbPtr->worldWidth = 1; nbPtr->yPad = 0; nTabs = CountTabs(nbPtr); if (nTabs == 0) { return; } /* Reset the pointers to the selected and starting tab. */ if (nbPtr->selectPtr == NULL) { linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); if (linkPtr != NULL) { nbPtr->selectPtr = Blt_ChainGetValue(linkPtr); } } if (nbPtr->startPtr == NULL) { nbPtr->startPtr = nbPtr->selectPtr; } if (nbPtr->focusPtr == NULL) { nbPtr->focusPtr = nbPtr->selectPtr; Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr, NULL); } if (nbPtr->side & SIDE_VERTICAL) { width = Tk_Height(nbPtr->tkwin) - 2 * (nbPtr->corner + nbPtr->xSelectPad); } else { width = Tk_Width(nbPtr->tkwin) - (2 * nbPtr->inset) - nbPtr->xSelectPad - nbPtr->corner; } nbPtr->flags |= TNB_STATIC; if (nbPtr->reqTiers > 1) { int total, maxWidth; /* Static multiple tier mode. */ /* Sum tab widths and determine the number of tiers needed. */ nTiers = 1; total = x = 0; for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); if ((x + tabPtr->worldWidth) > width) { nTiers++; x = 0; } tabPtr->worldX = x; tabPtr->tier = nTiers; extra = tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap; total += extra, x += extra; } maxWidth = width; if (nTiers > nbPtr->reqTiers) { /* * The tabs do not fit into the requested number of tiers. * Go into scrolling mode. */ width = ((total + nbPtr->tabWidth) / nbPtr->reqTiers); x = 0; nTiers = 1; for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); tabPtr->tier = nTiers; /* * Keep adding tabs to a tier until we overfill it. */ tabPtr->worldX = x; x += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap; if (x > width) { nTiers++; if (x > maxWidth) { maxWidth = x; } x = 0; } } nbPtr->flags &= ~TNB_STATIC; } nbPtr->worldWidth = maxWidth; nbPtr->nTiers = nTiers; if (nTiers > 1) { AdjustTabSizes(nbPtr, nTabs); } if (nbPtr->flags & TNB_STATIC) { nbPtr->worldWidth = VPORTWIDTH(nbPtr); } else { /* Do you add an offset ? */ nbPtr->worldWidth += (nbPtr->xSelectPad + nbPtr->corner); } nbPtr->worldWidth += nbPtr->overlap; if (nbPtr->selectPtr != NULL) { RenumberTiers(nbPtr, nbPtr->selectPtr); } } else { /* * Scrollable single tier mode. */ nTiers = 1; x = 0; for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); tabPtr->tier = nTiers; tabPtr->worldX = x; tabPtr->worldY = 0; x += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap; } nbPtr->worldWidth = x + nbPtr->corner - nbPtr->gap + nbPtr->xSelectPad + nbPtr->overlap; nbPtr->flags &= ~TNB_STATIC; } if (nTiers == 1) { nbPtr->yPad = nbPtr->ySelectPad; } nbPtr->nTiers = nTiers; nbPtr->pageTop = nbPtr->inset + nbPtr->yPad /* + 4 */ + (nbPtr->nTiers * nbPtr->tabHeight) + nbPtr->inset2; if (nbPtr->side & SIDE_VERTICAL) { for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); tabPtr->screenWidth = (short int)nbPtr->tabHeight; tabPtr->screenHeight = (short int)tabPtr->worldWidth; } } else { for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); tabPtr->screenWidth = (short int)tabPtr->worldWidth; tabPtr->screenHeight = (short int)nbPtr->tabHeight; } } } static void ComputeVisibleTabs(nbPtr) Notebook *nbPtr; { int nVisibleTabs; register Tab *tabPtr; Blt_ChainLink *linkPtr; nbPtr->nVisible = 0; if (Blt_ChainGetLength(nbPtr->chainPtr) == 0) { return; } nVisibleTabs = 0; if (nbPtr->flags & TNB_STATIC) { /* Static multiple tier mode. */ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); tabPtr->flags |= TAB_VISIBLE; nVisibleTabs++; } } else { int width, offset; /* * Scrollable (single or multiple) tier mode. */ offset = nbPtr->scrollOffset - (nbPtr->outerPad + nbPtr->xSelectPad); width = VPORTWIDTH(nbPtr) + nbPtr->scrollOffset + 2 * nbPtr->outerPad; for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); if ((tabPtr->worldX >= width) || ((tabPtr->worldX + tabPtr->worldWidth) < offset)) { tabPtr->flags &= ~TAB_VISIBLE; } else { tabPtr->flags |= TAB_VISIBLE; nVisibleTabs++; } } } for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tabPtr = Blt_ChainGetValue(linkPtr); tabPtr->screenX = tabPtr->screenY = -1000; if (tabPtr->flags & TAB_VISIBLE) { WorldToScreen(nbPtr, tabPtr->worldX, tabPtr->worldY, &(tabPtr->screenX), &(tabPtr->screenY)); switch (nbPtr->side) { case SIDE_RIGHT: tabPtr->screenX -= nbPtr->tabHeight; break; case SIDE_BOTTOM: tabPtr->screenY -= nbPtr->tabHeight; break; } } } nbPtr->nVisible = nVisibleTabs; Blt_PickCurrentItem(nbPtr->bindTable); } static void Draw3DFolder(nbPtr, tabPtr, drawable, side, pointArr, nPoints) Notebook *nbPtr; Tab *tabPtr; Drawable drawable; int side; XPoint pointArr[]; int nPoints; { GC gc; int relief, borderWidth; Tk_3DBorder border; if (tabPtr == nbPtr->selectPtr) { border = GETATTR(tabPtr, selBorder); } else if (tabPtr->border != NULL) { border = tabPtr->border; } else { border = nbPtr->defTabStyle.border; } relief = nbPtr->defTabStyle.relief; if ((side == SIDE_RIGHT) || (side == SIDE_TOP)) { borderWidth = -nbPtr->defTabStyle.borderWidth; if (relief == TK_RELIEF_SUNKEN) { relief = TK_RELIEF_RAISED; } else if (relief == TK_RELIEF_RAISED) { relief = TK_RELIEF_SUNKEN; } } else { borderWidth = nbPtr->defTabStyle.borderWidth; } { int i; #ifndef notdef int dx, dy; int oldType, newType; int start; dx = pointArr[0].x - pointArr[1].x; dy = pointArr[0].y - pointArr[1].y; oldType = ((dy < 0) || (dx > 0)); start = 0; for (i = 1; i < nPoints; i++) { dx = pointArr[i - 1].x - pointArr[i].x; dy = pointArr[i - 1].y - pointArr[i].y; newType = ((dy < 0) || (dx > 0)); if (newType != oldType) { if (oldType) { gc = Tk_GCForColor(nbPtr->shadowColor, drawable); } else { gc = Tk_3DBorderGC(nbPtr->tkwin, border, TK_3D_FLAT_GC); } XDrawLines(nbPtr->display, drawable, gc, pointArr + start, i - start, CoordModeOrigin); start = i - 1; oldType = newType; } } if (start != i) { if (oldType) { gc = Tk_GCForColor(nbPtr->shadowColor, drawable); } else { gc = Tk_3DBorderGC(nbPtr->tkwin, border, TK_3D_FLAT_GC); } XDrawLines(nbPtr->display, drawable, gc, pointArr + start, i - start, CoordModeOrigin); } #else /* Draw the outline of the folder. */ gc = Tk_GCForColor(nbPtr->shadowColor, drawable); XDrawLines(nbPtr->display, drawable, gc, pointArr, nPoints, CoordModeOrigin); #endif } /* And the folder itself. */ if (tabPtr->tile != NULL) { #ifdef notdef Tk_Fill3DPolygon(nbPtr->tkwin, drawable, border, pointArr, nPoints, borderWidth, relief); #endif Blt_TilePolygon(nbPtr->tkwin, drawable, tabPtr->tile, pointArr, nPoints); #ifdef notdef Tk_Draw3DPolygon(nbPtr->tkwin, drawable, border, pointArr, nPoints, borderWidth, relief); #endif } else { Tk_Fill3DPolygon(nbPtr->tkwin, drawable, border, pointArr, nPoints, borderWidth, relief); } } /* * x,y * |1|2|3| 4 |3|2|1| * * 1. tab border width * 2. corner offset * 3. label pad * 4. label width * * */ static void DrawLabel(nbPtr, tabPtr, drawable) Notebook *nbPtr; Tab *tabPtr; Drawable drawable; { int x, y, dx, dy; int tx, ty, ix, iy; int imgWidth, imgHeight; int active, selected; XColor *fgColor, *bgColor; Tk_3DBorder border; GC gc; if (!(tabPtr->flags & TAB_VISIBLE)) { return; } x = tabPtr->screenX; y = tabPtr->screenY; active = (nbPtr->activePtr == tabPtr); selected = (nbPtr->selectPtr == tabPtr); fgColor = GETATTR(tabPtr, textColor); border = GETATTR(tabPtr, border); if (selected) { border = GETATTR(tabPtr, selBorder); } bgColor = Tk_3DBorderColor(border); if (active) { Tk_3DBorder activeBorder; activeBorder = GETATTR(tabPtr, activeBorder); bgColor = Tk_3DBorderColor(activeBorder); } dx = (tabPtr->screenWidth - tabPtr->labelWidth) / 2; dy = (tabPtr->screenHeight - tabPtr->labelHeight) / 2; /* * The label position is computed with screen coordinates. This * is because both text and image components are oriented in * screen space, and not according to the orientation of the tabs * themselves. That's why we have to consider the side when * correcting for left/right slants. */ switch (nbPtr->side) { case SIDE_TOP: case SIDE_BOTTOM: if (nbPtr->slant == SLANT_LEFT) { x += nbPtr->overlap; } else if (nbPtr->slant == SLANT_RIGHT) { x -= nbPtr->overlap; } break; case SIDE_LEFT: case SIDE_RIGHT: if (nbPtr->slant == SLANT_LEFT) { y += nbPtr->overlap; } else if (nbPtr->slant == SLANT_RIGHT) { y -= nbPtr->overlap; } break; } /* * Draw the active or normal background color over the entire * label area. This includes both the tab's text and image. * The rectangle should be 2 pixels wider/taller than this * area. So if the label consists of just an image, we get an * halo around the image when the tab is active. */ gc = Tk_GCForColor(bgColor, drawable); XFillRectangle(nbPtr->display, drawable, gc, x + dx, y + dy, tabPtr->labelWidth, tabPtr->labelHeight); if ((nbPtr->flags & TNB_FOCUS) && (nbPtr->focusPtr == tabPtr)) { XDrawRectangle(nbPtr->display, drawable, nbPtr->defTabStyle.activeGC, x + dx, y + dy, tabPtr->labelWidth - 1, tabPtr->labelHeight - 1); } tx = ty = ix = iy = 0; /* Suppress compiler warning. */ imgWidth = imgHeight = 0; if (tabPtr->image != NULL) { imgWidth = ImageWidth(tabPtr->image); imgHeight = ImageHeight(tabPtr->image); } switch (nbPtr->defTabStyle.textSide) { case SIDE_LEFT: tx = x + dx + tabPtr->iPadX.side1; ty = y + (tabPtr->screenHeight - tabPtr->textHeight) / 2; ix = tx + tabPtr->textWidth + IMAGE_PAD; iy = y + (tabPtr->screenHeight - imgHeight) / 2; break; case SIDE_RIGHT: ix = x + dx + tabPtr->iPadX.side1 + IMAGE_PAD; iy = y + (tabPtr->screenHeight - imgHeight) / 2; tx = ix + imgWidth; ty = y + (tabPtr->screenHeight - tabPtr->textHeight) / 2; break; case SIDE_BOTTOM: iy = y + dy + tabPtr->iPadY.side1 + IMAGE_PAD; ix = x + (tabPtr->screenWidth - imgWidth) / 2; ty = iy + imgHeight; tx = x + (tabPtr->screenWidth - tabPtr->textWidth) / 2; break; case SIDE_TOP: tx = x + (tabPtr->screenWidth - tabPtr->textWidth) / 2; ty = y + dy + tabPtr->iPadY.side1 + IMAGE_PAD; ix = x + (tabPtr->screenWidth - imgWidth) / 2; iy = ty + tabPtr->textHeight; break; } if (tabPtr->image != NULL) { Tk_RedrawImage(ImageBits(tabPtr->image), 0, 0, imgWidth, imgHeight, drawable, ix, iy); } if (tabPtr->text != NULL) { TextStyle ts; XColor *activeColor; activeColor = fgColor; if (selected) { activeColor = GETATTR(tabPtr, selColor); } else if (active) { activeColor = GETATTR(tabPtr, activeFgColor); } Blt_SetDrawTextStyle(&ts, GETATTR(tabPtr, font), tabPtr->textGC, fgColor, activeColor, tabPtr->shadow.color, nbPtr->defTabStyle.rotate, TK_ANCHOR_NW, TK_JUSTIFY_LEFT, 0, tabPtr->shadow.offset); ts.state = tabPtr->state; ts.border = border; ts.padX.side1 = ts.padX.side2 = 2; if (selected || active) { ts.state |= STATE_ACTIVE; } Blt_DrawText(nbPtr->tkwin, drawable, tabPtr->text, &ts, tx, ty); } } static void DrawPerforation(nbPtr, tabPtr, drawable) Notebook *nbPtr; Tab *tabPtr; Drawable drawable; { XPoint pointArr[2]; int x, y; int segmentWidth, max; Tk_3DBorder border, perfBorder; if ((tabPtr->container != NULL) || (tabPtr->tkwin == NULL)) { return; } WorldToScreen(nbPtr, tabPtr->worldX + 2, tabPtr->worldY + tabPtr->worldHeight + 2, &x, &y); border = GETATTR(tabPtr, selBorder); segmentWidth = 3; if (nbPtr->flags & PERFORATION_ACTIVE) { perfBorder = GETATTR(tabPtr, activeBorder); } else { perfBorder = GETATTR(tabPtr, selBorder); } if (nbPtr->side & SIDE_HORIZONTAL) { pointArr[0].x = x; pointArr[0].y = pointArr[1].y = y; max = tabPtr->screenX + tabPtr->screenWidth - 2; Blt_Fill3DRectangle(nbPtr->tkwin, drawable, perfBorder, x - 2, y - 4, tabPtr->screenWidth, 8, 0, TK_RELIEF_FLAT); while (pointArr[0].x < max) { pointArr[1].x = pointArr[0].x + segmentWidth; if (pointArr[1].x > max) { pointArr[1].x = max; } Tk_Draw3DPolygon(nbPtr->tkwin, drawable, border, pointArr, 2, 1, TK_RELIEF_RAISED); pointArr[0].x += 2 * segmentWidth; } } else { pointArr[0].x = pointArr[1].x = x; pointArr[0].y = y; max = tabPtr->screenY + tabPtr->screenHeight - 2; Blt_Fill3DRectangle(nbPtr->tkwin, drawable, perfBorder, x - 4, y - 2, 8, tabPtr->screenHeight, 0, TK_RELIEF_FLAT); while (pointArr[0].y < max) { pointArr[1].y = pointArr[0].y + segmentWidth; if (pointArr[1].y > max) { pointArr[1].y = max; } Tk_Draw3DPolygon(nbPtr->tkwin, drawable, border, pointArr, 2, 1, TK_RELIEF_RAISED); pointArr[0].y += 2 * segmentWidth; } } } #define NextPoint(px, py) \ pointPtr->x = (px), pointPtr->y = (py), pointPtr++, nPoints++ #define EndPoint(px, py) \ pointPtr->x = (px), pointPtr->y = (py), nPoints++ #define BottomLeft(px, py) \ NextPoint((px) + nbPtr->corner, (py)), \ NextPoint((px), (py) - nbPtr->corner) #define TopLeft(px, py) \ NextPoint((px), (py) + nbPtr->corner), \ NextPoint((px) + nbPtr->corner, (py)) #define TopRight(px, py) \ NextPoint((px) - nbPtr->corner, (py)), \ NextPoint((px), (py) + nbPtr->corner) #define BottomRight(px, py) \ NextPoint((px), (py) - nbPtr->corner), \ NextPoint((px) - nbPtr->corner, (py)) /* * From the left edge: * * |a|b|c|d|e| f |d|e|g|h| i |h|g|e|d|f| j |e|d|c|b|a| * * a. highlight ring * b. notebook 3D border * c. outer gap * d. page border * e. page corner * f. gap + select pad * g. label pad x (worldX) * h. internal pad x * i. label width * j. rest of page width * * worldX, worldY * | * | * * 4+ . . +5 * 3+ +6 * . . * . . * 1+. . .2+ +7 . . . .+8 * 0+ +9 * . . * . . *13+ +10 * 12+-------------------------+11 * */ static void DrawFolder(nbPtr, tabPtr, drawable) Notebook *nbPtr; Tab *tabPtr; Drawable drawable; { XPoint pointArr[16]; XPoint *pointPtr; int width, height; int left, bottom, right, top, yBot, yTop; int x, y; register int i; int nPoints; width = VPORTWIDTH(nbPtr); height = VPORTHEIGHT(nbPtr); x = tabPtr->worldX; y = tabPtr->worldY; nPoints = 0; pointPtr = pointArr; /* Remember these are all world coordinates. */ /* * x Left side of tab. * y Top of tab. * yTop Top of folder. * yBot Bottom of the tab. * left Left side of the folder. * right Right side of the folder. * top Top of folder. * bottom Bottom of folder. */ left = nbPtr->scrollOffset - nbPtr->xSelectPad; right = left + width; yTop = y + tabPtr->worldHeight; yBot = nbPtr->pageTop - (nbPtr->inset + nbPtr->yPad); top = yBot - nbPtr->inset2 /* - 4 */; if (nbPtr->pageHeight == 0) { bottom = yBot + 2 * nbPtr->corner; } else { bottom = height - nbPtr->yPad - 1; } if (tabPtr != nbPtr->selectPtr) { /* * Case 1: Unselected tab * * * 3+ . . +4 * 2+ +5 * . . * 1+ +6 * 0+ . . +7 * */ if (nbPtr->slant & SLANT_LEFT) { NextPoint(x, yBot); NextPoint(x, yTop); NextPoint(x + nbPtr->tabHeight, y); } else { BottomLeft(x, yBot); TopLeft(x, y); } x += tabPtr->worldWidth; if (nbPtr->slant & SLANT_RIGHT) { NextPoint(x - nbPtr->tabHeight, y); NextPoint(x, yTop); NextPoint(x, yBot); } else { TopRight(x, y); BottomRight(x, yBot); } } else if (!(tabPtr->flags & TAB_VISIBLE)) { /* * Case 2: Selected tab not visible in viewport. Draw folder only. * * * 3+ . . +4 * 2+ +5 * . . * 1+ +6 * 0+------+7 * */ TopLeft(left, top); TopRight(right, top); BottomRight(right, bottom); BottomLeft(left, bottom); } else { int flags; int tabWidth; x -= nbPtr->xSelectPad; y -= nbPtr->yPad; tabWidth = tabPtr->worldWidth + 2 * nbPtr->xSelectPad; #define CLIP_NONE 0 #define CLIP_LEFT (1<<0) #define CLIP_RIGHT (1<<1) flags = 0; if (x < left) { flags |= CLIP_LEFT; } if ((x + tabWidth) > right) { flags |= CLIP_RIGHT; } switch (flags) { case CLIP_NONE: /* * worldX, worldY * | * * 4+ . . +5 * 3+ +6 * . . * . . * 1+. . .2+---------+7 . . . .+8 * 0+ +9 * . . * . . *13+ +10 * 12+ . . . . . . . . . . . . +11 */ if (x < (left + nbPtr->corner)) { NextPoint(left, top); } else { TopLeft(left, top); } if (nbPtr->slant & SLANT_LEFT) { NextPoint(x, yTop); NextPoint(x + nbPtr->tabHeight + nbPtr->yPad, y); } else { NextPoint(x, top); TopLeft(x, y); } x += tabWidth; if (nbPtr->slant & SLANT_RIGHT) { NextPoint(x - nbPtr->tabHeight - nbPtr->yPad, y); NextPoint(x, yTop); } else { TopRight(x, y); NextPoint(x, top); } if (x > (right - nbPtr->corner)) { NextPoint(right, top + nbPtr->corner); } else { TopRight(right, top); } BottomRight(right, bottom); BottomLeft(left, bottom); break; case CLIP_LEFT: /* * worldX, worldY * | * * 4+ . . +5 * 3+ +6 * . . * . . * 2+--------+7 . . . .+8 * 1+ . . . +0 +9 * . . * . . * 13+ +10 * 12+ . . . .+11 */ NextPoint(left, yBot); if (nbPtr->slant & SLANT_LEFT) { NextPoint(x, yBot); NextPoint(x, yTop); NextPoint(x + nbPtr->tabHeight + nbPtr->yPad, y); } else { BottomLeft(x, yBot); TopLeft(x, y); } x += tabWidth; if (nbPtr->slant & SLANT_RIGHT) { NextPoint(x - nbPtr->tabHeight - nbPtr->yPad, y); NextPoint(x, yTop); NextPoint(x, top); } else { TopRight(x, y); NextPoint(x, top); } if (x > (right - nbPtr->corner)) { NextPoint(right, top + nbPtr->corner); } else { TopRight(right, top); } BottomRight(right, bottom); BottomLeft(left, bottom); break; case CLIP_RIGHT: /* * worldX, worldY * | * * 9+ . . +10 * 8+ +11 * . . * . . * 6+ . . . .7+---------+12 * 5+ 0+ . . . +13 * . . * . . * 4+ +1 * 3+ . . . +2 */ NextPoint(right, yBot); BottomRight(right, bottom); BottomLeft(left, bottom); if (x < (left + nbPtr->corner)) { NextPoint(left, top); } else { TopLeft(left, top); } NextPoint(x, top); if (nbPtr->slant & SLANT_LEFT) { NextPoint(x, yTop); NextPoint(x + nbPtr->tabHeight + nbPtr->yPad, y); } else { TopLeft(x, y); } x += tabWidth; if (nbPtr->slant & SLANT_RIGHT) { NextPoint(x - nbPtr->tabHeight - nbPtr->yPad, y); NextPoint(x, yTop); NextPoint(x, yBot); } else { TopRight(x, y); BottomRight(x, yBot); } break; case (CLIP_LEFT | CLIP_RIGHT): /* * worldX, worldY * | * * 4+ . . . . . . . . +5 * 3+ +6 * . . * . . * 1+---------------------+7 * 2+ 0+ +9 .+8 * . . * . . * 13+ +10 * 12+ . . . +11 */ NextPoint(left, yBot); if (nbPtr->slant & SLANT_LEFT) { NextPoint(x, yBot); NextPoint(x, yTop); NextPoint(x + nbPtr->tabHeight + nbPtr->yPad, y); } else { BottomLeft(x, yBot); TopLeft(x, y); } x += tabPtr->worldWidth; if (nbPtr->slant & SLANT_RIGHT) { NextPoint(x - nbPtr->tabHeight - nbPtr->yPad, y); NextPoint(x, yTop); NextPoint(x, yBot); } else { TopRight(x, y); BottomRight(x, yBot); } NextPoint(right, yBot); BottomRight(right, bottom); BottomLeft(left, bottom); break; } } EndPoint(pointArr[0].x, pointArr[0].y); for (i = 0; i < nPoints; i++) { WorldToScreen(nbPtr, pointArr[i].x, pointArr[i].y, &x, &y); pointArr[i].x = x; pointArr[i].y = y; } Draw3DFolder(nbPtr, tabPtr, drawable, nbPtr->side, pointArr, nPoints); DrawLabel(nbPtr, tabPtr, drawable); if (tabPtr->container != NULL) { XRectangle rect; /* Draw a rectangle covering the spot representing the window */ GetWindowRectangle(tabPtr, nbPtr->tkwin, FALSE, &rect); XFillRectangles(nbPtr->display, drawable, tabPtr->backGC, &rect, 1); } } static void DrawOuterBorders(nbPtr, drawable) Notebook *nbPtr; Drawable drawable; { /* * Draw 3D border just inside of the focus highlight ring. We * draw the border even if the relief is flat so that any tabs * that hang over the edge will be clipped. */ if (nbPtr->borderWidth > 0) { Blt_Draw3DRectangle(nbPtr->tkwin, drawable, nbPtr->border, nbPtr->highlightWidth, nbPtr->highlightWidth, Tk_Width(nbPtr->tkwin) - 2 * nbPtr->highlightWidth, Tk_Height(nbPtr->tkwin) - 2 * nbPtr->highlightWidth, nbPtr->borderWidth, nbPtr->relief); } /* Draw focus highlight ring. */ if (nbPtr->highlightWidth > 0) { XColor *color; GC gc; color = (nbPtr->flags & TNB_FOCUS) ? nbPtr->highlightColor : nbPtr->highlightBgColor; gc = Tk_GCForColor(color, drawable); Tk_DrawFocusHighlight(nbPtr->tkwin, gc, nbPtr->highlightWidth, drawable); } } /* * ---------------------------------------------------------------------- * * DisplayNotebook -- * * This procedure is invoked to display the widget. * * Recomputes the layout of the widget if necessary. This is * necessary if the world coordinate system has changed. * Sets the vertical and horizontal scrollbars. This is done * here since the window width and height are needed for the * scrollbar calculations. * * Results: * None. * * Side effects: * The widget is redisplayed. * * ---------------------------------------------------------------------- */ static void DisplayNotebook(clientData) ClientData clientData; /* Information about widget. */ { Notebook *nbPtr = clientData; Pixmap drawable; int width, height; nbPtr->flags &= ~TNB_REDRAW; if (nbPtr->tkwin == NULL) { return; /* Window has been destroyed. */ } if (nbPtr->flags & TNB_LAYOUT) { ComputeLayout(nbPtr); nbPtr->flags &= ~TNB_LAYOUT; } if ((nbPtr->reqHeight == 0) || (nbPtr->reqWidth == 0)) { width = height = 0; if (nbPtr->side & SIDE_VERTICAL) { height = nbPtr->worldWidth; } else { width = nbPtr->worldWidth; } if (nbPtr->reqWidth > 0) { width = nbPtr->reqWidth; } else if (nbPtr->pageWidth > 0) { width = nbPtr->pageWidth; } if (nbPtr->reqHeight > 0) { height = nbPtr->reqHeight; } else if (nbPtr->pageHeight > 0) { height = nbPtr->pageHeight; } if (nbPtr->side & SIDE_VERTICAL) { width += nbPtr->pageTop + nbPtr->inset + nbPtr->inset2; height += nbPtr->inset + nbPtr->inset2; } else { height += nbPtr->pageTop + nbPtr->inset + nbPtr->inset2; width += nbPtr->inset + nbPtr->inset2; } if ((Tk_ReqWidth(nbPtr->tkwin) != width) || (Tk_ReqHeight(nbPtr->tkwin) != height)) { Tk_GeometryRequest(nbPtr->tkwin, width, height); } } if (nbPtr->flags & TNB_SCROLL) { width = VPORTWIDTH(nbPtr); nbPtr->scrollOffset = Blt_AdjustViewport(nbPtr->scrollOffset, nbPtr->worldWidth, width, nbPtr->scrollUnits, BLT_SCROLL_MODE_CANVAS); if (nbPtr->scrollCmdPrefix != NULL) { Blt_UpdateScrollbar(nbPtr->interp, nbPtr->scrollCmdPrefix, (double)nbPtr->scrollOffset / nbPtr->worldWidth, (double)(nbPtr->scrollOffset + width) / nbPtr->worldWidth); } ComputeVisibleTabs(nbPtr); nbPtr->flags &= ~TNB_SCROLL; } if (!Tk_IsMapped(nbPtr->tkwin)) { return; } height = Tk_Height(nbPtr->tkwin); drawable = Tk_GetPixmap(nbPtr->display, Tk_WindowId(nbPtr->tkwin), Tk_Width(nbPtr->tkwin), Tk_Height(nbPtr->tkwin), Tk_Depth(nbPtr->tkwin)); /* * Clear the background either by tiling a pixmap or filling with * a solid color. Tiling takes precedence. */ if (nbPtr->tile != NULL) { Blt_SetTileOrigin(nbPtr->tkwin, nbPtr->tile, 0, 0); Blt_TileRectangle(nbPtr->tkwin, drawable, nbPtr->tile, 0, 0, Tk_Width(nbPtr->tkwin), height); } else { Blt_Fill3DRectangle(nbPtr->tkwin, drawable, nbPtr->border, 0, 0, Tk_Width(nbPtr->tkwin), height, 0, TK_RELIEF_FLAT); } if (nbPtr->nVisible > 0) { register int i; register Tab *tabPtr; Blt_ChainLink *linkPtr; linkPtr = nbPtr->startPtr->linkPtr; for (i = 0; i < Blt_ChainGetLength(nbPtr->chainPtr); i++) { linkPtr = Blt_ChainPrevLink(linkPtr); if (linkPtr == NULL) { linkPtr = Blt_ChainLastLink(nbPtr->chainPtr); } tabPtr = Blt_ChainGetValue(linkPtr); if ((tabPtr != nbPtr->selectPtr) && (tabPtr->flags & TAB_VISIBLE)) { DrawFolder(nbPtr, tabPtr, drawable); } } DrawFolder(nbPtr, nbPtr->selectPtr, drawable); if (nbPtr->tearoff) { DrawPerforation(nbPtr, nbPtr->selectPtr, drawable); } if ((nbPtr->selectPtr->tkwin != NULL) && (nbPtr->selectPtr->container == NULL)) { XRectangle rect; GetWindowRectangle(nbPtr->selectPtr, nbPtr->tkwin, FALSE, &rect); ArrangeWindow(nbPtr->selectPtr->tkwin, &rect, 0); } } DrawOuterBorders(nbPtr, drawable); XCopyArea(nbPtr->display, drawable, Tk_WindowId(nbPtr->tkwin), nbPtr->highlightGC, 0, 0, Tk_Width(nbPtr->tkwin), height, 0, 0); Tk_FreePixmap(nbPtr->display, drawable); } /* * From the left edge: * * |a|b|c|d|e| f |d|e|g|h| i |h|g|e|d|f| j |e|d|c|b|a| * * a. highlight ring * b. notebook 3D border * c. outer gap * d. page border * e. page corner * f. gap + select pad * g. label pad x (worldX) * h. internal pad x * i. label width * j. rest of page width * * worldX, worldY * | * | * * 4+ . . +5 * 3+ +6 * . . * . . * 1+. . .2+ +7 . . . .+8 * 0+ +9 * . . * . . *13+ +10 * 12+-------------------------+11 * */ static void DisplayTearoff(clientData) ClientData clientData; { Notebook *nbPtr; Tab *tabPtr; Drawable drawable; XPoint pointArr[16]; XPoint *pointPtr; int width, height; int left, bottom, right, top; int x, y; int nPoints; Tk_Window tkwin; Tk_Window parent; XRectangle rect; tabPtr = clientData; if (tabPtr == NULL) { return; } tabPtr->flags &= ~TAB_REDRAW; nbPtr = tabPtr->nbPtr; if (nbPtr->tkwin == NULL) { return; } tkwin = tabPtr->container; drawable = Tk_WindowId(tkwin); /* * Clear the background either by tiling a pixmap or filling with * a solid color. Tiling takes precedence. */ if (nbPtr->tile != NULL) { Blt_SetTileOrigin(tkwin, nbPtr->tile, 0, 0); Blt_TileRectangle(tkwin, drawable, nbPtr->tile, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin)); } else { Blt_Fill3DRectangle(tkwin, drawable, nbPtr->border, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT); } width = Tk_Width(tkwin) - 2 * nbPtr->inset; height = Tk_Height(tkwin) - 2 * nbPtr->inset; x = nbPtr->inset + nbPtr->gap + nbPtr->corner; y = nbPtr->inset; left = nbPtr->inset; right = nbPtr->inset + width; top = nbPtr->inset + nbPtr->corner + nbPtr->xSelectPad; bottom = nbPtr->inset + height; /* * worldX, worldY * | * * 4+ . . +5 * 3+ +6 * . . * . . * 1+. . .2+ +7 . . . .+8 * 0+ +9 * . . * . . *13+ +10 * 12+-------------------------+11 */ nPoints = 0; pointPtr = pointArr; TopLeft(left, top); NextPoint(x, top); TopLeft(x, y); x += tabPtr->worldWidth; TopRight(x, y); NextPoint(x, top); TopRight(right, top); BottomRight(right, bottom); BottomLeft(left, bottom); EndPoint(pointArr[0].x, pointArr[0].y); Draw3DFolder(nbPtr, tabPtr, drawable, SIDE_TOP, pointArr, nPoints); parent = (tabPtr->container == NULL) ? nbPtr->tkwin : tabPtr->container; GetWindowRectangle(tabPtr, parent, TRUE, &rect); ArrangeWindow(tabPtr->tkwin, &rect, TRUE); /* Draw 3D border. */ if ((nbPtr->borderWidth > 0) && (nbPtr->relief != TK_RELIEF_FLAT)) { Blt_Draw3DRectangle(tkwin, drawable, nbPtr->border, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), nbPtr->borderWidth, nbPtr->relief); } } /* * -------------------------------------------------------------- * * NotebookCmd -- * * This procedure is invoked to process the "notebook" command. * See the user documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * * -------------------------------------------------------------- */ static Blt_OpSpec notebookOps[] = { {"activate", 1, (Blt_Op)ActivateOp, 3, 3, "index",}, {"bind", 1, (Blt_Op)BindOp, 2, 5, "index ?sequence command?",}, {"cget", 2, (Blt_Op)CgetOp, 3, 3, "option",}, {"configure", 2, (Blt_Op)ConfigureOp, 2, 0, "?option value?...",}, {"delete", 1, (Blt_Op)DeleteOp, 2, 0, "first ?last?",}, {"focus", 1, (Blt_Op)FocusOp, 3, 3, "index",}, {"highlight", 1, (Blt_Op)ActivateOp, 3, 3, "index",}, {"id", 2, (Blt_Op)IdOp, 3, 3, "index",}, {"index", 3, (Blt_Op)IndexOp, 3, 5, "string",}, {"insert", 3, (Blt_Op)InsertOp, 3, 0, "index ?option value?",}, {"invoke", 3, (Blt_Op)InvokeOp, 3, 3, "index",}, {"move", 1, (Blt_Op)MoveOp, 5, 5, "name after|before index",}, {"nearest", 1, (Blt_Op)NearestOp, 4, 4, "x y",}, {"perforation", 1, (Blt_Op)PerforationOp, 2, 0, "args",}, {"scan", 2, (Blt_Op)ScanOp, 5, 5, "dragto|mark x y",}, {"see", 3, (Blt_Op)SeeOp, 3, 3, "index",}, {"select", 3, (Blt_Op)SelectOp, 3, 3, "index",}, {"size", 2, (Blt_Op)SizeOp, 2, 2, "",}, {"tab", 1, (Blt_Op)TabOp, 2, 0, "oper args",}, {"view", 1, (Blt_Op)ViewOp, 2, 5, "?moveto fract? ?scroll number what?",}, }; static int nNotebookOps = sizeof(notebookOps) / sizeof(Blt_OpSpec); static int NotebookInstCmd(clientData, interp, argc, argv) ClientData clientData; /* Information about the widget. */ Tcl_Interp *interp; /* Interpreter to report errors back to. */ int argc; /* Number of arguments. */ char **argv; /* Vector of argument strings. */ { Blt_Op proc; Notebook *nbPtr = clientData; int result; proc = Blt_GetOp(interp, nNotebookOps, notebookOps, BLT_OP_ARG1, argc, argv, 0); if (proc == NULL) { return TCL_ERROR; } Tcl_Preserve(nbPtr); result = (*proc) (nbPtr, interp, argc, argv); Tcl_Release(nbPtr); return result; } /* *---------------------------------------------------------------------- * * NotebookInstDeletedCmd -- * * This procedure can be called if the window was destroyed * (tkwin will be NULL) and the command was deleted * automatically. In this case, we need to do nothing. * * Otherwise this routine was called because the command was * deleted. Then we need to clean-up and destroy the widget. * * Results: * None. * * Side Effects: * The widget is destroyed. * *---------------------------------------------------------------------- */ static void NotebookInstDeletedCmd(clientData) ClientData clientData; /* Pointer to widget record for widget. */ { Notebook *nbPtr = clientData; if (nbPtr->tkwin != NULL) { Tk_Window tkwin; tkwin = nbPtr->tkwin; nbPtr->tkwin = NULL; Tk_DestroyWindow(tkwin); #ifdef ITCL_NAMESPACES Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL); #endif /* ITCL_NAMESPACES */ } } /* * ------------------------------------------------------------------------ * * NotebookCmd -- * * This procedure is invoked to process the Tcl command that * corresponds to a widget managed by this module. See the user * documentation for details on what it does. * * Results: * A standard Tcl result. * * Side Effects: * See the user documentation. * * ----------------------------------------------------------------------- */ /* ARGSUSED */ static int NotebookCmd(clientData, interp, argc, argv) ClientData clientData; /* Main window associated with interpreter. */ Tcl_Interp *interp; /* Current interpreter. */ int argc; /* Number of arguments. */ char **argv; /* Argument strings. */ { Notebook *nbPtr; Tk_Window tkwin; unsigned int mask; Tcl_CmdInfo cmdInfo; if (argc < 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " pathName ?option value?...\"", (char *)NULL); return TCL_ERROR; } tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1], (char *)NULL); if (tkwin == NULL) { return TCL_ERROR; } nbPtr = CreateNotebook(interp, tkwin); if (ConfigureNotebook(interp, nbPtr, argc - 2, argv + 2, 0) != TCL_OK) { Tk_DestroyWindow(nbPtr->tkwin); return TCL_ERROR; } mask = (ExposureMask | StructureNotifyMask | FocusChangeMask); Tk_CreateEventHandler(tkwin, mask, NotebookEventProc, nbPtr); nbPtr->cmdToken = Tcl_CreateCommand(interp, argv[1], NotebookInstCmd, nbPtr, NotebookInstDeletedCmd); #ifdef ITCL_NAMESPACES Itk_SetWidgetCommand(nbPtr->tkwin, nbPtr->cmdToken); #endif /* * Try to invoke a procedure to initialize various bindings on * tabs. Source the file containing the procedure now if the * procedure isn't currently defined. We deferred this to now so * that the user could set the variable "blt_library" within the * script. */ if (!Tcl_GetCommandInfo(interp, "blt::TabnotebookInit", &cmdInfo)) { static char initCmd[] = "source [file join $blt_library tabnotebook.tcl]"; if (Tcl_GlobalEval(interp, initCmd) != TCL_OK) { char info[200]; sprintf(info, "\n (while loading bindings for %s)", argv[0]); Tcl_AddErrorInfo(interp, info); Tk_DestroyWindow(nbPtr->tkwin); return TCL_ERROR; } } if (Tcl_VarEval(interp, "blt::TabnotebookInit ", argv[1], (char *)NULL) != TCL_OK) { Tk_DestroyWindow(nbPtr->tkwin); return TCL_ERROR; } Tcl_SetResult(interp, Tk_PathName(nbPtr->tkwin), TCL_VOLATILE); return TCL_OK; } int Blt_TabnotebookInit(interp) Tcl_Interp *interp; { static Blt_CmdSpec cmdSpec = { "tabnotebook", NotebookCmd, }; if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) { return TCL_ERROR; } return TCL_OK; } #endif /* NO_TABNOTEBOOK */