The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
 * tixNBFrame.c --
 *
 *	This module implements "tixNoteBookFrame" widgets.
 *
 * Copyright (c) 1996, Expert Interface Technologies
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 *
 */

#include "tkInt.h"
#include "tixPort.h"
#include "tixInt.h"
#include "tkVMacro.h"
#include "tixDef.h"

#define NUM_TAB_POINTS 6

/*
 * A data structure of the following type is kept for each
 * widget managed by this file:
 */

typedef struct NoteBookFrameStruct {
    Tk_Window tkwin;		/* Window that embodies the widget.  NULL
				 * means window has been deleted but
				 * widget record hasn't been cleaned up yet. */
    Display *display;		/* X's token for the window's display. */
    Tcl_Interp *interp;		/* Interpreter associated with widget. */
    Tcl_Command widgetCmd;	/* Token for button's widget command. */

    /*
     * Information used when displaying widget:
     */
    int desiredWidth;		/* Desired narrow dimension of scrollbar,
				 * in pixels. */
    int width;			/* total width of the widget */
    int height;			/* total width of the widget */

    /*
     * Information used when displaying widget:
     */

    /* Border and general drawing */

    int borderWidth;		/* Width of 3-D borders. */
    Tk_3DBorder bgBorder;	/* Used for drawing background. */
    Tk_3DBorder focusBorder;	/* background of the "focus" tab. */
    Tk_3DBorder inActiveBorder;	/* background of the "active" tab */
    XColor * backPageColorPtr;	/* the color used as the "back page" */
    GC backPageGC;		/* GC for drawing text in normal mode. */
    int relief;			/* Indicates whether window as a whole is
				 * raised, sunken, or flat. */
    int tabPadx;
    int tabPady;

    int isSlave;		/* if is in Slave mode, do not request for
				 * germetry */
    /* Text drawing */
    TixFont font;		/* Information about text font, or NULL. */
    XColor *textColorPtr;	/* Color for drawing text. */
    XColor *disabledFg;		/* Foreground color when disabled.  NULL
				 * means use normalFg with a 50% stipple
				 * instead. */
    GC textGC;			/* GC for drawing text in normal mode. */
    GC focusGC;			/* GC for focusing text. */
    Pixmap gray;		/* Pixmap for displaying disabled text if
				 * disabledFg is NULL. */
    GC disabledGC;		/* Used to produce disabled effect.  If
				 * disabledFg isn't NULL, this GC is used to
				 * draw button text or icon.  Otherwise
				 * text or icon is drawn with normalGC and
				 * this GC is used to stipple background
				 * across it.*/

     Cursor cursor;		/* Current cursor for window, or None. */

    struct _Tab * tabHead;
    struct _Tab * tabTail;
    struct _Tab * active;
    struct _Tab * focus;

    int tabsWidth;		/* total width  of the tabs */
    int tabsHeight;		/* total height of the tabs */

    char *takeFocus;		/* Value of -takefocus option;  not used in
				 * the C code, but used by keyboard traversal
				 * scripts.  Malloc'ed, but may be NULL. */

    unsigned int redrawing : 1;
    unsigned int gotFocus : 1;

} NoteBookFrame;

typedef struct _Tab {
    struct _Tab * next;

    NoteBookFrame * wPtr;
    char * name;

    Tk_Uid state;		/* State of Tab's for display purposes:
				 * normal or disabled. */
    Tk_Anchor anchor;

    char * text;
    int width, height;
    int numChars;
    Tk_Justify justify;		/* Justification to use for multi-line text. */
    int wrapLength;
    int underline;		/* Index of character to underline.  < 0 means
				 * don't underline anything. */

    Tk_Image image;
    char * imageString;

    Pixmap bitmap;
} Tab;

typedef NoteBookFrame   WidgetRecord;
typedef NoteBookFrame * WidgetPtr;

/*
 * Information used for argv parsing.
 */
static Tk_ConfigSpec configSpecs[] = {

    {TK_CONFIG_BORDER, "-background", "background", "Background",
       DEF_NOTEBOOKFRAME_BG_COLOR, Tk_Offset(WidgetRecord, bgBorder),
       TK_CONFIG_COLOR_ONLY},

    {TK_CONFIG_BORDER, "-background", "background", "Background",
       DEF_NOTEBOOKFRAME_BG_MONO, Tk_Offset(WidgetRecord, bgBorder),
       TK_CONFIG_MONO_ONLY},

    {TK_CONFIG_COLOR, "-backpagecolor", "backPageColor", "BackPageColor",
       DEF_NOTEBOOKFRAME_BACKPAGE_COLOR,
       Tk_Offset(WidgetRecord, backPageColorPtr),
       TK_CONFIG_COLOR_ONLY},

    {TK_CONFIG_COLOR, "-backpagecolor", "backPageColor", "BackPageColor",
       DEF_NOTEBOOKFRAME_BACKPAGE_MONO,
       Tk_Offset(WidgetRecord, backPageColorPtr),
       TK_CONFIG_MONO_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_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
       DEF_NOTEBOOKFRAME_BORDER_WIDTH, Tk_Offset(WidgetRecord, borderWidth),
       0},

    {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
       DEF_NOTEBOOKFRAME_CURSOR, Tk_Offset(WidgetRecord, cursor),
       TK_CONFIG_NULL_OK},

    {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
       "DisabledForeground", DEF_NOTEBOOKFRAME_DISABLED_FG_COLOR,
       Tk_Offset(WidgetRecord, disabledFg),
       TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},

    {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
       "DisabledForeground", DEF_NOTEBOOKFRAME_DISABLED_FG_MONO,
       Tk_Offset(WidgetRecord, disabledFg),
       TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},

    {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
       (char *) NULL, 0, 0},

    {TK_CONFIG_BORDER, "-focuscolor", "focusColor", "FocusColor",
       DEF_NOTEBOOKFRAME_FOCUS_COLOR, Tk_Offset(WidgetRecord, focusBorder),
       TK_CONFIG_COLOR_ONLY},

    {TK_CONFIG_BORDER, "-focuscolor", "focusColor", "FocusColor",
       DEF_NOTEBOOKFRAME_FOCUS_MONO, Tk_Offset(WidgetRecord, focusBorder),
       TK_CONFIG_MONO_ONLY},

    {TK_CONFIG_FONT, "-font", "font", "Font",
       DEF_NOTEBOOKFRAME_FONT, Tk_Offset(WidgetRecord, font), 0},

    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
       DEF_NOTEBOOKFRAME_FG_COLOR, Tk_Offset(WidgetRecord, textColorPtr),
       TK_CONFIG_COLOR_ONLY},

    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
       DEF_NOTEBOOKFRAME_FG_MONO, Tk_Offset(WidgetRecord, textColorPtr),
       TK_CONFIG_MONO_ONLY},

    {TK_CONFIG_BORDER, "-inactivebackground", "inactiveBackground",
       "Background",
       DEF_NOTEBOOKFRAME_INACTIVE_BG_COLOR,
       Tk_Offset(WidgetRecord, inActiveBorder),
       TK_CONFIG_COLOR_ONLY},

    {TK_CONFIG_BORDER, "-inactivebackground", "inactiveBackground",
       "Background",
       DEF_NOTEBOOKFRAME_INACTIVE_BG_MONO,
       Tk_Offset(WidgetRecord, inActiveBorder),
       TK_CONFIG_MONO_ONLY},

    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
       DEF_NOTEBOOKFRAME_RELIEF, Tk_Offset(WidgetRecord, relief), 0},

    {TK_CONFIG_BOOLEAN, "-slave", "slave", "Slave",
       DEF_NOTEBOOKFRAME_SLAVE, Tk_Offset(WidgetRecord, isSlave), 0},

    {TK_CONFIG_PIXELS, "-tabpadx", "tabPadX", "Pad",
       DEF_NOTEBOOKFRAME_TABPADX, Tk_Offset(WidgetRecord, tabPadx), 0},

    {TK_CONFIG_PIXELS, "-tabpady", "tabPadY", "Pad",
       DEF_NOTEBOOKFRAME_TABPADY, Tk_Offset(WidgetRecord, tabPady), 0},

    {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
	DEF_NOTEBOOKFRAME_TAKE_FOCUS, Tk_Offset(WidgetRecord, takeFocus),
	TK_CONFIG_NULL_OK},

    {TK_CONFIG_PIXELS, "-width", "width", "Width",
       DEF_NOTEBOOKFRAME_WIDTH, Tk_Offset(WidgetRecord, desiredWidth), 0},

    {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
	(char *) NULL, 0, 0}
};

#define DEF_NBF_TAB_ANCHOR		"c"
#define DEF_NBF_TAB_BITMAP		""
#define DEF_NBF_TAB_IMAGE		""
#define DEF_NBF_TAB_JUSTIFY		"center"
#define DEF_NBF_TAB_TEXT		""
#define DEF_NBF_TAB_STATE		"normal"
#define DEF_NBF_TAB_UNDERLINE		"-1"
#define DEF_NBF_TAB_WRAPLENGTH		"0"

static Tk_ConfigSpec tabConfigSpecs[] = {
    {TK_CONFIG_ANCHOR, "-anchor", (char*)NULL, (char*)NULL,
       DEF_NBF_TAB_ANCHOR, Tk_Offset(Tab, anchor), 0},

    {TK_CONFIG_BITMAP, "-bitmap", (char*)NULL, (char*)NULL,
       DEF_NBF_TAB_BITMAP, Tk_Offset(Tab, bitmap), TK_CONFIG_NULL_OK},

    {TK_CONFIG_STRING, "-image", (char*)NULL, (char*)NULL,
       DEF_NBF_TAB_IMAGE, Tk_Offset(Tab, imageString),
       TK_CONFIG_NULL_OK},

    {TK_CONFIG_JUSTIFY, "-justify", (char*)NULL, (char*)NULL,
	DEF_NBF_TAB_JUSTIFY, Tk_Offset(Tab, justify), 0},

    {TK_CONFIG_STRING, "-label", (char*)NULL, (char*)NULL,
       DEF_NBF_TAB_TEXT, Tk_Offset(Tab, text), TK_CONFIG_NULL_OK},

    {TK_CONFIG_UID, "-state", (char*)NULL, (char*)NULL,
       DEF_NBF_TAB_STATE, Tk_Offset(Tab, state), 0},

    {TK_CONFIG_INT, "-underline", (char*)NULL, (char*)NULL,
	DEF_NBF_TAB_UNDERLINE, Tk_Offset(Tab, underline), 0},

    {TK_CONFIG_PIXELS, "-wraplength", (char*)NULL, (char*)NULL,
       DEF_NBF_TAB_WRAPLENGTH, Tk_Offset(Tab, wrapLength), 0},

    {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
       (char *) NULL, 0, 0}
};

/*
 * Forward declarations for procedures defined later in this file:
 */

	/* These are standard procedures for TK widgets
	 * implemeted in C
	 */
static void		WidgetCmdDeletedProc _ANSI_ARGS_((
			    ClientData clientData));
static int		WidgetConfigure _ANSI_ARGS_((Tcl_Interp *interp,
			    WidgetPtr wPtr, int argc, char **argv,
			    int flags));
static int		WidgetCommand _ANSI_ARGS_((ClientData clientData,
			    Tcl_Interp *, int argc, char **argv));
static void		WidgetComputeGeometry _ANSI_ARGS_((WidgetPtr wPtr));
static void		WidgetDestroy _ANSI_ARGS_((char *clientData));
static void		WidgetDisplay _ANSI_ARGS_((ClientData clientData));
static void		WidgetEventProc _ANSI_ARGS_((ClientData clientData,
			    XEvent *eventPtr));

	/* Extra procedures for this widget
	 */
static int		AddTab _ANSI_ARGS_((WidgetPtr wPtr, char * name,
			    char ** argv, int argc));
static void		DeleteTab _ANSI_ARGS_((Tab * tPtr));
static void		CancelRedrawWhenIdle _ANSI_ARGS_((WidgetPtr wPtr));
static void		ComputeGeometry _ANSI_ARGS_((WidgetPtr wPtr));
static void		DrawTab _ANSI_ARGS_((WidgetPtr wPtr,
			    Tab * tPtr, int x, int isActive,
			    Drawable drawable));
static Tab * 		FindTab _ANSI_ARGS_((Tcl_Interp *interp,
			    WidgetPtr wPtr, char * name));
static void		FocusTab _ANSI_ARGS_((WidgetPtr wPtr,
			    Tab * tPtr, int x, Drawable drawable));
static void		GetTabPoints _ANSI_ARGS_((
			    WidgetPtr wPtr, Tab * tPtr,
			    int x, XPoint *points));
static void		ImageProc _ANSI_ARGS_((ClientData clientData,
			    int x, int y, int width, int height,
			    int imgWidth, int imgHeight));
static void		RedrawWhenIdle _ANSI_ARGS_((WidgetPtr wPtr));
static int		TabConfigure _ANSI_ARGS_((WidgetPtr wPtr,
			    Tab *tPtr, char ** argv, int argc));


/*
 *--------------------------------------------------------------
 *
 * Tix_NoteBookFrameCmd --
 *
 *	This procedure is invoked to process the "tixNoteBookFrame" Tcl
 *	command.  It creates a new "TixNoteBookFrame" widget.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	A new widget is created and configured.
 *
 *--------------------------------------------------------------
 */
int
Tix_NoteBookFrameCmd(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. */
{
    Tk_Window mainw = (Tk_Window) clientData;
    WidgetPtr wPtr;
    Tk_Window tkwin;

    if (argc < 2) {
	Tcl_AppendResult(interp, "wrong # args:  should be \"",
		argv[0], " pathName ?options?\"", (char *) NULL);
	return TCL_ERROR;
    }

    tkwin = Tk_CreateWindowFromPath(interp, mainw, argv[1], (char *) NULL);
    if (tkwin == NULL) {
	return TCL_ERROR;
    }

    Tk_SetClass(tkwin, "TixNoteBookFrame");

    /*
     * Allocate and initialize the widget record.
     */
    wPtr = (WidgetPtr) ckalloc(sizeof(WidgetRecord));
    wPtr->tkwin 	 	= tkwin;
    wPtr->display 	 	= Tk_Display(tkwin);
    wPtr->interp 	 	= interp;
    wPtr->isSlave 	 	= 1;
    wPtr->desiredWidth  	= 0;
    wPtr->interp 	 	= interp;
    wPtr->width 	 	= 0;
    wPtr->borderWidth 	 	= 0;
    wPtr->bgBorder 	 	= NULL;
    wPtr->backPageGC	 	= None;
    wPtr->backPageColorPtr 	= NULL;
    wPtr->disabledFg 		= NULL;
    wPtr->gray	 		= None;
    wPtr->disabledGC 		= None;
    wPtr->inActiveBorder 	= NULL;
    wPtr->focusBorder	 	= NULL;
    wPtr->font	 	= NULL;
    wPtr->textColorPtr	 	= NULL;
    wPtr->textGC	 	= None;
    wPtr->focusGC	 	= None;
    wPtr->relief 	 	= TK_RELIEF_FLAT;
    wPtr->cursor 	 	= None;

    wPtr->tabHead 	 	= 0;
    wPtr->tabTail	 	= 0;
    wPtr->tabPadx	 	= 0;
    wPtr->tabPady	 	= 0;
    wPtr->active	 	= 0;
    wPtr->focus		 	= 0;
    wPtr->takeFocus	 	= 0;
    wPtr->redrawing	 	= 0;
    wPtr->gotFocus	 	= 0;

    Tk_CreateEventHandler(wPtr->tkwin,
	ExposureMask|StructureNotifyMask|FocusChangeMask,
	WidgetEventProc, (ClientData) wPtr);
    wPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(wPtr->tkwin),
	WidgetCommand, (ClientData) wPtr, WidgetCmdDeletedProc);
    if (WidgetConfigure(interp, wPtr, argc-2, argv+2, 0) != TCL_OK) {
	Tk_DestroyWindow(wPtr->tkwin);
	return TCL_ERROR;
    }

    interp->result = Tk_PathName(wPtr->tkwin);
    return TCL_OK;
}

/*
 *--------------------------------------------------------------
 *
 * WidgetCommand --
 *
 *	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.
 *
 *--------------------------------------------------------------
 */

static int
WidgetCommand(clientData, interp, argc, argv)
    ClientData clientData;		/* Information about the widget. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    int result = TCL_OK;
    int length;
    char c;

    if (argc < 2) {
	Tcl_AppendResult(interp, "wrong # args: should be \"",
		argv[0], " option ?arg arg ...?\"", (char *) NULL);
	return TCL_ERROR;
    }
    Tcl_Preserve((ClientData) wPtr);
    c = argv[1][0];
    length = strlen(argv[1]);

    if (((c == 'a') && (strncmp(argv[1], "activate", length) == 0))||
	((c == 'f') && (strncmp(argv[1], "focus", length) == 0))){
	if (argc != 3) {
	    Tcl_AppendResult(interp, "wrong # args: should be \"",
		argv[0], " ", argv[1], " name\"", (char *) NULL);
	    goto error;
	}
	else {
	    if (strcmp(argv[2], "")==0) {
		if (c == 'a') {
		    wPtr->active = 0;
		    wPtr->focus  = 0;
		}
		else {
		    wPtr->focus  = 0;
		}
		RedrawWhenIdle(wPtr);
	    }
	    else {
		Tab * tPtr;

		for (tPtr=wPtr->tabHead; tPtr; tPtr=tPtr->next) {
		    if (strcmp(argv[2], tPtr->name) == 0) {
			if (c == 'a') {
			    wPtr->active = tPtr;
			    wPtr->focus  = tPtr;
			}
			else {
			    wPtr->focus  = tPtr;
			}
			RedrawWhenIdle(wPtr);
			goto done;
		    }
		}

		Tcl_AppendResult(interp, "unknown tab \"",
		    argv[0], "\"", (char *) NULL);
		goto error;
	    }
	}
    }
    else if ((c == 'a') && (strncmp(argv[1], "add", length) == 0)) {
	if (argc < 3) {
	    Tcl_AppendResult(interp, "wrong # args: should be ",
		argv[0], " add name ?options?", (char *) NULL);
	    goto error;
	}
	else {
	    if (AddTab(wPtr, argv[2], argv+3, argc-3)!= TCL_OK) {
		goto error;
	    } else {
		WidgetComputeGeometry(wPtr);
		RedrawWhenIdle(wPtr);
	    }
	}
    }
    else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)) {
	if (argc == 3) {
	    result = Tk_ConfigureValue(interp, wPtr->tkwin, configSpecs,
		(char *)wPtr, argv[2], 0);
	} else {
	    result = Tix_ArgcError(interp, argc, argv, 2, "option");
	}
    }
    else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
	if (argc == 2) {
	    result = Tk_ConfigureInfo(interp, wPtr->tkwin, configSpecs,
		    (char *) wPtr, (char *) NULL, 0);
	} else if (argc == 3) {
	    result = Tk_ConfigureInfo(interp, wPtr->tkwin, configSpecs,
		    (char *) wPtr, argv[2], 0);
	} else {
	    result = WidgetConfigure(interp, wPtr, argc-2, argv+2,
		    TK_CONFIG_ARGV_ONLY);
	}
    }
    else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
	Tab * tPtr, * prev ;

	if (argc != 3) {
	    Tix_ArgcError(interp, argc, argv, 2, "page");
	    goto error;
	}

	/* Find the tab from the list */
	for (prev=tPtr=wPtr->tabHead; tPtr; prev=tPtr,tPtr=tPtr->next) {
	    if (strcmp(tPtr->name, argv[2])==0) {
		break;
	    }
	}
	if (tPtr == NULL) {
	    Tcl_AppendResult(wPtr->interp,
		"Unknown tab \"", argv[2], "\"", (char*) NULL);
	    goto error;
	}
	if (tPtr == prev) {
	    if (wPtr->tabHead == wPtr->tabTail) {
		wPtr->tabHead = wPtr->tabTail = NULL;
	    } else {
		wPtr->tabHead = tPtr->next;
	    }
	} else {
	    if (tPtr == wPtr->tabTail) {
		wPtr->tabTail = prev;
	    }
	    prev->next = tPtr->next;
	}

	DeleteTab(tPtr);
	ComputeGeometry(wPtr);
	RedrawWhenIdle(wPtr);
    }
    else if ((c == 'g') && (strncmp(argv[1], "geometryinfo", length) == 0)) {
	ComputeGeometry(wPtr);
	Tcl_IntResults(interp, 2, 0, wPtr->width, wPtr->height);
    }
    else if ((c == 'i') && (strncmp(argv[1], "identify", length) == 0)) {
	if (argc != 4) {
	    Tcl_AppendResult(interp, "wrong # args: should be \"",
		argv[0], " identify x y\"", (char *) NULL);
	    goto error;
	}
	else {
	    int x, y, left, right;
	    Tab * tPtr;

	    if (Tcl_GetInt(interp, argv[2], &x) != TCL_OK) {
		goto error;
	    }
	    if (Tcl_GetInt(interp, argv[3], &y) != TCL_OK) {
		goto error;
	    }

	    if (y < wPtr->tabsHeight) {
		left = 0;
		for (tPtr=wPtr->tabHead; tPtr; tPtr=tPtr->next) {
		    right = left + (wPtr->borderWidth +  wPtr->tabPadx) * 2
		      + tPtr->width;

		    if (x >= left && x <= right && tPtr->state ==tixNormalUid){
			Tcl_AppendResult(interp, tPtr->name, NULL);
			goto done;
		    }
		    left = right;
		}
	    }

	    /*
	     * An empty string is returned to indicate "nothing selected"
	     */
	    Tcl_ResetResult(interp);
	}
    }
    else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
	Tcl_ResetResult(interp);

	if (argc == 3 && strcmp(argv[2], "pages")==0 ) {
	    Tab * tPtr;

	    for (tPtr=wPtr->tabHead; tPtr; tPtr=tPtr->next) {
		Tcl_AppendElement(interp, tPtr->name);
	    }
	}
	else if (argc == 3 && strcmp(argv[2], "active")==0 ) {
	    if (wPtr->active) {
		Tcl_AppendResult(interp, wPtr->active->name, NULL);
	    }
	}
	else if (argc == 3 && strcmp(argv[2], "focus")==0 ) {
	    if (wPtr->focus) {
		Tcl_AppendResult(interp, wPtr->focus->name, NULL);
	    }
	}
	else if (argc == 3 && strcmp(argv[2], "focusnext")==0 ) {
	    Tab * next;
	    if (wPtr->focus) {
		if (wPtr->focus->next) {
		    next = wPtr->focus->next;
		} else {
		    next = wPtr->tabHead;
		}
		Tcl_AppendResult(interp, next->name, NULL);
	    }
	}
	else if (argc == 3 && strcmp(argv[2], "focusprev")==0 ) {
	    Tab * prev, *tPtr;

	    if (wPtr->focus==wPtr->tabHead) {
		prev = wPtr->tabTail;
	    }
	    else {
		for (prev=tPtr=wPtr->tabHead;tPtr; prev=tPtr,tPtr=tPtr->next) {
		    if (tPtr == wPtr->focus) {
			break;
		    }
		}
	    }

	    if (prev) {
		Tcl_AppendResult(interp, prev->name, NULL);
	    }
	}
	else {
	    Tcl_AppendResult(interp, "wrong number of arguments or ",
		"unknown option", NULL);
	    goto error;
	}
    }
    else if ((c == 'm') && (strncmp(argv[1], "move", length) == 0)) {

    }
    else if ((c == 'p') && (strncmp(argv[1], "pagecget", length) == 0)) {
	Tab * tPtr;

	if (argc != 4) {
	    Tix_ArgcError(interp, argc, argv, 2, "option");
	    goto error;
	}
	if ((tPtr=FindTab(interp, wPtr, argv[2])) == NULL) {
	    goto error;
	}
	result = Tk_ConfigureValue(interp, wPtr->tkwin, tabConfigSpecs,
	    (char *)tPtr, argv[3], 0);
    }
    else if ((c == 'p') && (strncmp(argv[1], "pageconfigure", length) == 0)) {
	Tab * tPtr;

	if (argc < 3) {
	    Tix_ArgcError(interp, argc, argv, 2,
		"page ?option value ...?");
	    goto error;
	}
	if ((tPtr=FindTab(interp, wPtr, argv[2])) == NULL) {
	    goto error;
	}
	if (argc == 3) {
	    result = Tk_ConfigureInfo(interp, wPtr->tkwin, tabConfigSpecs,
		(char *)tPtr, (char *) NULL, 0);
	} else if (argc == 4) {
	    result = Tk_ConfigureInfo(interp, wPtr->tkwin, tabConfigSpecs,
		(char *)tPtr, argv[3], 0);
	} else {
	    result = TabConfigure(wPtr, tPtr, argv+3, argc-3);
	}
    }
    else {
	Tcl_AppendResult(interp, "bad option \"", argv[1],
	    "\":  must be activate, add, configure, delete, ",
	    "geometryinfo, identify, move, pagecget or ",
	    "pageconfigure", (char *) NULL);
	goto error;
    }

  done:
    Tcl_Release((ClientData) wPtr);
    return result;

  error:
    Tcl_Release((ClientData) wPtr);
    return TCL_ERROR;
}

/*
 *----------------------------------------------------------------------
 *
 * WidgetConfigure --
 *
 *	This procedure is called to process an argv/argc list in
 *	conjunction with the Tk option database to configure (or
 *	reconfigure) a Notebookframe 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 colors, border width,
 *	etc. get set for wPtr;  old resources get freed,
 *	if there were any.
 *
 *----------------------------------------------------------------------
 */
static int
WidgetConfigure(interp, wPtr, argc, argv, flags)
    Tcl_Interp *interp;			/* Used for error reporting. */
    WidgetPtr wPtr;			/* Information about widget. */
    int argc;				/* Number of valid entries in argv. */
    char **argv;			/* Arguments. */
    int flags;				/* Flags to pass to
					 * Tk_ConfigureWidget. */
{
    XGCValues gcValues;
    GC newGC;
    int mask;

    if (Tk_ConfigureWidget(interp, wPtr->tkwin, configSpecs,
	    argc, argv, (char *) wPtr, flags) != TCL_OK) {
	return TCL_ERROR;
    }

    if (wPtr->tabPadx < 3) {
	wPtr->tabPadx = 3;
    }
    if (wPtr->tabPady < 2) {
	wPtr->tabPady = 2;
    }

    Tk_SetBackgroundFromBorder(wPtr->tkwin, wPtr->bgBorder);

    /*
     * Get the back page GC
     */
    gcValues.foreground = wPtr->backPageColorPtr->pixel;
    gcValues.graphics_exposures = False;
    newGC = Tk_GetGC(wPtr->tkwin, GCForeground|GCGraphicsExposures,
	    &gcValues);
    if (wPtr->backPageGC != None) {
	Tk_FreeGC(wPtr->display, wPtr->backPageGC);
    }
    wPtr->backPageGC = newGC;

    /*
     * Get the text GC
     */
    gcValues.foreground = wPtr->textColorPtr->pixel;
    gcValues.background = Tk_3DBorderColor(wPtr->bgBorder)->pixel;
    gcValues.font = TixFontId(wPtr->font);
    gcValues.graphics_exposures = False;
    newGC = Tk_GetGC(wPtr->tkwin,
	 GCBackground|GCForeground|GCFont|GCGraphicsExposures,
	 &gcValues);
    if (wPtr->textGC != None) {
	Tk_FreeGC(wPtr->display, wPtr->textGC);
    }
    wPtr->textGC = newGC;

    /*
     * Get the disabled GC
     */
    if (wPtr->disabledFg != NULL) {
	gcValues.foreground = wPtr->disabledFg->pixel;
	gcValues.background = Tk_3DBorderColor(wPtr->bgBorder)->pixel;
	mask = GCForeground|GCBackground|GCFont;
    } else {
	gcValues.foreground = Tk_3DBorderColor(wPtr->bgBorder)->pixel;
	if (wPtr->gray == None) {
	    wPtr->gray = Tk_GetBitmap(interp, wPtr->tkwin,
			Tk_GetUid("gray50"));
	    if (wPtr->gray == None) {
		return TCL_ERROR;
	    }
	}
	gcValues.fill_style = FillStippled;
	gcValues.stipple = wPtr->gray;
	mask = GCForeground|GCFillStyle|GCFont|GCStipple;
    }
    gcValues.font = TixFontId(wPtr->font);
    newGC = Tk_GetGC(wPtr->tkwin, mask, &gcValues);
    if (wPtr->disabledGC != None) {
	Tk_FreeGC(wPtr->display, wPtr->disabledGC);
    }
    wPtr->disabledGC = newGC;

    /*
     * Get the focus GC
     */
    gcValues.foreground 	= wPtr->textColorPtr->pixel;
    gcValues.background 	= Tk_3DBorderColor(wPtr->bgBorder)->pixel;
    gcValues.graphics_exposures = False;
    gcValues.line_style         = LineDoubleDash;
    gcValues.dashes 		= 2;
    newGC = Tk_GetGC(wPtr->tkwin,
	GCForeground|GCBackground|GCGraphicsExposures|GCLineStyle|GCDashList,
	&gcValues);
    if (wPtr->focusGC != None) {
	Tk_FreeGC(wPtr->display, wPtr->focusGC);
    }
    wPtr->focusGC = newGC;

    WidgetComputeGeometry(wPtr);
    RedrawWhenIdle(wPtr);

    return TCL_OK;
}

/*
 *--------------------------------------------------------------
 *
 * WidgetEventProc --
 *
 *	This procedure is invoked by the Tk dispatcher for various
 *	events on Notebookframes.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	When the window gets deleted, internal structures get
 *	cleaned up.  When it gets exposed, it is redisplayed.
 *
 *--------------------------------------------------------------
 */

static void
WidgetEventProc(clientData, eventPtr)
    ClientData clientData;	/* Information about window. */
    XEvent *eventPtr;		/* Information about event. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;

    switch (eventPtr->type ) {
      case DestroyNotify:
	if (wPtr->tkwin != NULL) {
	    wPtr->tkwin = NULL;
	    Tcl_DeleteCommand(wPtr->interp,
	        Tcl_GetCommandName(wPtr->interp, wPtr->widgetCmd));
	}
	CancelRedrawWhenIdle(wPtr);
	Tcl_EventuallyFree((ClientData) wPtr, WidgetDestroy);
	break;

      case Expose:
      case ConfigureNotify:
	RedrawWhenIdle(wPtr);
	break;

      case FocusIn:
	if (eventPtr->xfocus.detail != NotifyVirtual) {
	    wPtr->gotFocus = 1;
	    if (wPtr->focus == NULL) {
		wPtr->focus = wPtr->active;
	    }
	    RedrawWhenIdle(wPtr);
	}
	break;

      case FocusOut:
	if (eventPtr->xfocus.detail != NotifyVirtual) {
	    wPtr->gotFocus = 0;
	    RedrawWhenIdle(wPtr);
	}
	break;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * WidgetDestroy --
 *
 *	This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
 *	to clean up the internal structure of a Notebookframe at a safe time
 *	(when no-one is using it anymore).
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Everything associated with the Notebookframe is freed up.
 *
 *----------------------------------------------------------------------
 */

static void
WidgetDestroy(clientData)
    char *clientData;	/* Info about my widget. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    Tab * tPtr;

    for (tPtr=wPtr->tabHead; tPtr;) {
	Tab * toDelete;

	toDelete = tPtr;
	tPtr=tPtr->next;

	DeleteTab(toDelete);
    }

    if (wPtr->backPageGC != None) {
	Tk_FreeGC(wPtr->display, wPtr->backPageGC);
    }
    if (wPtr->textGC != None) {
	Tk_FreeGC(wPtr->display, wPtr->textGC);
    }
    if (wPtr->focusGC != None) {
	Tk_FreeGC(wPtr->display, wPtr->focusGC);
    }
    if (wPtr->gray != None) {
	Tk_FreeBitmap(wPtr->display, wPtr->gray);
    }
    if (wPtr->disabledGC != None) {
	Tk_FreeGC(wPtr->display, wPtr->disabledGC);
    }
    Tk_FreeOptions(configSpecs, (char *) wPtr, wPtr->display, 0);
    ckfree((char *) wPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * WidgetCmdDeletedProc --
 *
 *	This procedure is invoked when a widget command is deleted.  If
 *	the widget isn't already in the process of being destroyed,
 *	this command destroys it.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The widget is destroyed.
 *
 *----------------------------------------------------------------------
 */
static void
WidgetCmdDeletedProc(clientData)
    ClientData clientData;	/* Pointer to widget record for widget. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;

    /*
     * This procedure could be invoked either because the window was
     * destroyed and the command was then deleted (in which case tkwin
     * is NULL) or because the command was deleted, and then this procedure
     * destroys the widget.
     */
    if (wPtr->tkwin != NULL) {
	Tk_Window tkwin = wPtr->tkwin;
	wPtr->tkwin = NULL;
	Tk_DestroyWindow(tkwin);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * FindTab --
 *
 *	Seraches for the Tab is the widget's tab list
 *----------------------------------------------------------------------
 */
static Tab * FindTab(interp, wPtr, name)
    Tcl_Interp * interp;
    WidgetPtr wPtr;
    char * name;
{
    Tab *tPtr;

    for (tPtr=wPtr->tabHead; tPtr; tPtr=tPtr->next) {
	if (strcmp(tPtr->name, name) == 0) {
	    return tPtr;
	}
    }

    Tcl_AppendResult(interp, "Unknown tab \"", name, "\"", (char*) NULL);
    return NULL;
}


static int TabConfigure(wPtr, tPtr, argv, argc)
    WidgetPtr wPtr;
    Tab *tPtr;
    char ** argv;
    int argc;
{
    if (Tk_ConfigureWidget(wPtr->interp, wPtr->tkwin, tabConfigSpecs,
	argc, argv, (char *)tPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
	return TCL_ERROR;
    }

    /*
     * Free the old images for the widget, if there were any.
     */
    if (tPtr->image != NULL) {
	Tk_FreeImage(tPtr->image);
	tPtr->image = NULL;
    }

    if (tPtr->imageString != NULL) {
	tPtr->image = Tk_GetImage(wPtr->interp, wPtr->tkwin,
	    tPtr->imageString, ImageProc, (ClientData) tPtr);
	if (tPtr->image == NULL) {
	    return TCL_ERROR;
	}
    }

    if (tPtr->text != NULL) {
	tPtr->numChars = strlen(tPtr->text);
	TixComputeTextGeometry(wPtr->font, tPtr->text, tPtr->numChars,
	    tPtr->wrapLength, &tPtr->width, &tPtr->height);
    }
    else if (tPtr->image != NULL) {
	Tk_SizeOfImage(tPtr->image, &tPtr->width, &tPtr->height);
    }
    else if (tPtr->bitmap != None) {
	Tk_SizeOfBitmap(wPtr->display, tPtr->bitmap, &tPtr->width,
	    &tPtr->height);
    }
    else {
	tPtr->width = 0;
	tPtr->height = 0;
    }

    WidgetComputeGeometry(wPtr);
    RedrawWhenIdle(wPtr);

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * AddTab --
 *
 *	Adds a new tab into the list of tabs.
 *
 * 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 colors, border width,
 *	etc. get set for wPtr;  old resources get freed,
 *	if there were any.
 *
 *----------------------------------------------------------------------
 */
static int
AddTab(wPtr, name, argv, argc)
    WidgetPtr wPtr;			/* Information about widget. */
    char * name;			/* Arguments. */
    char ** argv;
    int argc;
{
    Tab * tPtr;

    tPtr = (Tab*)ckalloc(sizeof(Tab));

    tPtr->next = NULL;
    tPtr->wPtr = wPtr;
    tPtr->name = tixStrDup(name);
    tPtr->state = tixNormalUid;
    tPtr->text = NULL;
    tPtr->width = 0;
    tPtr->height = 0;
    tPtr->numChars = 0;
    tPtr->justify = TK_JUSTIFY_CENTER;
    tPtr->wrapLength = 0;
    tPtr->underline = -1;
    tPtr->image = NULL;
    tPtr->imageString = NULL;
    tPtr->bitmap = None;
    tPtr->anchor = TK_ANCHOR_CENTER;

    if (TabConfigure(wPtr, tPtr, argv, argc) != TCL_OK) {
	return TCL_ERROR;
    }

    /* Append the tab to the end of the list */

    if (wPtr->tabHead == 0) {
	wPtr->tabHead = wPtr->tabTail = tPtr;
    }
    else {
	/* Insert right after the tail */
	wPtr->tabTail->next = tPtr;
	wPtr->tabTail = tPtr;
    }

    return TCL_OK;
}

static void DeleteTab(tPtr)
    Tab * tPtr;
{
    if (tPtr->wPtr->focus == tPtr) {
	tPtr->wPtr->focus = 0;
    }
    if (tPtr->wPtr->active == tPtr) {
	tPtr->wPtr->active = 0;
    }
    if (tPtr->name) {
	ckfree(tPtr->name);
    }
    if (tPtr->image) {
	Tk_FreeImage(tPtr->image);
    }

    if (tPtr->wPtr->tkwin) {
	Tk_FreeOptions(tabConfigSpecs, (char *)tPtr,
		       Tk_Display(tPtr->wPtr->tkwin), 0);
    }
    ckfree((char*)tPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * RedrawWhenIdle --
 *
 *	Redraw this widget when idle
 *
 * Results:
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */
static void
RedrawWhenIdle(wPtr)
    WidgetPtr wPtr;			/* Information about widget. */
{
    if (! wPtr->redrawing && Tk_IsMapped(wPtr->tkwin)) {
	wPtr->redrawing = 1;
	Tcl_DoWhenIdle(WidgetDisplay, (ClientData)wPtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * CancelRedrawWhenIdle --
 *
 *	Redraw this widget when idle
 *
 * Results:
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */
static void
CancelRedrawWhenIdle(wPtr)
    WidgetPtr wPtr;			/* Information about widget. */
{
    if (wPtr->redrawing) {
	wPtr->redrawing = 0;
	Tcl_CancelIdleCall(WidgetDisplay, (ClientData)wPtr);
    }
}

static void GetTabPoints(wPtr, tPtr, x, points)
    WidgetPtr wPtr;
    Tab * tPtr;
    int x;
    XPoint *points;
{
    points[0].x = x + wPtr->borderWidth;
    points[0].y = wPtr->tabsHeight;
    points[1].x = points[0].x;
    points[1].y = wPtr->borderWidth * 2;
    points[2].x = x + wPtr->borderWidth * 2;
    points[2].y = wPtr->borderWidth;

    points[3].x = x + tPtr->width + wPtr->tabPadx*2;
    points[3].y = points[2].y;
    points[4].x = points[3].x + wPtr->borderWidth;
    points[4].y = points[1].y;
    points[5].x = points[4].x;
    points[5].y = points[0].y;
}

/*
 *----------------------------------------------------------------------
 *
 * DrawTab --
 *
 *	Draws one tab according to its position and text
 *
 * Results:
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */
static void DrawTab(wPtr, tPtr, x, isActive, drawable)
    WidgetPtr wPtr;
    Tab * tPtr;
    int x;
    int isActive;
    Drawable drawable;
{
    Tk_3DBorder border;
    XPoint points[NUM_TAB_POINTS];
    int drawX, drawY, extraH;

    if (isActive) {
	border = wPtr->bgBorder;
    } else {
	border = wPtr->inActiveBorder;
    }

    GetTabPoints(wPtr, tPtr, x, points);
    drawX = x + wPtr->borderWidth + wPtr->tabPadx;
    drawY = wPtr->borderWidth + wPtr->tabPady;
    extraH = wPtr->tabsHeight - tPtr->height -
      wPtr->borderWidth - wPtr->tabPady *2;

    if (extraH > 0) {
	switch (tPtr->anchor) {
	  case TK_ANCHOR_SW: case TK_ANCHOR_S: case TK_ANCHOR_SE:
	    drawY += extraH;
	    break;
	  case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
	    drawY += extraH/2;
	    break;
	  case TK_ANCHOR_N: case TK_ANCHOR_NE: case TK_ANCHOR_NW:
	    /*
	     * Do nothing.
	     */
	    break;
	}
    }

    Tk_Fill3DPolygon(wPtr->tkwin, drawable,
	border, points, NUM_TAB_POINTS,
	wPtr->borderWidth, TK_RELIEF_SUNKEN);

    if (tPtr->text != NULL) {
	if (tPtr->state == tixNormalUid) {
	    TixDisplayText(wPtr->display, drawable, wPtr->font,
	        tPtr->text, tPtr->numChars,
	    	drawX, drawY,
	        tPtr->width,
	        tPtr->justify,
	        tPtr->underline,
	        wPtr->textGC);
	} else {
	    TixDisplayText(wPtr->display, drawable, wPtr->font,
	        tPtr->text, tPtr->numChars,
	    	drawX, drawY,
	        tPtr->width,
	        tPtr->justify,
	        tPtr->underline,
	        wPtr->disabledGC);
	}
    }
    else if (tPtr->image != NULL) {
	Tk_RedrawImage(tPtr->image, 0, 0, tPtr->width, tPtr->height,
	    drawable, drawX, drawY);
    }
    else if (tPtr->bitmap != None) {
	GC gc;

	if (tPtr->state == tixNormalUid) {
	    gc = wPtr->textGC;
	} else {
	    gc = wPtr->disabledGC;
	}
	XSetClipOrigin(wPtr->display, gc, drawX, drawY);
	XCopyPlane(wPtr->display, tPtr->bitmap, drawable,
		gc, 0, 0, tPtr->width, tPtr->height,
		drawX, drawY, 1);
	XSetClipOrigin(wPtr->display, gc, 0, 0);
    }

#if 0
    if (wPtr->gotFocus && tPtr == wPtr->focus) {
	XDrawLine(Tk_Display(wPtr->tkwin), drawable, wPtr->focusGC,
            drawX,
	    drawY + tPtr->height + 1,
	    drawX + tPtr->width,
	    drawY + tPtr->height + 1);
    }
#endif
}
/*
 *----------------------------------------------------------------------
 *
 * FocusTab --
 *
 *	Draws focus highlight on a tab.
 *
 * Results:
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */
static void FocusTab(wPtr, tPtr, x, drawable)
    WidgetPtr wPtr;
    Tab * tPtr;
    int x;
    Drawable drawable;
{
    Tk_3DBorder border;

    XPoint points[NUM_TAB_POINTS];

    if (wPtr->active == tPtr) {
	border = wPtr->bgBorder;
    } else {
	border = wPtr->inActiveBorder;
    }

    GetTabPoints(wPtr, tPtr, x, points);
    Tk_Draw3DPolygon(wPtr->tkwin, drawable,
	wPtr->focusBorder, points, NUM_TAB_POINTS,
	wPtr->borderWidth, TK_RELIEF_SUNKEN);

    if (wPtr->active == tPtr) {
	Tk_Draw3DPolygon(wPtr->tkwin, drawable,
	    border, points, NUM_TAB_POINTS,
	    wPtr->borderWidth/2, TK_RELIEF_SUNKEN);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * WidgetDisplay --
 *
 *	Redraw this widget
 *
 * Results:
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */
static void
WidgetDisplay(clientData)
    ClientData clientData;			/* Information about widget. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    Tab * tPtr;
    int width, height;
    Drawable buffer;

    /* Now let's redraw */
    if (wPtr->tabHead == NULL) {
	/*
	 * no tabs to redraw: just draw the border
	 */
	if ((wPtr->bgBorder != NULL) && (wPtr->relief != TK_RELIEF_FLAT)) {
	    Tk_Fill3DRectangle(wPtr->tkwin, Tk_WindowId(wPtr->tkwin),
		wPtr->bgBorder, 0, 0,
		Tk_Width(wPtr->tkwin), Tk_Height(wPtr->tkwin),
		wPtr->borderWidth, wPtr->relief);
	}
    }
    else {
	int x, y, activex = 0;

	buffer = Tix_GetRenderBuffer(wPtr->display, Tk_WindowId(wPtr->tkwin),
	    Tk_Width(wPtr->tkwin), Tk_Height(wPtr->tkwin),
	    Tk_Depth(wPtr->tkwin));
	XFillRectangle(Tk_Display(wPtr->tkwin), buffer, wPtr->backPageGC,
	    0, 0, Tk_Width(wPtr->tkwin), Tk_Height(wPtr->tkwin));

	Tk_Fill3DRectangle(wPtr->tkwin, buffer,
		wPtr->bgBorder, 0, wPtr->tabsHeight,
		Tk_Width(wPtr->tkwin),
		Tk_Height(wPtr->tkwin) - wPtr->tabsHeight,
		wPtr->borderWidth, wPtr->relief);

	/* Draw the tabs */
	x = 0;
	for (tPtr=wPtr->tabHead; tPtr; tPtr=tPtr->next) {
	    if (tPtr == wPtr->active) {
		activex = x;
		DrawTab(wPtr, tPtr, x, 1, buffer);
	    }
	    else {
		DrawTab(wPtr, tPtr, x, 0, buffer);
	    }
	    if (tPtr == wPtr->focus && wPtr->gotFocus) {
		FocusTab(wPtr, tPtr, x, buffer);
	    }

	    x += (wPtr->borderWidth +  wPtr->tabPadx) * 2;
	    x += tPtr->width;
	}

	/* Draw the box */
	Tk_Draw3DRectangle(wPtr->tkwin, buffer,
		wPtr->bgBorder, 0, wPtr->tabsHeight,
		Tk_Width(wPtr->tkwin),
		Tk_Height(wPtr->tkwin) - wPtr->tabsHeight,
		wPtr->borderWidth, wPtr->relief);

	if (wPtr->active != NULL) {
	    /*
	     * Fill up the gap between the active tab and the box
	     */
	    x = activex + wPtr->borderWidth;
	    y = wPtr->tabsHeight;
	    height = wPtr->borderWidth;
	    width = wPtr->active->width + wPtr->tabPadx*2;

	    XFillRectangle(wPtr->display, buffer,
		Tk_3DBorderGC(wPtr->tkwin, wPtr->bgBorder, TK_3D_FLAT_GC),
		x, y, width, height);
	}

	if (buffer != Tk_WindowId(wPtr->tkwin)) {
	    XCopyArea(wPtr->display, buffer, Tk_WindowId(wPtr->tkwin),
	        wPtr->textGC, 0, 0, Tk_Width(wPtr->tkwin),
		Tk_Height(wPtr->tkwin), 0, 0);
	    Tk_FreePixmap(wPtr->display, buffer);
	}
    }

    wPtr->redrawing = 0;
}

/*
 *--------------------------------------------------------------
 *
 * ComputeGeometry --
 *
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the user documentation.
 *
 *--------------------------------------------------------------
 */
static void
ComputeGeometry(wPtr)
    WidgetPtr wPtr;
{
    Tab * tPtr;

    /* Calculate the requested size of the widget */
    if (wPtr->tabHead == NULL) {
	wPtr->width  = wPtr->borderWidth * 2;
	wPtr->height = wPtr->borderWidth * 2;

	wPtr->tabsWidth  = 0;
	wPtr->tabsHeight = 0;
    } else {
	wPtr->tabsWidth  = 0;
	wPtr->tabsHeight = 0;
	for (tPtr=wPtr->tabHead; tPtr; tPtr=tPtr->next) {
	
	    if (tPtr->text != NULL) {
		tPtr->numChars = strlen(tPtr->text);
		TixComputeTextGeometry(wPtr->font, tPtr->text,
		    tPtr->numChars, tPtr->wrapLength,
		    &tPtr->width, &tPtr->height);
	    }
	    else if (tPtr->image != NULL) {
		Tk_SizeOfImage(tPtr->image, &tPtr->width, &tPtr->height);
	    }
	    else if (tPtr->bitmap != None) {
		Tk_SizeOfBitmap(wPtr->display, tPtr->bitmap, &tPtr->width,
		    &tPtr->height);
	    }
	    else {
		tPtr->width = 0;
		tPtr->height = 0;
	    }

	    wPtr->tabsWidth += (wPtr->borderWidth + wPtr->tabPadx) * 2;
	    wPtr->tabsWidth += tPtr->width;

	    if (tPtr->height > wPtr->tabsHeight) {
		wPtr->tabsHeight = tPtr->height;
	    }
	}
	wPtr->tabsHeight += wPtr->tabPady*2 + wPtr->borderWidth;

	wPtr->width  = wPtr->tabsWidth;
	wPtr->height = wPtr->tabsHeight + wPtr->borderWidth*2;
    }
}

/*
 *--------------------------------------------------------------
 *
 * WidgetComputeGeometry --
 *
 *	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.
 *
 *--------------------------------------------------------------
 */
static void
WidgetComputeGeometry(wPtr)
    WidgetPtr wPtr;
{
    int width;

    ComputeGeometry(wPtr);

    if (!wPtr->isSlave) {
	if (wPtr->desiredWidth > 0) {
	    width = wPtr->desiredWidth;
	} else {
	    width = wPtr->width;
	}
	Tk_GeometryRequest(wPtr->tkwin, width, wPtr->height);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * ImageProc --
 *
 *	This procedure is invoked by the image code whenever the manager
 *	for an image does something that affects the size of contents
 *	of an image displayed in this widget.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Arranges for the HList to get redisplayed.
 *
 *----------------------------------------------------------------------
 */
static void
ImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
    ClientData clientData;		/* Pointer to widget record. */
    int x, y;				/* Upper left pixel (within image)
					 * that must be redisplayed. */
    int width, height;			/* Dimensions of area to redisplay
					 * (may be <= 0). */
    int imgWidth, imgHeight;		/* New dimensions of image. */
{
    Tab * tPtr;

    tPtr = (Tab *)clientData;

    WidgetComputeGeometry(tPtr->wPtr);
    RedrawWhenIdle(tPtr->wPtr);
}