/*
 * bltTed.c --
 *
 *	This module implements an editor for the  table geometry
 *	manager in the BLT toolkit.
 *
 * Copyright 1995 by AT&T Bell Laboratories.
 * 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 AT&T Bell Laboratories any of their entities not be used
 * in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * AT&T disclaims all warranties with regard to this software, including
 * all implied warranties of merchantability and fitness.  In no event
 * shall AT&T 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.
 *
 * Table editor was created by George Howlett.
 */

#include "bltInt.h"

#include "bltTable.h"

extern Tk_CustomOption bltDistanceOption;
extern Tk_CustomOption bltDashesOption;

typedef struct TedStruct Ted;

#define TABLE_THREAD_KEY	"BLT Table Data"

typedef struct {
    Blt_HashTable tableTable;	/* Hash table of table structures keyed by 
				 * the address of the reference Tk window */
} TableData;


typedef struct {
    int flags;
    Tcl_Interp *interp;
    Tk_Window tkwin;		/* Entry window */
    Entry *entryPtr;		/* Entry it represents */
    Table *tablePtr;		/* Table where it can be found */
    Ted *tedPtr;		/* Table editor */
    int mapped;			/* Indicates if the debugging windows are
				 * mapped */
} EntryRep;


typedef struct {
    Tk_Font font;
    XColor *widgetColor;
    XColor *cntlColor;
    XColor *normalFg, *normalBg;
    XColor *activeFg, *activeBg;

    Tk_Cursor cursor;		/* Cursor to display inside of this window */
    Pixmap stipple;

    GC drawGC;			/* GC to draw grid, outlines */
    GC fillGC;			/* GC to fill entry area */
    GC widgetFillGC;		/* GC to fill widget area */
    GC cntlGC;			/* GC to fill rectangles */

} EntryAttributes;

typedef struct {
    int count;
    XRectangle *array;
} Rectangles;

struct TedStruct {
    int gridLineWidth;		/* Width of grid lines */
    int buttonHeight;		/* Height of row/column buttons */
    int cavityPad;		/* Extra padding to add to entry cavity */
    int minSize;		/* Minimum size for partitions */

    EditorDrawProc *drawProc;
    EditorDestroyProc *destroyProc;

    Display *display;
    Tk_Font font;
    Table *tablePtr;		/* Pointer to table being debugged */
    Tcl_Interp *interp;
    int flags;
    Tk_Window tkwin;		/* Grid window */
    Tk_Window input;		/* InputOnly window to receive events */
    int inputIsSibling;

    /* Form the grid */
    XSegment *segArr;
    int nSegs;
    XRectangle *padRectArr;
    int nPadRects;
    XRectangle *widgetPadRectArr;
    int nWidgetPadRects;

    XRectangle *cntlRectArr;
    int nCntlRects;

    XRectangle *rectArr;
    int nRects;

    XRectangle activeRectArr[5];
    int spanActive;

    GC rectGC;			/* GC to fill rectangles */
    GC drawGC;			/* GC to draw grid, outlines */
    GC fillGC;			/* GC to fill window */
    GC spanGC;			/* GC to fill spans */
    GC padRectGC;		/* GC to draw padding  */

    Tk_3DBorder border;		/* Border to use with buttons */
    int relief;
    int borderWidth;		/* Border width of buttons */
    XColor *normalBg;
    XColor *padColor;
    XColor *gridColor;
    XColor *buttonColor;
    XColor *spanColor;

    Pixmap padStipple;
    Pixmap spanStipple;
    Blt_Dashes dashes;
    char *fileName;		/* If non-NULL, indicates name of file
				 * to write final table output to */
    int mapped;			/* Indicates if the debugging windows are
				 * mapped */
    int gripSize;
    int doubleBuffer;
    Tk_Cursor cursor;
    Blt_Chain *chainPtr;
    int nextWindowId;

    EntryAttributes attributes;	/* Entry attributes */
};

#define REDRAW_PENDING	(1<<0)	/* A DoWhenIdle handler has already
				 * been queued to redraw the window */
#define LAYOUT_PENDING	(1<<1)

/*  
 * 
 *
 *	|Cavity|1|2|
 *
 *
 */
#define DEF_ENTRY_ACTIVE_BG_MONO	RGB_BLACK
#define DEF_ENTRY_ACTIVE_FG_MONO	RGB_WHITE
#define DEF_ENTRY_ACTIVE_BACKGROUND	RGB_BLACK
#define DEF_ENTRY_ACTIVE_FOREGROUND	RGB_WHITE
#define DEF_ENTRY_CURSOR		(char *)NULL
#define DEF_ENTRY_FONT	 	"*-Courier-Bold-R-Normal-*-100-*"
#define DEF_ENTRY_NORMAL_BACKGROUND	RGB_BLUE
#define DEF_ENTRY_NORMAL_BG_MONO	RGB_BLACK
#define DEF_ENTRY_NORMAL_FOREGROUND	RGB_WHITE
#define DEF_ENTRY_NORMAL_FG_MONO	RGB_WHITE
#define DEF_ENTRY_WIDGET_BACKGROUND	RGB_GREEN
#define DEF_ENTRY_CONTROL_BACKGROUND	RGB_YELLOW
#define DEF_ENTRY_WIDGET_BG_MONO	RGB_BLACK
#define DEF_ENTRY_STIPPLE		"gray50"
#define DEF_GRID_BACKGROUND		RGB_WHITE
#define DEF_GRID_BG_MONO		RGB_WHITE
#define DEF_GRID_CURSOR			"crosshair"
#define DEF_GRID_DASHES			(char *)NULL
#define DEF_GRID_FOREGROUND		RGB_BLACK
#define DEF_GRID_FG_MONO		RGB_BLACK
#define DEF_GRID_FONT			"*-Courier-Bold-R-Normal-*-100-*"
#define DEF_GRID_LINE_WIDTH		"1"
#define DEF_GRID_PAD_COLOR		RGB_RED
#define DEF_GRID_PAD_MONO		RGB_BLACK
#define DEF_GRID_PAD_STIPPLE		"gray25"
#define DEF_GRID_PAD_CAVITY		"0"
#define DEF_GRID_PAD_MIN		"8"
#define DEF_ROWCOL_BACKGROUND		RGB_RED
#define DEF_ROWCOL_BG_MONO		RGB_BLACK
#define DEF_ROWCOL_BORDER_COLOR		RGB_RED
#define DEF_ROWCOL_BORDER_MONO		RGB_BLACK
#define DEF_ROWCOL_BORDERWIDTH		"2"
#define DEF_ROWCOL_HEIGHT		"8"
#define DEF_ROWCOL_RELIEF		"raised"
#define DEF_SPAN_STIPPLE		"gray50"
#define DEF_SPAN_COLOR			RGB_BLACK
#define DEF_SPAN_MONO			RGB_BLACK
#define DEF_SPAN_GRIP_SIZE		"5"
#define DEF_GRID_DOUBLE_BUFFER		"1"

static Tk_ConfigSpec configSpecs[] =
{
    {TK_CONFIG_BORDER, "-bg", "tedBorder", (char *)NULL,
	DEF_ROWCOL_BORDER_COLOR, Tk_Offset(Ted, border), TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_BORDER, "-bg", "tedBorder", (char *)NULL,
	DEF_ROWCOL_BORDER_MONO, Tk_Offset(Ted, border), TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_COLOR, "-background", "tedBackground", (char *)NULL,
	DEF_GRID_BACKGROUND, Tk_Offset(Ted, normalBg), TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_COLOR, "-background", "tedBackground", (char *)NULL,
	DEF_GRID_BG_MONO, Tk_Offset(Ted, normalBg), TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_CURSOR, "-cursor", "cursor", (char *)NULL,
	DEF_GRID_CURSOR, Tk_Offset(Ted, cursor), TK_CONFIG_NULL_OK},
    {TK_CONFIG_COLOR, "-gridcolor", "gridColor", (char *)NULL,
	DEF_GRID_FOREGROUND, Tk_Offset(Ted, gridColor), TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_COLOR, "-gridcolor", "gridColor", (char *)NULL,
	DEF_GRID_FG_MONO, Tk_Offset(Ted, gridColor), TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_COLOR, "-buttoncolor", "buttonColor", (char *)NULL,
	DEF_ROWCOL_BACKGROUND, Tk_Offset(Ted, buttonColor), TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_COLOR, "-buttoncolor", "buttonColor", (char *)NULL,
	DEF_ROWCOL_BG_MONO, Tk_Offset(Ted, buttonColor), TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_COLOR, "-padcolor", "padColor", (char *)NULL,
	DEF_GRID_PAD_COLOR, Tk_Offset(Ted, padColor), TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_COLOR, "-padcolor", "padColor", (char *)NULL,
	DEF_GRID_PAD_MONO, Tk_Offset(Ted, padColor), TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_BITMAP, "-padstipple", "padStipple", (char *)NULL,
	DEF_GRID_PAD_STIPPLE, Tk_Offset(Ted, padStipple), TK_CONFIG_NULL_OK},
    {TK_CONFIG_FONT, "-font", "font", (char *)NULL,
	DEF_GRID_FONT, Tk_Offset(Ted, font), 0},
    {TK_CONFIG_CUSTOM, "-gridlinewidth", "gridLineWidth", (char *)NULL,
	DEF_GRID_LINE_WIDTH, Tk_Offset(Ted, gridLineWidth),
	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
    {TK_CONFIG_CUSTOM, "-buttonheight", "buttonHeight", (char *)NULL,
	DEF_ROWCOL_HEIGHT, Tk_Offset(Ted, buttonHeight),
	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
    {TK_CONFIG_CUSTOM, "-cavitypad", "cavityPad", (char *)NULL,
	DEF_GRID_PAD_CAVITY, Tk_Offset(Ted, cavityPad),
	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
    {TK_CONFIG_CUSTOM, "-minsize", "minSize", (char *)NULL,
	DEF_GRID_PAD_MIN, Tk_Offset(Ted, minSize),
	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
    {TK_CONFIG_CUSTOM, "-dashes", "dashes", (char *)NULL,
	DEF_GRID_DASHES, Tk_Offset(Ted, dashes),
	TK_CONFIG_NULL_OK, &bltDashesOption},
    {TK_CONFIG_RELIEF, "-relief", "relief", (char *)NULL,
	DEF_ROWCOL_RELIEF, Tk_Offset(Ted, relief), TK_CONFIG_DONT_SET_DEFAULT},
    {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", (char *)NULL,
	DEF_ROWCOL_BORDERWIDTH, Tk_Offset(Ted, borderWidth),
	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
    {TK_CONFIG_CURSOR, "-entrycursor", "entryCursor", (char *)NULL,
	DEF_ENTRY_CURSOR, Tk_Offset(Ted, attributes.cursor),
	TK_CONFIG_NULL_OK},
    {TK_CONFIG_FONT, "-entryfont", "entryFont", (char *)NULL,
	DEF_ENTRY_FONT, Tk_Offset(Ted, attributes.font), 0},
    {TK_CONFIG_BITMAP, "-entrystipple", "entryStipple", (char *)NULL,
	DEF_ENTRY_STIPPLE, Tk_Offset(Ted, attributes.stipple),
	TK_CONFIG_NULL_OK},
    {TK_CONFIG_COLOR, "-widgetbackground", "widgetBackground", (char *)NULL,
	DEF_ENTRY_WIDGET_BACKGROUND, Tk_Offset(Ted, attributes.widgetColor),
	TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_COLOR, "-widgetbackground", "widgetBackground", (char *)NULL,
	DEF_ENTRY_WIDGET_BG_MONO, Tk_Offset(Ted, attributes.widgetColor),
	TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_COLOR, "-controlbackground", "controlBackground", (char *)NULL,
	DEF_ENTRY_CONTROL_BACKGROUND, Tk_Offset(Ted, attributes.cntlColor),
	TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_COLOR, "-controlbackground", "controlBackground", (char *)NULL,
	DEF_ENTRY_WIDGET_BG_MONO, Tk_Offset(Ted, attributes.cntlColor),
	TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_COLOR, "-entrybackground", "entryBackground", (char *)NULL,
	DEF_ENTRY_NORMAL_BACKGROUND, Tk_Offset(Ted, attributes.normalBg),
	TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_COLOR, "-entrybackground", "entryBackground", (char *)NULL,
	DEF_ENTRY_NORMAL_BG_MONO, Tk_Offset(Ted, attributes.normalBg),
	TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_COLOR, "-entryactivebackground", "entryActiveBackground", (char *)NULL,
	DEF_ENTRY_ACTIVE_BACKGROUND, Tk_Offset(Ted, attributes.activeBg),
	TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_COLOR, "-entryactivebackground", "entryActiveBackground", (char *)NULL,
	DEF_ENTRY_ACTIVE_BG_MONO, Tk_Offset(Ted, attributes.activeBg),
	TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_COLOR, "-entryactiveforeground", "entryActiveForeground", (char *)NULL,
	DEF_ENTRY_ACTIVE_FOREGROUND, Tk_Offset(Ted, attributes.activeFg),
	TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_COLOR, "-entryactiveforeground", "entryActiveForeground", (char *)NULL,
	DEF_ENTRY_ACTIVE_FG_MONO, Tk_Offset(Ted, attributes.activeFg),
	TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_COLOR, "-entryforeground", "entryForeground", (char *)NULL,
	DEF_ENTRY_NORMAL_FOREGROUND, Tk_Offset(Ted, attributes.normalFg),
	TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_COLOR, "-entryforeground", "entryForeground", (char *)NULL,
	DEF_ENTRY_NORMAL_FG_MONO, Tk_Offset(Ted, attributes.normalFg),
	TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_COLOR, "-spancolor", "spanColor", (char *)NULL,
	DEF_SPAN_COLOR, Tk_Offset(Ted, spanColor), TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_COLOR, "-spancolor", "spanColor", (char *)NULL,
	DEF_SPAN_MONO, Tk_Offset(Ted, spanColor), TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_BITMAP, "-spanstipple", "spanStipple", (char *)NULL,
	DEF_SPAN_STIPPLE, Tk_Offset(Ted, spanStipple), TK_CONFIG_NULL_OK},
    {TK_CONFIG_CUSTOM, "-gripsize", "gripSize", (char *)NULL,
	DEF_SPAN_GRIP_SIZE, Tk_Offset(Ted, gripSize),
	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
    {TK_CONFIG_BOOLEAN, "-dbl", "doubleBuffer", (char *)NULL,
	DEF_GRID_DOUBLE_BUFFER, Tk_Offset(Ted, doubleBuffer),
	TK_CONFIG_DONT_SET_DEFAULT},
    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
};


static void DrawEditor _ANSI_ARGS_((Editor *editor));
static void DestroyEditor _ANSI_ARGS_((DestroyData destroyData));
static void DisplayTed _ANSI_ARGS_((ClientData clientData));
static void DestroyTed _ANSI_ARGS_((DestroyData destroyData));
static void DisplayEntry _ANSI_ARGS_((ClientData clientData));
static void DestroyEntry _ANSI_ARGS_((DestroyData destoryData));

static Tcl_CmdProc TedCmd;
static Tk_EventProc EntryEventProc;
static Tk_EventProc TedEventProc;

/*
 *----------------------------------------------------------------------
 *
 * EventuallyRedraw --
 *
 *	Queues a request to redraw the text window 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.  This doesn't
 *	seem to hurt performance noticeably, but if it does then this
 *	could be changed.
 *
 *----------------------------------------------------------------------
 */
static void
EventuallyRedraw(tedPtr)
    Ted *tedPtr;		/* Information about editor. */
{
    if ((tedPtr->tkwin != NULL) && !(tedPtr->flags & REDRAW_PENDING)) {
	tedPtr->flags |= REDRAW_PENDING;
	Tcl_DoWhenIdle(DisplayTed, tedPtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * EventuallyRedraw --
 *
 *	Queues a request to redraw the text window 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.  This doesn't
 *	seem to hurt performance noticeably, but if it does then this
 *	could be changed.
 *
 *----------------------------------------------------------------------
 */
static void
EventuallyRedrawEntry(repPtr)
    EntryRep *repPtr;		/* Information about editor. */
{
    if ((repPtr->tkwin != NULL) && !(repPtr->flags & REDRAW_PENDING)) {
	repPtr->flags |= REDRAW_PENDING;
	Tcl_DoWhenIdle(DisplayEntry, repPtr);
    }
}

/*
 * --------------------------------------------------------------
 *
 * EntryEventProc --
 *
 * 	This procedure is invoked by the Tk dispatcher for various
 * 	events on the editing grid for the table.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	When the window gets deleted, internal structures get
 *	cleaned up.  When it gets exposed, it is redisplayed.
 *
 * --------------------------------------------------------------
 */
static void
EntryEventProc(clientData, eventPtr)
    ClientData clientData;	/* Information about window. */
    XEvent *eventPtr;		/* Information about event. */
{
    EntryRep *repPtr = (EntryRep *) clientData;

    if (eventPtr->type == ConfigureNotify) {
	EventuallyRedrawEntry(repPtr);
    } else if (eventPtr->type == Expose) {
	if (eventPtr->xexpose.count == 0) {
	    EventuallyRedrawEntry(repPtr);
	}
    } else if (eventPtr->type == DestroyNotify) {
	repPtr->tkwin = NULL;
	if (repPtr->flags & REDRAW_PENDING) {
	    Tcl_CancelIdleCall(DisplayEntry, repPtr);
	}
	Tcl_EventuallyFree(repPtr, DestroyEntry);
    }
}

/*
 * --------------------------------------------------------------
 *
 * TedEventProc --
 *
 * 	This procedure is invoked by the Tk dispatcher for various
 * 	events on the editing grid for the table.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	When the window gets deleted, internal structures get
 *	cleaned up.  When it gets exposed, it is redisplayed.
 *
 * --------------------------------------------------------------
 */
static void
TedEventProc(clientData, eventPtr)
    ClientData clientData;	/* Information about window. */
    XEvent *eventPtr;		/* Information about event. */
{
    Ted *tedPtr = (Ted *) clientData;

    if (eventPtr->type == ConfigureNotify) {
	EventuallyRedraw(tedPtr);
    } else if (eventPtr->type == Expose) {
	if (eventPtr->xexpose.count == 0) {
	    EventuallyRedraw(tedPtr);
	}
    } else if (eventPtr->type == DestroyNotify) {
	tedPtr->tkwin = NULL;
	if (tedPtr->flags & REDRAW_PENDING) {
	    Tcl_CancelIdleCall(DisplayTed, tedPtr);
	}
	Tcl_EventuallyFree(tedPtr, DestroyTed);
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * CreateGrid --
 *
 * ----------------------------------------------------------------------------
 */
static int
CreateGrid(tedPtr)
    Ted *tedPtr;
{
    Tcl_Interp *interp;
    Tk_Window tkwin;
    Tk_Window master;
    /*
     * Create a sibling window to cover the master window. It will
     * be stacked just above the master window.
     */
    interp = tedPtr->tablePtr->interp;
    master = tedPtr->tablePtr->tkwin;
    tkwin = Tk_CreateWindow(interp, master, "ted_%output%", (char *)NULL);
    if (tkwin == NULL) {
	return TCL_ERROR;
    }
    Tk_SetClass(tkwin, "BltTed");
    Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
	TedEventProc, tedPtr);
    Tk_MoveResizeWindow(tkwin, 0, 0, Tk_Width(master), Tk_Height(master));
    Tk_RestackWindow(tkwin, Below, (Tk_Window)NULL);
    Tk_MapWindow(tkwin);
    tedPtr->tkwin = tkwin;
    return TCL_OK;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CreateEventWindow --
 *
 * ----------------------------------------------------------------------------
 */
static int
CreateEventWindow(tedPtr)
    Ted *tedPtr;
{
    Tcl_Interp *interp;
    Tk_Window tkwin;
    Tk_Window master;
    Tk_Window parent;

    interp = tedPtr->tablePtr->interp;
    master = tedPtr->tablePtr->tkwin;
    /*
     * Create an InputOnly window which sits above the table to
     * collect and dispatch user events.
     */
    if (Tk_IsTopLevel(master)) {
	/*
	 * If master is a top-level window, it's also the parent of
	 * the widgets (it can't have a sibling).
	 * In this case, the InputOnly window is a child of the
	 * master instead of a sibling.
	 */
	parent = master;
	tkwin = Tk_CreateWindow(interp, parent, "ted_%input%", (char *)NULL);
	if (tkwin != NULL) {
	    Tk_ResizeWindow(tkwin, Tk_Width(parent), Tk_Height(parent));
	}
	tedPtr->inputIsSibling = 0;
    } else {
	char *namePtr;		/* Name of InputOnly window. */

	parent = Tk_Parent(master);
	namePtr = Blt_Malloc(strlen(Tk_Name(master)) + 5);
	sprintf(namePtr, "ted_%s", Tk_Name(master));
	tkwin = Tk_CreateWindow(interp, parent, namePtr, (char *)NULL);
	Blt_Free(namePtr);
	if (tkwin != NULL) {
	    Tk_MoveResizeWindow(tkwin, Tk_X(master), Tk_Y(master),
		Tk_Width(master), Tk_Height(master));
	}
	tedPtr->inputIsSibling = 1;
    }
    if (tkwin == NULL) {
	return TCL_ERROR;
    }
    Blt_MakeTransparentWindowExist(tkwin, Tk_WindowId(parent), TRUE);
    Tk_RestackWindow(tkwin, Above, (Tk_Window)NULL);
    Tk_MapWindow(tkwin);
    tedPtr->input = tkwin;
    return TCL_OK;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CreateEntry --
 *
 * ----------------------------------------------------------------------------
 */
static int
CreateEntry(tedPtr, entryPtr)
    Ted *tedPtr;
    Entry *entryPtr;
{
    Tk_Window tkwin, master;
    char string[200];
    EntryRep *repPtr;
    Blt_ChainLink *linkPtr;

    repPtr = Blt_Calloc(1, sizeof(EntryRep));
    assert(repPtr);
    repPtr->tablePtr = tedPtr->tablePtr;
    repPtr->tedPtr = tedPtr;
    repPtr->interp = tedPtr->interp;
    repPtr->entryPtr = entryPtr;
    repPtr->mapped = 0;

    /*
     * Create a sibling window to cover the master window. It will
     * be stacked just above the master window.
     */

    master = tedPtr->tablePtr->tkwin;
    sprintf(string, "bltTed%d", tedPtr->nextWindowId);
    tedPtr->nextWindowId++;
    tkwin = Tk_CreateWindow(tedPtr->interp, master, string, (char *)NULL);
    if (tkwin == NULL) {
	Blt_Free(repPtr);
	return TCL_ERROR;
    }
    Tk_SetClass(tkwin, "BltTed");
    Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
	EntryEventProc, repPtr);
    repPtr->tkwin = tkwin;
    linkPtr = Blt_ChainNewLink();
    Blt_ChainSetValue(linkPtr, repPtr);
    Blt_ChainLinkAfter(tedPtr->chainPtr, linkPtr, (Blt_ChainLink *)NULL);
    return TCL_OK;
}

/*
 * ----------------------------------------------------------------------------
 *
 * DestroyEntry --
 *
 * ----------------------------------------------------------------------------
 */
static void
DestroyEntry(data)
    DestroyData data;
{
    EntryRep *repPtr = (EntryRep *)data;
    Blt_ChainLink *linkPtr;
    Entry *entryPtr;

    for (linkPtr = Blt_ChainFirstLink(repPtr->tedPtr->chainPtr);
	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
	entryPtr = Blt_ChainGetValue(linkPtr);
	if (entryPtr == repPtr->entryPtr) {
	    Blt_ChainDeleteLink(repPtr->tedPtr->chainPtr, linkPtr);
	    Blt_Free(repPtr);
	    return;
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * DisplayEntry --
 *
 * ----------------------------------------------------------------------------
 */
static void
DisplayEntry(clientData)
    ClientData clientData;
{
    EntryRep *repPtr = (EntryRep *) clientData;
    Ted *tedPtr;
    Entry *entryPtr;
    Tk_Window tkwin;
    int x, y, width, height;

    repPtr->flags &= ~REDRAW_PENDING;
    if ((repPtr->tkwin == NULL) || (repPtr->entryPtr == NULL)) {
	return;
    }
    if (!Tk_IsMapped(repPtr->tkwin)) {
	return;
    }
    tedPtr = repPtr->tedPtr;
    entryPtr = repPtr->entryPtr;
    tkwin = repPtr->tkwin;

    /*
     * Check if the entry size and position.
     * Move and resize the window accordingly.
     */
    x = Tk_X(entryPtr->tkwin) - (entryPtr->padLeft + tedPtr->cavityPad);
    y = Tk_Y(entryPtr->tkwin) - (entryPtr->padTop + tedPtr->cavityPad);
    width = Tk_Width(entryPtr->tkwin) + PADDING(entryPtr->padX) +
	(2 * tedPtr->cavityPad);
    height = Tk_Height(entryPtr->tkwin) + PADDING(entryPtr->padY) +
	(2 * tedPtr->cavityPad);


    if ((Tk_X(tkwin) != x) || (Tk_Y(tkwin) != y) || 
	(Tk_Width(tkwin) != width) || (Tk_Height(tkwin) != height)) {
	Tk_MoveResizeWindow(tkwin, x, y, width, height);
	Tk_RestackWindow(tkwin, Above, (Tk_Window)NULL);
    }
    /* Clear the background of the entry */

    XFillRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
	tedPtr->attributes.fillGC, 0, 0, width, height);

    /* Draw the window */

    x = entryPtr->padLeft + tedPtr->cavityPad;
    y = entryPtr->padTop + tedPtr->cavityPad;

    XFillRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
	tedPtr->attributes.widgetFillGC, x, y, Tk_Width(entryPtr->tkwin),
	Tk_Height(entryPtr->tkwin));
    XDrawRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
	tedPtr->attributes.drawGC, x, y, Tk_Width(entryPtr->tkwin),
	Tk_Height(entryPtr->tkwin));
}

/*
 * ----------------------------------------------------------------------------
 *
 * FindEditor --
 *
 *	Searches for a table associated with the window given by its
 *	pathname.  This window represents the master window of the table.
 *
 *	Errors may occur because
 *	  1) pathName does not represent a valid Tk window or
 *	  2) the window is not associated with any table as its master.
 *
 * Results:
 *	If a table entry exists, a pointer to the Table structure is
 *	returned. Otherwise NULL is returned.
 *
 * ----------------------------------------------------------------------------
 */
static Ted *
FindEditor(clientData, interp, pathName)
    ClientData clientData;	/* Thread-specific data. */
    Tcl_Interp *interp;		/* Interpreter to report errors back to */
    char *pathName;		/* Path name of the master window */
{
    Table *tablePtr;

    if (Blt_GetTable(clientData, interp, pathName, &tablePtr) != TCL_OK) {
	return NULL;
    }
    if (tablePtr->editPtr == NULL) {
	Tcl_AppendResult(interp, "no editor exists for table \"",
	    Tk_PathName(tablePtr->tkwin), "\"", (char *)NULL);
	return NULL;
    }
    return (Ted *) tablePtr->editPtr;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CreateTed --
 *
 * ----------------------------------------------------------------------------
 */
static Ted *
CreateTed(tablePtr, interp)
    Table *tablePtr;
    Tcl_Interp *interp;
{
    Ted *tedPtr;

    tedPtr = Blt_Calloc(1, sizeof(Ted));
    assert(tedPtr);
    tedPtr->nextWindowId = 0;
    tedPtr->interp = interp;
    tedPtr->tablePtr = tablePtr;
    tedPtr->gridLineWidth = 1;
    tedPtr->buttonHeight = 0;
    tedPtr->cavityPad = 0;
    tedPtr->minSize = 3;
    tedPtr->gripSize = 5;
    tedPtr->drawProc = DrawEditor;
    tedPtr->destroyProc = DestroyEditor;
    tedPtr->display = Tk_Display(tablePtr->tkwin);
    tedPtr->relief = TK_RELIEF_RAISED;
    tedPtr->borderWidth = 2;
    tedPtr->doubleBuffer = 1;
    tedPtr->chainPtr = Blt_ChainCreate();
    /* Create the grid window */

    if (CreateGrid(tedPtr) != TCL_OK) {
	return NULL;
    }
    /* Create an InputOnly window to collect user events */
    if (CreateEventWindow(tedPtr) != TCL_OK) {
	return NULL;
    }
    tablePtr->editPtr = (Editor *)tedPtr;
    return tedPtr;
}

/*
 * ----------------------------------------------------------------------------
 *
 * DestroyTed --
 *
 * ----------------------------------------------------------------------------
 */
static void
DestroyTed(freeProcData)
    DestroyData freeProcData;
{
    Ted *tedPtr = (Ted *) freeProcData;

    if (tedPtr->rectArr != NULL) {
	Blt_Free(tedPtr->rectArr);
    }
    if (tedPtr->segArr != NULL) {
	Blt_Free(tedPtr->segArr);
    }
    if (tedPtr->fillGC != NULL) {
	Tk_FreeGC(tedPtr->display, tedPtr->fillGC);
    }
    if (tedPtr->drawGC != NULL) {
	Tk_FreeGC(tedPtr->display, tedPtr->drawGC);
    }
    if (tedPtr->rectGC != NULL) {
	Tk_FreeGC(tedPtr->display, tedPtr->rectGC);
    }
    if (tedPtr->padRectGC != NULL) {
	Tk_FreeGC(tedPtr->display, tedPtr->padRectGC);
    }
    /* Is this save ? */
    tedPtr->tablePtr->editPtr = NULL;
    Blt_Free(tedPtr);

}

/*
 * ----------------------------------------------------------------------------
 *
 * ConfigureTed --
 *
 *	This procedure is called to process an argv/argc list in order to
 *	configure the table geometry manager.
 *
 * Results:
 *	The return value is a standard Tcl result.  If TCL_ERROR is
 *	returned, then the result contains an error message.
 *
 * Side effects:
 *	Table configuration options (padx, pady, rows, columns, etc) get
 *	set.   The table is recalculated and arranged at the next idle
 *	point.
 *
 * ----------------------------------------------------------------------------
 */
static int
ConfigureTed(tedPtr, argc, argv, flags)
    Ted *tedPtr;
    int argc;
    char **argv;		/* Option-value pairs */
    int flags;
{
    XGCValues gcValues;
    GC newGC;
    unsigned long gcMask;

    if (Tk_ConfigureWidget(tedPtr->interp, tedPtr->tkwin, configSpecs,
	    argc, argv, (char *)tedPtr, flags) != TCL_OK) {
	return TCL_ERROR;
    }
    /* GC for filling background of edit window */

    gcMask = GCForeground;
    gcValues.foreground = tedPtr->normalBg->pixel;
    newGC = Tk_GetGC(tedPtr->tkwin, gcMask, &gcValues);
    if (tedPtr->fillGC != NULL) {
	Tk_FreeGC(tedPtr->display, tedPtr->fillGC);
    }
    tedPtr->fillGC = newGC;

    /* GC for drawing grid lines */

    gcMask = (GCForeground | GCBackground | GCLineWidth | GCLineStyle |
	GCCapStyle | GCJoinStyle | GCFont);
    gcValues.font = Tk_FontId(tedPtr->font);
    gcValues.foreground = tedPtr->gridColor->pixel;
    gcValues.background = tedPtr->normalBg->pixel;
    gcValues.line_width = LineWidth(tedPtr->gridLineWidth);
    gcValues.cap_style = CapRound;
    gcValues.join_style = JoinRound;
    gcValues.line_style = LineSolid;
    if (LineIsDashed(tedPtr->dashes)) {
	gcValues.line_style = LineOnOffDash;
    }
    newGC = Blt_GetPrivateGC(tedPtr->tkwin, gcMask, &gcValues);
    if (tedPtr->drawGC != NULL) {
	Blt_FreePrivateGC(tedPtr->display, tedPtr->drawGC);
    }
    if (LineIsDashed(tedPtr->dashes)) {
	XSetDashes(tedPtr->display, newGC, 0, 
		   (CONST char *)tedPtr->dashes.values,
		   strlen((char *)tedPtr->dashes.values));
    }
    tedPtr->drawGC = newGC;

    /* GC for button rectangles */

    gcMask = GCForeground;
    gcValues.foreground = tedPtr->buttonColor->pixel;
    newGC = Tk_GetGC(tedPtr->tkwin, gcMask, &gcValues);
    if (tedPtr->rectGC != NULL) {
	Tk_FreeGC(tedPtr->display, tedPtr->rectGC);
    }
    tedPtr->rectGC = newGC;

    /* GC for button rectangles */

    gcMask = GCForeground;
    gcValues.foreground = tedPtr->padColor->pixel;
    if (tedPtr->padStipple != None) {
	gcMask |= GCStipple | GCFillStyle;
	gcValues.stipple = tedPtr->padStipple;
	gcValues.fill_style = FillStippled;
    }
    newGC = Tk_GetGC(tedPtr->tkwin, gcMask, &gcValues);
    if (tedPtr->padRectGC != NULL) {
	Tk_FreeGC(tedPtr->display, tedPtr->padRectGC);
    }
    tedPtr->padRectGC = newGC;

    /* GC for filling entrys */

    gcMask = GCForeground;
    gcValues.foreground = tedPtr->attributes.normalBg->pixel;
    if (tedPtr->attributes.stipple != None) {
	gcMask |= GCStipple | GCFillStyle;
	gcValues.stipple = tedPtr->attributes.stipple;
	gcValues.fill_style = FillStippled;
    }
    newGC = Tk_GetGC(tedPtr->tkwin, gcMask, &gcValues);
    if (tedPtr->attributes.fillGC != NULL) {
	Tk_FreeGC(tedPtr->display, tedPtr->attributes.fillGC);
    }
    tedPtr->attributes.fillGC = newGC;

    /* GC for drawing entrys */

    gcMask = GCForeground | GCBackground | GCFont;
    gcValues.foreground = tedPtr->attributes.normalFg->pixel;
    gcValues.background = tedPtr->attributes.normalBg->pixel;
    gcValues.font = Tk_FontId(tedPtr->attributes.font);
    newGC = Tk_GetGC(tedPtr->tkwin, gcMask, &gcValues);
    if (tedPtr->attributes.drawGC != NULL) {
	Blt_FreePrivateGC(tedPtr->display, tedPtr->attributes.drawGC);
    }
    tedPtr->attributes.drawGC = newGC;

    /* GC for filling widget rectangles */

    gcMask = GCForeground;
    gcValues.foreground = tedPtr->attributes.widgetColor->pixel;
    newGC = Tk_GetGC(tedPtr->tkwin, gcMask, &gcValues);
    if (tedPtr->attributes.widgetFillGC != NULL) {
	Tk_FreeGC(tedPtr->display, tedPtr->attributes.widgetFillGC);
    }
    tedPtr->attributes.widgetFillGC = newGC;

    gcMask = GCForeground;
    gcValues.foreground = tedPtr->attributes.cntlColor->pixel;
    newGC = Tk_GetGC(tedPtr->tkwin, gcMask, &gcValues);
    if (tedPtr->attributes.cntlGC != NULL) {
	Tk_FreeGC(tedPtr->display, tedPtr->attributes.cntlGC);
    }
    tedPtr->attributes.cntlGC = newGC;

    /* GC for filling span rectangle */

    gcMask = GCForeground;
    gcValues.foreground = tedPtr->spanColor->pixel;
    if (tedPtr->spanStipple != None) {
	gcMask |= GCStipple | GCFillStyle;
	gcValues.stipple = tedPtr->spanStipple;
	gcValues.fill_style = FillStippled;
    }
    newGC = Tk_GetGC(tedPtr->tkwin, gcMask, &gcValues);
    if (tedPtr->spanGC != NULL) {
	Tk_FreeGC(tedPtr->display, tedPtr->spanGC);
    }
    tedPtr->spanGC = newGC;

    /* Define cursor for grid events */
    if (tedPtr->cursor != None) {
	Tk_DefineCursor(tedPtr->input, tedPtr->cursor);
    } else {
	Tk_UndefineCursor(tedPtr->input);
    }
    return TCL_OK;
}


static void
LayoutGrid(tedPtr)
    Ted *tedPtr;
{
    int needed;
    XSegment *segArr;
    Table *tablePtr;
    Blt_ChainLink *linkPtr;
    RowColumn *rcPtr;
    int startX, endX;
    int startY, endY;
    int count;

    tablePtr = tedPtr->tablePtr;
    if (tedPtr->segArr != NULL) {
	Blt_Free(tedPtr->segArr);
	tedPtr->segArr = NULL;
    }
    tedPtr->nSegs = 0;
    if ((tablePtr->nRows == 0) || (tablePtr->nColumns == 0)) {
	return;
    }
    needed = tablePtr->nRows + tablePtr->nColumns + 2;
    segArr = Blt_Calloc(needed, sizeof(XSegment));
    if (segArr == NULL) {
	return;
    }
    linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
    rcPtr = Blt_ChainGetValue(linkPtr);
    startX = rcPtr->offset - tedPtr->gridLineWidth;

    linkPtr = Blt_ChainLastLink(tablePtr->columnInfo.chainPtr);
    rcPtr = Blt_ChainGetValue(linkPtr);
    endX = rcPtr->offset + rcPtr->size - 1;

    linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
    rcPtr = Blt_ChainGetValue(linkPtr);
    startY = rcPtr->offset - tedPtr->gridLineWidth;

    linkPtr = Blt_ChainLastLink(tablePtr->rowInfo.chainPtr);
    rcPtr = Blt_ChainGetValue(linkPtr);
    endY = rcPtr->offset + rcPtr->size - 1;

    count = 0;			/* Reset segment counter */

    for (linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
	rcPtr = Blt_ChainGetValue(linkPtr);
	segArr[count].x1 = startX;
	segArr[count].x2 = endX;
	segArr[count].y1 = segArr[count].y2 = rcPtr->offset - 
	    tedPtr->gridLineWidth;
	count++;
    }
    segArr[count].x1 = startX;
    segArr[count].x2 = endX;
    segArr[count].y1 = segArr[count].y2 = endY;
    count++;

    for (linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
	rcPtr = Blt_ChainGetValue(linkPtr);
	segArr[count].y1 = startY;
	segArr[count].y2 = endY;
	segArr[count].x1 = segArr[count].x2 = rcPtr->offset - 
	    tedPtr->gridLineWidth;
	count++;
    }
    segArr[count].x1 = segArr[count].x2 = endX;
    segArr[count].y1 = startY;
    segArr[count].y2 = endY;
    count++;
    assert(count == needed);
    if (tedPtr->segArr != NULL) {
	Blt_Free(tedPtr->segArr);
    }
    tedPtr->segArr = segArr;
    tedPtr->nSegs = count;
}


static void
LayoutPads(tedPtr)
    Ted *tedPtr;
{
    int needed;
    XRectangle *rectArr, *rectPtr;
    Table *tablePtr;
    Blt_ChainLink *linkPtr;
    RowColumn *rcPtr;
    int startX, endX;
    int startY, endY;
    int count;

    tablePtr = tedPtr->tablePtr;
    if (tedPtr->padRectArr != NULL) {
	Blt_Free(tedPtr->padRectArr);
	tedPtr->padRectArr = NULL;
    }
    tedPtr->nPadRects = 0;
    if ((tablePtr->nRows == 0) || (tablePtr->nColumns == 0)) {
	return;
    }
    needed = 2 * (tablePtr->nRows + tablePtr->nColumns);
    rectArr = Blt_Calloc(needed, sizeof(XRectangle));
    if (rectArr == NULL) {
	return;
    }
    linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
    rcPtr = Blt_ChainGetValue(linkPtr);
    startX = rcPtr->offset;

    linkPtr = Blt_ChainLastLink(tablePtr->columnInfo.chainPtr);
    rcPtr = Blt_ChainGetValue(linkPtr);
    endX = (rcPtr->offset + rcPtr->size);

    linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
    rcPtr = Blt_ChainGetValue(linkPtr);
    startY = rcPtr->offset;

    linkPtr = Blt_ChainLastLink(tablePtr->rowInfo.chainPtr);
    rcPtr = Blt_ChainGetValue(linkPtr);
    endY = (rcPtr->offset + rcPtr->size);

    count = 0;			/* Reset segment counter */
    rectPtr = rectArr;
    for (linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
	rcPtr = Blt_ChainGetValue(linkPtr);
	if (rcPtr->pad.side1 > 0) {
	    rectPtr->x = startX;
	    rectPtr->y = rcPtr->offset;
	    rectPtr->height = rcPtr->pad.side1;
	    rectPtr->width = endX - startX - 1;
	    rectPtr++, count++;
	}
	if (rcPtr->pad.side2 > 0) {
	    rectPtr->x = startX;
	    rectPtr->y = rcPtr->offset + rcPtr->size - rcPtr->pad.side2 - 1;
	    rectPtr->height = rcPtr->pad.side2;
	    rectPtr->width = endX - startX - 1;
	    rectPtr++, count++;
	}
    }
    for (linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
	rcPtr = Blt_ChainGetValue(linkPtr);
	if (rcPtr->pad.side1 > 0) {
	    rectPtr->x = rcPtr->offset;
	    rectPtr->y = startY;
	    rectPtr->height = endY - startY - 1;
	    rectPtr->width = rcPtr->pad.side1;
	    rectPtr++, count++;
	}
	if (rcPtr->pad.side2 > 0) {
	    rectPtr->x = rcPtr->offset + rcPtr->size - rcPtr->pad.side2;
	    rectPtr->y = startY;
	    rectPtr->height = endY - startY - 1;
	    rectPtr->width = rcPtr->pad.side2;
	    rectPtr++, count++;
	}
    }
    if (count == 0) {
	Blt_Free(rectArr);
	return;
    }
    tedPtr->padRectArr = rectArr;
    tedPtr->nPadRects = count;
}

static void
LayoutEntries(tedPtr)
    Ted *tedPtr;
{
    Entry *entryPtr;
    XRectangle *rectArr;
    int needed;
    int count;
    Blt_ChainLink *linkPtr;

    if (tedPtr->widgetPadRectArr != NULL) {
	Blt_Free(tedPtr->widgetPadRectArr);
	tedPtr->widgetPadRectArr = NULL;
    }
    tedPtr->nWidgetPadRects = 0;

    needed = Blt_ChainGetLength(tedPtr->tablePtr->chainPtr);
    rectArr = Blt_Calloc(needed, sizeof(XRectangle));
    if (rectArr == NULL) {
	return;
    }
    /* Draw any entry windows */
    count = 0;
    for (linkPtr = Blt_ChainFirstLink(tedPtr->tablePtr->chainPtr);
	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
	entryPtr = Blt_ChainGetValue(linkPtr);
	if ((PADDING(entryPtr->padX) + PADDING(entryPtr->padY)) == 0) {
	    continue;
	}
	rectArr[count].x = Tk_X(entryPtr->tkwin) - entryPtr->padLeft;
	rectArr[count].y = Tk_Y(entryPtr->tkwin) - entryPtr->padTop;
	rectArr[count].width = Tk_Width(entryPtr->tkwin) +
	    PADDING(entryPtr->padX);
	rectArr[count].height = Tk_Height(entryPtr->tkwin) +
	    PADDING(entryPtr->padY);
	count++;
    }
    if (count == 0) {
	Blt_Free(rectArr);
	return;
    }
    tedPtr->widgetPadRectArr = rectArr;
    tedPtr->nWidgetPadRects = count;
}

static void
LayoutControlEntries(tedPtr)
    Ted *tedPtr;
{
    Entry *entryPtr;
    XRectangle *rectArr;
    int needed;
    int count;
    Table *tablePtr = tedPtr->tablePtr;
    Blt_ChainLink *linkPtr;
    RowColumn *rcPtr;

    if (tedPtr->cntlRectArr != NULL) {
	Blt_Free(tedPtr->cntlRectArr);
	tedPtr->cntlRectArr = NULL;
    }
    tedPtr->nCntlRects = 0;

    needed = (tablePtr->nRows + tablePtr->nColumns);
    rectArr = Blt_Calloc(needed, sizeof(XRectangle));
    if (rectArr == NULL) {
	return;
    }
    /* Draw any entry windows */
    count = 0;
    for (linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
	rcPtr = Blt_ChainGetValue(linkPtr);
	entryPtr = rcPtr->control;
	if (entryPtr != NULL) {
	    rectArr[count].x = Tk_X(entryPtr->tkwin) - entryPtr->padLeft;
	    rectArr[count].y = Tk_Y(entryPtr->tkwin) - entryPtr->padTop;
	    rectArr[count].width = Tk_Width(entryPtr->tkwin) +
		PADDING(entryPtr->padX);
	    rectArr[count].height = Tk_Height(entryPtr->tkwin) +
		PADDING(entryPtr->padY);
	    count++;
	}
    }
    for (linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
	rcPtr = Blt_ChainGetValue(linkPtr);
	entryPtr = rcPtr->control;
	if (entryPtr != NULL) {
	    rectArr[count].x = Tk_X(entryPtr->tkwin) - entryPtr->padLeft;
	    rectArr[count].y = Tk_Y(entryPtr->tkwin) - entryPtr->padTop;
	    rectArr[count].width = Tk_Width(entryPtr->tkwin) +
		PADDING(entryPtr->padX);
	    rectArr[count].height = Tk_Height(entryPtr->tkwin) +
		PADDING(entryPtr->padY);
	    count++;
	}
    }
    if (count == 0) {
	Blt_Free(rectArr);
	return;
    }
    tedPtr->cntlRectArr = rectArr;
    tedPtr->nCntlRects = count;
}

static void
LayoutButtons(tedPtr)
    Ted *tedPtr;
{
    int needed;
    XRectangle *rectArr;
    Table *tablePtr;
    Blt_ChainLink *linkPtr;
    RowColumn *rcPtr;
    int count;

    tablePtr = tedPtr->tablePtr;
    if ((tablePtr->nRows == 0) || (tablePtr->nColumns == 0)) {
	if (tedPtr->rectArr != NULL) {
	    Blt_Free(tedPtr->rectArr);
	}
	tedPtr->rectArr = NULL;
	tedPtr->nRects = 0;
	return;			/* Nothing to display, empty table */
    }
    needed = 2 * (tablePtr->nRows + tablePtr->nColumns);
    rectArr = Blt_Calloc(needed, sizeof(XRectangle));
    if (rectArr == NULL) {
	return;			/* Can't allocate rectangles */
    }
    count = 0;
    for (linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
	rcPtr = Blt_ChainGetValue(linkPtr);
	rectArr[count].x = 0;
	rectArr[count].y = rcPtr->offset - rcPtr->pad.side1;
	rectArr[count].width = tedPtr->buttonHeight;
	rectArr[count].height = rcPtr->size - 2;
	count++;
	rectArr[count].x = Tk_Width(tedPtr->tkwin) - tedPtr->buttonHeight;
	rectArr[count].y = rcPtr->offset - rcPtr->pad.side1;
	rectArr[count].width = tedPtr->buttonHeight;
	rectArr[count].height = rcPtr->size - 2;
	count++;
    }
    for (linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
	rcPtr = Blt_ChainGetValue(linkPtr);
	rectArr[count].x = rcPtr->offset - rcPtr->pad.side1;
	rectArr[count].y = 0;
	rectArr[count].width = rcPtr->size - 2;
	rectArr[count].height = tedPtr->buttonHeight;
	count++;
	rectArr[count].x = rcPtr->offset - rcPtr->pad.side1;
	rectArr[count].y = Tk_Height(tedPtr->tkwin) - tedPtr->buttonHeight;
	rectArr[count].width = rcPtr->size - 2;
	rectArr[count].height = tedPtr->buttonHeight;
	count++;
    }
    assert(count == needed);
    if (tedPtr->rectArr != NULL) {
	Blt_Free(tedPtr->rectArr);
    }
    tedPtr->rectArr = rectArr;
    tedPtr->nRects = count;
}


static void
DisplayTed(clientData)
    ClientData clientData;
{
    Ted *tedPtr = (Ted *) clientData;
    Tk_Window master;
    Tk_Window tkwin;
    Blt_ChainLink *linkPtr;
    EntryRep *repPtr;
    Drawable drawable;
    Pixmap pixmap;

#ifdef notdef
    fprintf(stderr, "display grid\n");
#endif
    tedPtr->flags &= ~REDRAW_PENDING;
    if (!Tk_IsMapped(tedPtr->tkwin)) {
	return;
    }
    /*
     * Check if the master window has changed size and resize the
     * grid and input windows accordingly.
     */
    master = tedPtr->tablePtr->tkwin;
    if ((Tk_Width(master) != Tk_Width(tedPtr->tkwin)) ||
	(Tk_Height(master) != Tk_Height(tedPtr->tkwin))) {
#ifdef notdef
	fprintf(stderr, "resizing windows\n");
#endif
	Tk_ResizeWindow(tedPtr->tkwin, Tk_Width(master), Tk_Height(master));
	Tk_ResizeWindow(tedPtr->input, Tk_Width(master), Tk_Height(master));
	if (tedPtr->inputIsSibling) {
	    Tk_MoveWindow(tedPtr->input, Tk_X(master), Tk_X(master));
	}
	tedPtr->flags |= LAYOUT_PENDING;
    }
    if (tedPtr->flags & LAYOUT_PENDING) {
#ifdef notdef
	fprintf(stderr, "layout of grid\n");
#endif
	LayoutPads(tedPtr);
	LayoutEntries(tedPtr);
	LayoutControlEntries(tedPtr);
	LayoutGrid(tedPtr);
	LayoutButtons(tedPtr);
	tedPtr->flags &= ~LAYOUT_PENDING;
    }
    tkwin = tedPtr->tkwin;

    pixmap = None;		/* Suppress compiler warning. */
    drawable = Tk_WindowId(tkwin);
    if (tedPtr->doubleBuffer) {
	/* Create an off-screen pixmap for semi-smooth scrolling. */
	pixmap = Tk_GetPixmap(tedPtr->display, Tk_WindowId(tkwin),
	    Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
	drawable = pixmap;
    }
    /* Clear the background of the grid */

    XFillRectangle(Tk_Display(tkwin), drawable, tedPtr->fillGC, 0, 0,
	Tk_Width(tkwin), Tk_Height(tkwin));

    /* Draw the row and column buttons */

    if (tedPtr->nRects > 0) {
	int i;

	for (i = 0; i < tedPtr->nRects; i++) {
	    Blt_Fill3DRectangle(tkwin, drawable, tedPtr->border,
		tedPtr->rectArr[i].x, tedPtr->rectArr[i].y,
		tedPtr->rectArr[i].width, tedPtr->rectArr[i].height,
		tedPtr->borderWidth, tedPtr->relief);
	}
#ifdef notdef
	XFillRectangles(tedPtr->display, drawable, tedPtr->rectGC,
	    tedPtr->rectArr, tedPtr->nRects);
	XDrawRectangles(tedPtr->display, drawable, tedPtr->drawGC,
	    tedPtr->rectArr, tedPtr->nRects);
#endif
    }
    if (tedPtr->nPadRects > 0) {
	XFillRectangles(tedPtr->display, drawable, tedPtr->padRectGC,
	    tedPtr->padRectArr, tedPtr->nPadRects);
    }
    if (tedPtr->spanActive) {
	XFillRectangles(tedPtr->display, drawable, tedPtr->spanGC,
	    tedPtr->activeRectArr, 1);
	XFillRectangles(tedPtr->display, drawable, tedPtr->drawGC,
	    tedPtr->activeRectArr + 1, 4);
    }
    if (tedPtr->nWidgetPadRects > 0) {
	XFillRectangles(tedPtr->display, drawable, tedPtr->attributes.fillGC,
	    tedPtr->widgetPadRectArr, tedPtr->nWidgetPadRects);
    }
    if (tedPtr->nCntlRects > 0) {
	XFillRectangles(tedPtr->display, drawable, tedPtr->attributes.cntlGC,
	    tedPtr->cntlRectArr, tedPtr->nCntlRects);
    }
    /* Draw the grid lines */
    if (tedPtr->nSegs > 0) {
	XDrawSegments(tedPtr->display, drawable, tedPtr->drawGC,
	    tedPtr->segArr, tedPtr->nSegs);
    }
#ifndef notdef
    /* Draw any entry windows */
    for (linkPtr = Blt_ChainFirstLink(tedPtr->chainPtr); linkPtr != NULL;
	linkPtr = Blt_ChainNextLink(linkPtr)) {
	repPtr = Blt_ChainGetValue(linkPtr);
	if (repPtr->mapped) {
	    DisplayEntry(repPtr);
	}
    }
#endif
    if (tedPtr->doubleBuffer) {
	XCopyArea(tedPtr->display, drawable, Tk_WindowId(tkwin), tedPtr->fillGC,
	    0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
	Tk_FreePixmap(tedPtr->display, pixmap);
    }
}


static void
DrawEditor(editPtr)
    Editor *editPtr;
{
    Ted *tedPtr = (Ted *) editPtr;

    tedPtr->flags |= LAYOUT_PENDING;
    if ((tedPtr->tkwin != NULL) && !(tedPtr->flags & REDRAW_PENDING)) {
	tedPtr->flags |= REDRAW_PENDING;
#ifdef notdef
	fprintf(stderr, "from draw editor\n");
#endif
	Tcl_DoWhenIdle(DisplayTed, tedPtr);
    }
}

static void
DestroyEditor(destroyData)
    DestroyData destroyData;
{
    Ted *tedPtr = (Ted *) destroyData;

    tedPtr->tkwin = NULL;
    if (tedPtr->flags & REDRAW_PENDING) {
	Tcl_CancelIdleCall(DisplayTed, tedPtr);
    }
    Tcl_EventuallyFree(tedPtr, DestroyTed);
}

/*
 * ----------------------------------------------------------------------------
 *
 * EditOp --
 *
 *	Processes an argv/argc list of table entries to add and configure
 *	new widgets into the table.  A table entry consists of the
 *	window path name, table index, and optional configuration options.
 *	The first argument in the argv list is the name of the table.  If
 *	no table exists for the given window, a new one is created.
 *
 * Results:
 *	Returns a standard Tcl result.  If an error occurred, TCL_ERROR is
 *	returned and an error message is in Tcl_GetStringResult(interp).
 *
 * Side Effects:
 *	Memory is allocated, a new master table is possibly created, etc.
 *	The table is re-computed and arranged at the next idle point.
 *
 * ----------------------------------------------------------------------------
 */
static int
EditOp(dataPtr, interp, argc, argv)
    TableInterpData *dataPtr;	/* Interpreter-specific data. */
    Tcl_Interp *interp;		/* Interpreter to return list of names to */
    int argc;			/* Number of arguments */
    char **argv;
{
    Table *tablePtr;
    Ted *tedPtr;

    if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
	return TCL_ERROR;
    }
    if (tablePtr->editPtr != NULL) {	/* Already editing this table */
	tedPtr = (Ted *) tablePtr->editPtr;
    } else {
	tedPtr = CreateTed(tablePtr, interp);
	if (tedPtr == NULL) {
	    return TCL_ERROR;
	}
    }
    if (ConfigureTed(tedPtr, argc - 3, argv + 3, 0) != TCL_OK) {
	tedPtr->tkwin = NULL;
	if (tedPtr->flags & REDRAW_PENDING) {
	    Tcl_CancelIdleCall(DisplayTed, tedPtr);
	}
	Tcl_EventuallyFree(tedPtr, DestroyTed);
	return TCL_ERROR;
    }
    /* Rearrange the table */
    if (!(tablePtr->flags & ARRANGE_PENDING)) {
	tablePtr->flags |= ARRANGE_PENDING;
	Tcl_DoWhenIdle(tablePtr->arrangeProc, tablePtr);
    }
	Tcl_SetResult(interp, Tk_PathName(tedPtr->tkwin), TCL_VOLATILE);
    tedPtr->flags |= LAYOUT_PENDING;
    EventuallyRedraw(tedPtr);
    return TCL_OK;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CgetCmd --
 *
 * Results:
 *	The return value is a standard Tcl result.  If TCL_ERROR is
 *	returned, then the result contains an error message.
 *
 * ----------------------------------------------------------------------------
 */
/*ARGSUSED*/
static int
CgetOp(dataPtr, interp, argc, argv)
    TableInterpData *dataPtr;	/* Interpreter-specific data. */
    Tcl_Interp *interp;		/* Interpreter to report results back to */
    int argc;			/* Not used. */
    char **argv;		/* Option-value pairs */
{
    Ted *tedPtr;

    tedPtr = FindEditor(dataPtr, interp, argv[2]);
    if (tedPtr == NULL) {
	return TCL_ERROR;
    }
    return Tk_ConfigureValue(interp, tedPtr->tkwin, configSpecs,
	    (char *)tedPtr, argv[3], 0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * ConfigureCmd --
 *
 *	This procedure is called to process an argv/argc list in order to
 *	configure the table geometry manager.
 *
 * Results:
 *	The return value is a standard Tcl result.  If TCL_ERROR is
 *	returned, then the result contains an error message.
 *
 * Side effects:
 *	Table configuration options (padx, pady, rows, columns, etc) get
 *	set.   The table is recalculated and arranged at the next idle
 *	point.
 *
 * ----------------------------------------------------------------------------
 */
static int
ConfigureOp(dataPtr, interp, argc, argv)
    TableInterpData *dataPtr;	/* Interpreter-specific data. */
    Tcl_Interp *interp;		/* Interpreter to report results back to */
    int argc;
    char **argv;		/* Option-value pairs */
{
    Ted *tedPtr;

    tedPtr = FindEditor(dataPtr, interp, argv[2]);
    if (tedPtr == NULL) {
	return TCL_ERROR;
    }
    if (argc == 3) {
	return Tk_ConfigureInfo(interp, tedPtr->tkwin, configSpecs,
		(char *)tedPtr, (char *)NULL, 0);
    } else if (argc == 4) {
	return Tk_ConfigureInfo(interp, tedPtr->tkwin, configSpecs,
		(char *)tedPtr, argv[3], 0);
    }
    if (ConfigureTed(tedPtr, argc - 3, argv + 3,
	    TK_CONFIG_ARGV_ONLY) != TCL_OK) {
	return TCL_ERROR;
    }
    EventuallyRedraw(tedPtr);
    return TCL_OK;
}

/*
 * ----------------------------------------------------------------------------
 *
 * SelectOp --
 *
 * ----------------------------------------------------------------------------
 */
/*ARGSUSED*/
static int
SelectOp(dataPtr, interp, argc, argv)
    TableInterpData *dataPtr;	/* Interpreter-specific data. */
    Tcl_Interp *interp;		/* Interpreter to return list of names to */
    int argc;			/* Not used. */
    char **argv;
{
    Table *tablePtr;
    Ted *tedPtr;
    Entry *entryPtr;
    int active;
    int x, y, width, height;
    int ix, iy;
    Blt_ChainLink *linkPtr;
    Tk_Window tkwin;

    /* ted select master @x,y */
    tkwin = Tk_MainWindow(interp);
    tedPtr = FindEditor(dataPtr, interp, argv[2]);
    if (tedPtr == NULL) {
	return TCL_ERROR;
    }
    if (Blt_GetXY(interp, tkwin, argv[3], &ix, &iy) != TCL_OK) {
	return TCL_ERROR;
    }
    tablePtr = tedPtr->tablePtr;
    active = 0;
    for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr);
	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
	entryPtr = Blt_ChainGetValue(linkPtr);
	x = entryPtr->x - entryPtr->padX.side1;
	y = entryPtr->y - entryPtr->padY.side1;
	width = Tk_Width(entryPtr->tkwin) + PADDING(entryPtr->padX);
	height = Tk_Height(entryPtr->tkwin) + PADDING(entryPtr->padY);
	if ((ix >= x) && (ix <= (x + width)) &&
	    (iy >= y) && (iy <= (y + height))) {
	    int left, right, top, bottom;
	    int last;
	    int grip;
	    RowColumn *rcPtr;

	    last = entryPtr->column.rcPtr->index + entryPtr->column.span - 1;
	    linkPtr = Blt_ChainGetNthLink(tablePtr->columnInfo.chainPtr, last);
	    rcPtr = Blt_ChainGetValue(linkPtr);

	    /* Calculate the span rectangle */
	    left = (entryPtr->column.rcPtr->offset -
		entryPtr->column.rcPtr->pad.side1);
	    right = (rcPtr->offset - rcPtr->pad.side1) + rcPtr->size;

	    top = (entryPtr->row.rcPtr->offset -
		entryPtr->row.rcPtr->pad.side1);

	    last = entryPtr->row.rcPtr->index + entryPtr->row.span - 1;
	    linkPtr = Blt_ChainGetNthLink(tablePtr->rowInfo.chainPtr, last);
	    rcPtr = Blt_ChainGetValue(linkPtr);
	    bottom = (rcPtr->offset - rcPtr->pad.side1) + rcPtr->size;

	    tedPtr->activeRectArr[0].x = left;
	    tedPtr->activeRectArr[0].y = top;
	    tedPtr->activeRectArr[0].width = (right - left);
	    tedPtr->activeRectArr[0].height = (bottom - top);

	    grip = tedPtr->gripSize;
	    tedPtr->activeRectArr[1].x = (left + right - grip) / 2;
	    tedPtr->activeRectArr[1].y = top;
	    tedPtr->activeRectArr[1].width = grip - 1;
	    tedPtr->activeRectArr[1].height = grip - 1;

	    tedPtr->activeRectArr[2].x = left;
	    tedPtr->activeRectArr[2].y = (top + bottom - grip) / 2;
	    tedPtr->activeRectArr[2].width = grip - 1;
	    tedPtr->activeRectArr[2].height = grip - 1;

	    tedPtr->activeRectArr[3].x = (left + right - grip) / 2;
	    tedPtr->activeRectArr[3].y = bottom - grip;
	    tedPtr->activeRectArr[3].width = grip - 1;
	    tedPtr->activeRectArr[3].height = grip - 1;

	    tedPtr->activeRectArr[4].x = right - grip;
	    tedPtr->activeRectArr[4].y = (top + bottom - grip) / 2;
	    tedPtr->activeRectArr[4].width = grip - 1;
	    tedPtr->activeRectArr[4].height = grip - 1;

	    Tcl_SetResult(interp, Tk_PathName(entryPtr->tkwin), TCL_VOLATILE);
	    active = 1;
	    break;
	}
    }
    if ((active) || (active != tedPtr->spanActive)) {
	tedPtr->spanActive = active;
	EventuallyRedraw(tedPtr);
    }
    return TCL_OK;
}

/*
 * ----------------------------------------------------------------------------
 *
 * EditOp --
 *
 *	Processes an argv/argc list of table entries to add and configure
 *	new widgets into the table.  A table entry consists of the
 *	window path name, table index, and optional configuration options.
 *	The first argument in the argv list is the name of the table.  If
 *	no table exists for the given window, a new one is created.
 *
 * Results:
 *	Returns a standard Tcl result.  If an error occurred, TCL_ERROR is
 *	returned and an error message is in the result.
 *
 * Side Effects:
 *	Memory is allocated, a new master table is possibly created, etc.
 *	The table is re-computed and arranged at the next idle point.
 *
 * ----------------------------------------------------------------------------
 */
static int
RepOp(dataPtr, interp, argc, argv)
    TableInterpData *dataPtr;	/* Interpreter-specific data. */
    Tcl_Interp *interp;		/* Interpreter to return list of names to */
    int argc;			/* Number of arguments */
    char **argv;
{
    Tk_Window tkwin;
    Table *tablePtr;
    Ted *tedPtr;

    /* ted rep master index */
    tkwin = Tk_NameToWindow(interp, argv[3], Tk_MainWindow(interp));
    if (tkwin == NULL) {
	return TCL_ERROR;
    }
    if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
	return TCL_ERROR;
    }
    if (tablePtr->editPtr != NULL) {	/* Already editing this table */
	tedPtr = (Ted *) tablePtr->editPtr;
    } else {
	tedPtr = CreateTed(tablePtr, interp);
	if (tedPtr == NULL) {
	    return TCL_ERROR;
	}
    }
    if (ConfigureTed(tedPtr, argc - 3, argv + 3, 0) != TCL_OK) {
	tedPtr->tkwin = NULL;
	if (tedPtr->flags & REDRAW_PENDING) {
	    Tcl_CancelIdleCall(DisplayTed, tedPtr);
	}
	Tcl_EventuallyFree(tedPtr, DestroyTed);
	return TCL_ERROR;
    }
    /* Rearrange the table */
    if (!(tablePtr->flags & ARRANGE_PENDING)) {
	tablePtr->flags |= ARRANGE_PENDING;
	Tcl_DoWhenIdle(tablePtr->arrangeProc, tablePtr);
    }
    Tcl_SetResult(interp, Tk_PathName(tedPtr->tkwin), TCL_VOLATILE);
    tedPtr->flags |= LAYOUT_PENDING;
    EventuallyRedraw(tedPtr);
    return TCL_OK;
}

/*
 * ----------------------------------------------------------------------------
 *
 * Command options for the table editor.
 *
 * The fields for Blt_OperSpec are as follows:
 *
 *   - option name
 *   - minimum number of characters required to disambiguate the option name.
 *   - function associated with command option.
 *   - minimum number of arguments required.
 *   - maximum number of arguments allowed (0 indicates no limit).
 *   - usage string
 *
 * ----------------------------------------------------------------------------
 */
static Blt_OpSpec opSpecs[] =
{
    {"cget", 2, (Blt_Op)CgetOp, 4, 4, "master option",},
    {"configure", 2, (Blt_Op)ConfigureOp, 3, 0,
	"master ?option...?",},
    {"edit", 1, (Blt_Op)EditOp, 3, 0, "master ?options...?",},
    {"rep", 1, (Blt_Op)RepOp, 2, 0, "master index ?options...?",},
    {"select", 1, (Blt_Op)SelectOp, 4, 0, "master @x,y",},
 /* {"forget", 1, (Blt_Op)ForgetOp, 3, 0, "master ?master...?",},
    {"index", 1, (Blt_Op)IndexOp, 3, 0, "master ?item...?",}, */
};
static int nSpecs = sizeof(opSpecs) / sizeof(Blt_OpSpec);

/*
 * ----------------------------------------------------------------------------
 *
 * TedCmd --
 *
 *	This procedure is invoked to process the Tcl command that
 *	corresponds to the table geometry manager.  See the user
 *	documentation for details on what it does.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the user documentation.
 *
 * ----------------------------------------------------------------------------
 */
static int
TedCmd(clientData, interp, argc, argv)
    ClientData clientData;	/* Thread-specific data. */
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    Blt_Op proc;
    int result;

    proc = Blt_GetOp(interp, nSpecs, opSpecs, BLT_OP_ARG1, argc, argv, 0);
    if (proc == NULL) {
	return TCL_ERROR;
    }
    result = (*proc) (clientData, interp, argc, argv);
    return result;
}

static TableData *
GetTableInterpData(interp)
     Tcl_Interp *interp;
{
    TableData *dataPtr;
    Tcl_InterpDeleteProc *proc;

    dataPtr = (TableData *)Tcl_GetAssocData(interp, TABLE_THREAD_KEY, &proc);
    assert(dataPtr);
    return dataPtr;
}

/*
 * ----------------------------------------------------------------------------
 *
 * Blt_TedInit --
 *
 *	This procedure is invoked to initialize the Tcl command that
 *	corresponds to the table geometry manager.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Creates the new command and adds an entry into a global Tcl
 *	associative array.
 *
 * ---------------------------------------------------------------------------
 */
int
Blt_TedInit(interp)
    Tcl_Interp *interp;
{
    static Blt_CmdSpec cmdSpec = {"ted", TedCmd, };
    TableData *dataPtr;

    dataPtr = GetTableInterpData(interp);
    cmdSpec.clientData = dataPtr;
    if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
	return TCL_ERROR;
    }
    return TCL_OK;
}
