
/*	$Id: tixGrid.c,v 1.1.1.1 2000/05/17 11:08:42 idiscovery Exp $	*/

/*
 * tixGrid.c --
 *
 *	This module implements "tixGrid" 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 "tixDef.h"
#include "tixGrid.h"

/*
 * Information used for argv parsing.
 */
static Tk_ConfigSpec configSpecs[] = {
    {TK_CONFIG_COLOR, "-background", "background", "Background",
       DEF_GRID_BG_COLOR, Tk_Offset(WidgetRecord, normalBg),
       TK_CONFIG_COLOR_ONLY},

    {TK_CONFIG_COLOR, "-background", "background", "Background",
       DEF_GRID_BG_MONO, Tk_Offset(WidgetRecord, normalBg),
       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_GRID_BORDER_WIDTH, Tk_Offset(WidgetRecord, borderWidth), 0},

    {TK_CONFIG_CALLBACK, "-browsecmd", "browseCmd", "BrowseCmd",
       "", Tk_Offset(WidgetRecord, browseCmd),
       TK_CONFIG_NULL_OK},

    {TK_CONFIG_CALLBACK, "-command", "command", "Command",
       "", Tk_Offset(WidgetRecord, command),
       TK_CONFIG_NULL_OK},

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

    {TK_CONFIG_CALLBACK, "-editdonecmd", "editDoneCmd", "EditDoneCmd",
       DEF_GRID_EDITDONE_COMMAND, Tk_Offset(WidgetRecord, editDoneCmd),
       TK_CONFIG_NULL_OK},

    {TK_CONFIG_CALLBACK, "-editnotifycmd", "editNotifyCmd", "EditNotifyCmd",
       DEF_GRID_EDITNOTIFY_COMMAND, Tk_Offset(WidgetRecord, editNotifyCmd),
       TK_CONFIG_NULL_OK},

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

    {TK_CONFIG_BOOLEAN, "-floatingcols", "floatingCols", "FloatingCols",
	DEF_GRID_FLOATING_COLS, Tk_Offset(WidgetRecord, floatRange[1]), 0},

    {TK_CONFIG_BOOLEAN, "-floatingrows", "floatingRows", "FloatingRows",
	DEF_GRID_FLOATING_ROWS, Tk_Offset(WidgetRecord, floatRange[0]), 0},

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

    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
       DEF_GRID_FG_COLOR, Tk_Offset(WidgetRecord, normalFg),
       TK_CONFIG_COLOR_ONLY},

    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
       DEF_GRID_FG_MONO, Tk_Offset(WidgetRecord, normalFg),
       TK_CONFIG_MONO_ONLY},

    {TK_CONFIG_CALLBACK, "-formatcmd", "formatCmd", "FormatCmd",
       DEF_GRID_FORMAT_COMMAND, Tk_Offset(WidgetRecord, formatCmd),
       TK_CONFIG_NULL_OK},

    {TK_CONFIG_PIXELS, "-height", "height", "Height",
       DEF_GRID_HEIGHT, Tk_Offset(WidgetRecord, reqSize[1]), 0},

    {TK_CONFIG_BORDER, "-highlightbackground", "highlightBackground",
       "HighlightBackground",
       DEF_GRID_BG_COLOR, Tk_Offset(WidgetRecord, border),
       TK_CONFIG_COLOR_ONLY},

    {TK_CONFIG_BORDER, "-highlightbackground", "highlightBackground",
       "HighlightBackground",
       DEF_GRID_BG_MONO, Tk_Offset(WidgetRecord, border),
       TK_CONFIG_MONO_ONLY},

    {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
       DEF_GRID_HIGHLIGHT_COLOR, Tk_Offset(WidgetRecord, highlightColorPtr),
       TK_CONFIG_COLOR_ONLY},

    {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
       DEF_GRID_HIGHLIGHT_MONO, Tk_Offset(WidgetRecord, highlightColorPtr),
       TK_CONFIG_MONO_ONLY},

    {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
       "HighlightThickness",
       DEF_GRID_HIGHLIGHT_WIDTH, Tk_Offset(WidgetRecord, highlightWidth), 0},

    {TK_CONFIG_INT, "-leftmargin", "leftMargin", "LeftMargin",
       DEF_GRID_LEFT_MARGIN, Tk_Offset(WidgetRecord, hdrSize[0]), 0},

    {TK_CONFIG_CUSTOM, "-itemtype", "itemType", "ItemType",
       DEF_GRID_ITEM_TYPE, Tk_Offset(WidgetRecord, diTypePtr),
       0, &tixConfigItemType},

    {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
	DEF_GRID_PADX, Tk_Offset(WidgetRecord, padX), 0},

    {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
	DEF_GRID_PADY, Tk_Offset(WidgetRecord, padY), 0},

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

    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
	DEF_GRID_SELECT_BG_COLOR, Tk_Offset(WidgetRecord, selectBorder),
	TK_CONFIG_COLOR_ONLY},

    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
	DEF_GRID_SELECT_BG_MONO, Tk_Offset(WidgetRecord, selectBorder),
	TK_CONFIG_MONO_ONLY},

    {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth","BorderWidth",
       DEF_GRID_SELECT_BORDERWIDTH,Tk_Offset(WidgetRecord, selBorderWidth),0},

    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
       DEF_GRID_SELECT_FG_COLOR, Tk_Offset(WidgetRecord, selectFg),
       TK_CONFIG_COLOR_ONLY},

    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
       DEF_GRID_SELECT_FG_MONO, Tk_Offset(WidgetRecord, selectFg),
       TK_CONFIG_MONO_ONLY},

    {TK_CONFIG_UID, "-selectmode", "selectMode", "SelectMode",
	DEF_GRID_SELECT_MODE, Tk_Offset(WidgetRecord, selectMode), 0},

    {TK_CONFIG_UID, "-selectunit", "selectUnit", "SelectUnit",
	DEF_GRID_SELECT_UNIT, Tk_Offset(WidgetRecord, selectUnit), 0},

    {TK_CONFIG_CALLBACK, "-sizecmd", "sizeCmd", "SizeCmd",
       DEF_GRID_SIZE_COMMAND, Tk_Offset(WidgetRecord, sizeCmd),
       TK_CONFIG_NULL_OK},

    {TK_CONFIG_UID, "-state", (char*)NULL, (char*)NULL,
       DEF_GRID_STATE, Tk_Offset(WidgetRecord, state), 0},

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

    {TK_CONFIG_INT, "-topmargin", "topMargin", "TopMargin",
       DEF_GRID_TOP_MARGIN, Tk_Offset(WidgetRecord, hdrSize[1]), 0},

    {TK_CONFIG_PIXELS, "-width", "width", "Width",
	DEF_GRID_WIDTH, Tk_Offset(WidgetRecord, reqSize[0]), 0},

    {TK_CONFIG_CALLBACK, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
       DEF_GRID_X_SCROLL_COMMAND,
       Tk_Offset(WidgetRecord, scrollInfo[0].command),
       TK_CONFIG_NULL_OK},

    {TK_CONFIG_CALLBACK, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
       DEF_GRID_Y_SCROLL_COMMAND,
       Tk_Offset(WidgetRecord, scrollInfo[1].command),
       TK_CONFIG_NULL_OK},

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

static Tk_ConfigSpec entryConfigSpecs[] = {
    {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 void		WidgetDestroy _ANSI_ARGS_((char *clientData));
static void		WidgetEventProc _ANSI_ARGS_((ClientData clientData,
			    XEvent *eventPtr));
static int		WidgetCommand _ANSI_ARGS_((ClientData clientData,
			    Tcl_Interp *, int argc, char **argv));
static void		WidgetDisplay _ANSI_ARGS_((ClientData clientData));
static void		WidgetComputeGeometry _ANSI_ARGS_((
			    ClientData clientData));
static void		IdleHandler _ANSI_ARGS_((
			    ClientData clientData));
	/* Extra procedures for this widget
	 */
static int		ConfigElement _ANSI_ARGS_((WidgetPtr wPtr,
			    TixGrEntry *chPtr, int argc, char ** argv,
			    int flags, int forced));
static void		Tix_GrDisplayMainBody _ANSI_ARGS_((
			    WidgetPtr wPtr, Drawable buffer,
			    int winW, int winH));
static void		Tix_GrDrawBackground _ANSI_ARGS_((WidgetPtr wPtr,
			    RenderInfo * riPtr,Drawable drawable));
static void		Tix_GrDrawCells _ANSI_ARGS_((WidgetPtr wPtr,
			    RenderInfo * riPtr,Drawable drawable));
static void		Tix_GrDrawSites _ANSI_ARGS_((WidgetPtr wPtr,
			    RenderInfo * riPtr,Drawable drawable));
int			Tix_GrGetElementPosn _ANSI_ARGS_((
    			    WidgetPtr wPtr, int x, int y,
			    int rect[2][2], int clipOK, int isSite,
			    int isScr, int nearest));
static void		UpdateScrollBars _ANSI_ARGS_((WidgetPtr wPtr,
			    int sizeChanged));
static void		GetScrollFractions _ANSI_ARGS_((
			    WidgetPtr wPtr, Tix_GridScrollInfo *siPtr,
			    double * first_ret, double * last_ret));
static void		Tix_GrDItemSizeChanged _ANSI_ARGS_((
			    Tix_DItem *iPtr));
static TixGrEntry *	Tix_GrFindCreateElem _ANSI_ARGS_((Tcl_Interp * interp,
			    WidgetPtr wPtr, int x, int y));
static TixGrEntry *	Tix_GrFindElem _ANSI_ARGS_((Tcl_Interp * interp,
			    WidgetPtr wPtr, int x, int y));
static void		Tix_GrPropagateSize _ANSI_ARGS_((
			    WidgetPtr wPtr, TixGrEntry * chPtr));
static RenderBlock *	Tix_GrAllocateRenderBlock _ANSI_ARGS_((
			    WidgetPtr wPtr, int winW, int winH,
			    int *exactW, int *exactH));
static void		Tix_GrFreeRenderBlock _ANSI_ARGS_((
			    WidgetPtr wPtr, RenderBlock * rbPtr));
static void		Tix_GrComputeSelection _ANSI_ARGS_((
			    WidgetPtr wPtr));
static int		Tix_GrBBox _ANSI_ARGS_((Tcl_Interp * interp,
			    WidgetPtr wPtr, int x, int y));
static int		TranslateFromTo _ANSI_ARGS_((Tcl_Interp * interp,
			    WidgetPtr wPtr, int argc, char **argv, int *from,
			    int * to, int *which));
static void		Tix_GrComputeSubSelection _ANSI_ARGS_((
			    WidgetPtr wPtr, int rect[2][2], int offs[2]));
static int		Tix_GrCallFormatCmd _ANSI_ARGS_((WidgetPtr wPtr,
			    int which));
static void		RecalScrollRegion _ANSI_ARGS_((WidgetPtr wPtr,
			    int winW, int winH,
			    Tix_GridScrollInfo *scrollInfo));
static void		Tix_GrResetRenderBlocks _ANSI_ARGS_((WidgetPtr wPtr));

static TIX_DECLARE_SUBCMD(Tix_GrBdType);
static TIX_DECLARE_SUBCMD(Tix_GrCGet);
static TIX_DECLARE_SUBCMD(Tix_GrConfig);
static TIX_DECLARE_SUBCMD(Tix_GrDelete);
static TIX_DECLARE_SUBCMD(Tix_GrEdit);
static TIX_DECLARE_SUBCMD(Tix_GrEntryCget);
static TIX_DECLARE_SUBCMD(Tix_GrEntryConfig);
EXTERN TIX_DECLARE_SUBCMD(Tix_GrFormat);
static TIX_DECLARE_SUBCMD(Tix_GrGeometryInfo);
static TIX_DECLARE_SUBCMD(Tix_GrInfo);
static TIX_DECLARE_SUBCMD(Tix_GrIndex);
static TIX_DECLARE_SUBCMD(Tix_GrMove);
static TIX_DECLARE_SUBCMD(Tix_GrSee);
static TIX_DECLARE_SUBCMD(Tix_GrNearest);
EXTERN TIX_DECLARE_SUBCMD(Tix_GrSelection);
static TIX_DECLARE_SUBCMD(Tix_GrSet);
EXTERN TIX_DECLARE_SUBCMD(Tix_GrSetSize);
static TIX_DECLARE_SUBCMD(Tix_GrSetSite);
EXTERN TIX_DECLARE_SUBCMD(Tix_GrSort);
static TIX_DECLARE_SUBCMD(Tix_GrView);
static TIX_DECLARE_SUBCMD(Tix_GrUnset);


/*
 *--------------------------------------------------------------
 *
 * Tix_GridCmd --
 *
 *	This procedure is invoked to process the "tixGrid" Tcl
 *	command.  It creates a new "TixGrid" widget.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	A new widget is created and configured.
 *
 *--------------------------------------------------------------
 */
int
Tix_GridCmd(clientData, interp, argc, argv)
    ClientData clientData;
    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, "TixGrid");

    /*
     * Allocate and initialize the widget record.
     */
    wPtr = (WidgetPtr) ckalloc(sizeof(WidgetRecord));

    wPtr->dispData.tkwin 	= tkwin;
    wPtr->dispData.display 	= Tk_Display(tkwin);
    wPtr->dispData.interp 	= interp;
    wPtr->dispData.sizeChangedProc = Tix_GrDItemSizeChanged;
    wPtr->font		= NULL;
    wPtr->normalBg 		= NULL;
    wPtr->normalFg		= NULL;
    wPtr->command 		= NULL;
    wPtr->border 		= NULL;
    wPtr->borderWidth 		= 0;
    wPtr->selectBorder 		= NULL;
    wPtr->selBorderWidth 	= 0;
    wPtr->selectFg		= NULL;
    wPtr->backgroundGC		= None;
    wPtr->selectGC		= None;
    wPtr->anchorGC		= None;
    wPtr->highlightWidth	= 0;
    wPtr->highlightColorPtr	= NULL;
    wPtr->highlightGC		= None;
    wPtr->relief 		= TK_RELIEF_FLAT;
    wPtr->cursor 		= None;
    wPtr->selectMode		= NULL;
    wPtr->selectUnit		= NULL;
    wPtr->anchor[0] 		= TIX_SITE_NONE;
    wPtr->anchor[1] 		= TIX_SITE_NONE;
    wPtr->dragSite[0] 		= TIX_SITE_NONE;
    wPtr->dragSite[1] 		= TIX_SITE_NONE;
    wPtr->dropSite[0] 		= TIX_SITE_NONE;
    wPtr->dropSite[1] 		= TIX_SITE_NONE;
    wPtr->browseCmd		= 0;
    wPtr->formatCmd		= 0;
    wPtr->editDoneCmd		= 0;
    wPtr->editNotifyCmd		= 0;
    wPtr->sizeCmd		= 0;
    wPtr->takeFocus		= NULL;
    wPtr->serial		= 0;
    wPtr->mainRB		= (RenderBlock*)NULL;
    wPtr->hdrSize[0]		= 1;
    wPtr->hdrSize[1]		= 1;
    wPtr->expArea.x1 		= 10000;
    wPtr->expArea.y1 		= 10000;
    wPtr->expArea.x2 		= 0;
    wPtr->expArea.y2 		= 0;
    wPtr->dataSet		= TixGridDataSetInit();
    wPtr->renderInfo		= NULL;
    wPtr->defSize[0].sizeType	= TIX_GR_DEFINED_CHAR;
    wPtr->defSize[0].charValue	= 10.0;
    wPtr->defSize[0].pad0	= 2;
    wPtr->defSize[0].pad1	= 2;
    wPtr->defSize[1].sizeType	= TIX_GR_DEFINED_CHAR;
    wPtr->defSize[1].charValue	= 1.2;
    wPtr->defSize[1].pad0	= 2;
    wPtr->defSize[1].pad1	= 2;
    wPtr->gridSize[0]		= 0;
    wPtr->gridSize[1]		= 0;
    wPtr->reqSize[0]		= 0;
    wPtr->reqSize[1]		= 0;
    wPtr->state			= tixNormalUid;
    wPtr->colorInfoCounter	= 0;

    /* The flags */
    wPtr->idleEvent 		= 0;
    wPtr->toRedraw 		= 0;
    wPtr->toResize 		= 0;
    wPtr->toResetRB 		= 0;
    wPtr->toComputeSel 		= 0;
    wPtr->toRedrawHighlight 	= 0;

    wPtr->scrollInfo[0].command  = NULL;
    wPtr->scrollInfo[1].command  = NULL;

    wPtr->scrollInfo[0].max  = 1;
    wPtr->scrollInfo[0].unit   = 1;
    wPtr->scrollInfo[0].offset = 0;
    wPtr->scrollInfo[0].window = 1.0;
    wPtr->scrollInfo[1].max  = 1;
    wPtr->scrollInfo[1].unit   = 1;
    wPtr->scrollInfo[1].offset = 0;
    wPtr->scrollInfo[1].window = 1.0;

    Tix_SimpleListInit(&wPtr->colorInfo);
    Tix_SimpleListInit(&wPtr->selList);
    Tix_SimpleListInit(&wPtr->mappedWindows);

    Tk_CreateEventHandler(wPtr->dispData.tkwin,
	ExposureMask|StructureNotifyMask|FocusChangeMask,
	WidgetEventProc, (ClientData) wPtr);
    wPtr->widgetCmd = Tcl_CreateCommand(interp,
	Tk_PathName(wPtr->dispData.tkwin), WidgetCommand, (ClientData) wPtr,
	WidgetCmdDeletedProc);

    if (WidgetConfigure(interp, wPtr, argc-2, argv+2, 0) != TCL_OK) {
	Tk_DestroyWindow(wPtr->dispData.tkwin);
	return TCL_ERROR;
    }

    interp->result = Tk_PathName(wPtr->dispData.tkwin);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * WidgetConfigure --
 *
 *	This procedure is called to process an argv/argc list in
 *	conjunction with the Tk option database to configure (or
 *	reconfigure) a List 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;
    TixFont oldfont;
    Tix_StyleTemplate stTmpl;

    oldfont = wPtr->font;

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

    wPtr->bdPad = wPtr->highlightWidth + wPtr->borderWidth;

    if ((wPtr->state != tixNormalUid) && (wPtr->state != tixDisabledUid)) {
	Tcl_AppendResult(interp, "bad state value \"", wPtr->state,
	    "\":  must be normal or disabled", (char *) NULL);
	wPtr->state = tixNormalUid;
	return TCL_ERROR;
    }

    if (oldfont != wPtr->font) {
	int i;

	/*
	 * Font has been changed (initialized), we need to reset the render
	 * blocks
	 */
	wPtr->toResetRB = 1;

	TixComputeTextGeometry(wPtr->font, "0", 1,
	    0, &wPtr->fontSize[0], &wPtr->fontSize[1]);

	/* Recalculate the default size of the cells
	 */
	for (i=0; i<2; i++) {
	    switch (wPtr->defSize[i].sizeType) {
	      case TIX_GR_DEFINED_CHAR:
		wPtr->defSize[i].pixels = (int)
		    (wPtr->defSize[i].charValue * wPtr->fontSize[i]);
		break;
	      case TIX_GR_AUTO:
		if (i==0) {
		    wPtr->defSize[i].pixels = 10 * wPtr->fontSize[0];
		}
		if (i==1) {
		    wPtr->defSize[i].pixels =  1 * wPtr->fontSize[1];
		}
		break;
	    }
	}
    }

    /*
     * A few options need special processing, such as setting the
     * background from a 3-D border, or filling in complicated
     * defaults that couldn't be specified to Tk_ConfigureWidget.
     */

    Tk_SetBackgroundFromBorder(wPtr->dispData.tkwin, wPtr->border);

    /*
     * Note: GraphicsExpose events are disabled in normalGC because it's
     * used to copy stuff from an off-screen pixmap onto the screen (we know
     * that there's no problem with obscured areas).
     */

    /* The background GC */
    gcValues.foreground 	= wPtr->normalBg->pixel;
    gcValues.graphics_exposures = False;

    newGC = Tk_GetGC(wPtr->dispData.tkwin,
	GCForeground|GCGraphicsExposures, &gcValues);
    if (wPtr->backgroundGC != None) {
	Tk_FreeGC(wPtr->dispData.display, wPtr->backgroundGC);
    }
    wPtr->backgroundGC = newGC;

    /* The selected text GC */
    gcValues.font 		= TixFontId(wPtr->font);
    gcValues.foreground 	= wPtr->selectFg->pixel;
    gcValues.background 	= Tk_3DBorderColor(wPtr->selectBorder)->pixel;
    gcValues.graphics_exposures = False;

    newGC = Tk_GetGC(wPtr->dispData.tkwin,
	GCForeground|GCBackground|GCFont|GCGraphicsExposures, &gcValues);
    if (wPtr->selectGC != None) {
	Tk_FreeGC(wPtr->dispData.display, wPtr->selectGC);
    }
    wPtr->selectGC = newGC;

    /* The dotted anchor lines */
    gcValues.foreground 	= wPtr->normalFg->pixel;
    gcValues.background 	= wPtr->normalBg->pixel;
    gcValues.graphics_exposures = False;
    gcValues.line_style         = LineDoubleDash;
    gcValues.dashes 		= 2;
    gcValues.subwindow_mode	= IncludeInferiors;

    newGC = Tk_GetGC(wPtr->dispData.tkwin,
	GCForeground|GCBackground|GCGraphicsExposures|GCLineStyle|GCDashList|
	GCSubwindowMode, &gcValues);
    if (wPtr->anchorGC != None) {
	Tk_FreeGC(wPtr->dispData.display, wPtr->anchorGC);
    }
    wPtr->anchorGC = newGC;

    /* The highlight border */
    gcValues.background 	= wPtr->selectFg->pixel;
    gcValues.foreground 	= wPtr->highlightColorPtr->pixel;
    gcValues.graphics_exposures = False;

    newGC = Tk_GetGC(wPtr->dispData.tkwin,
	GCForeground|GCBackground|GCGraphicsExposures, &gcValues);
    if (wPtr->highlightGC != None) {
	Tk_FreeGC(wPtr->dispData.display, wPtr->highlightGC);
    }
    wPtr->highlightGC = newGC;

    /* We must set the options of the default styles so that
     * -- the default styles will change according to what is in
     *    stTmpl
     */
    stTmpl.font 			= wPtr->font;
    stTmpl.pad[0]  			= wPtr->padX;
    stTmpl.pad[1]  			= wPtr->padY;
    stTmpl.colors[TIX_DITEM_NORMAL].fg  = wPtr->normalFg;
    stTmpl.colors[TIX_DITEM_NORMAL].bg  = wPtr->normalBg;
    stTmpl.colors[TIX_DITEM_SELECTED].fg= wPtr->selectFg;
    stTmpl.colors[TIX_DITEM_SELECTED].bg= Tk_3DBorderColor(wPtr->selectBorder);
    stTmpl.flags = TIX_DITEM_FONT|TIX_DITEM_NORMAL_BG|
	TIX_DITEM_SELECTED_BG|TIX_DITEM_NORMAL_FG|TIX_DITEM_SELECTED_FG |
	TIX_DITEM_PADX|TIX_DITEM_PADY;

    Tix_SetDefaultStyleTemplate(wPtr->dispData.tkwin, &stTmpl);

    Tix_GrDoWhenIdle(wPtr, TIX_GR_RESIZE);

    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. */
{
    int code;

    static Tix_SubCmdInfo subCmdInfo[] = {
	{TIX_DEFAULT_LEN, "anchor", 1, 3, Tix_GrSetSite,
	   "option ?x y?"},
	{TIX_DEFAULT_LEN, "bdtype", 2, 4, Tix_GrBdType,
	   "x y ?xbdWidth ybdWidth?"},
	{TIX_DEFAULT_LEN, "cget", 1, 1, Tix_GrCGet,
	   "option"},
	{TIX_DEFAULT_LEN, "configure", 0, TIX_VAR_ARGS, Tix_GrConfig,
	   "?option? ?value? ?option value ... ?"},
	{TIX_DEFAULT_LEN, "delete", 2, 3, Tix_GrDelete,
	   "option from ?to?"},
	{TIX_DEFAULT_LEN, "dragsite", 1, 3, Tix_GrSetSite,
	   "option ?x y?"},
	{TIX_DEFAULT_LEN, "dropsite", 1, 3, Tix_GrSetSite,
	   "option ?x y?"},
	{TIX_DEFAULT_LEN, "entrycget", 3, 3, Tix_GrEntryCget,
	   "x y option"},
	{TIX_DEFAULT_LEN, "edit", 1, 3, Tix_GrEdit,
	   "option ?args ...?"},
	{TIX_DEFAULT_LEN, "entryconfigure", 2, TIX_VAR_ARGS, Tix_GrEntryConfig,
	   "x y ?option? ?value? ?option value ... ?"},
	{TIX_DEFAULT_LEN, "format", 1, TIX_VAR_ARGS, Tix_GrFormat,
	   "option ?args ...?"},
	{TIX_DEFAULT_LEN, "geometryinfo", 0, 2, Tix_GrGeometryInfo,
	   "?width height?"},
	{TIX_DEFAULT_LEN, "info", 1, TIX_VAR_ARGS, Tix_GrInfo,
	   "option ?args ...?"},
	{TIX_DEFAULT_LEN, "index", 2, 2, Tix_GrIndex,
	   "x y"},
	{TIX_DEFAULT_LEN, "move", 4, 4, Tix_GrMove,
	   "option from to by"},
	{TIX_DEFAULT_LEN, "nearest", 2, 2, Tix_GrNearest,
	   "x y"},
	{TIX_DEFAULT_LEN, "see", 2, 2, Tix_GrSee,
	   "x y"},
	{TIX_DEFAULT_LEN, "selection", 3, 5, Tix_GrSelection,
	   "option x1 y1 ?x2 y2?"},
	{TIX_DEFAULT_LEN, "set", 2, TIX_VAR_ARGS, Tix_GrSet,
	   "x y ?option value ...?"},
	{TIX_DEFAULT_LEN, "size", 1, TIX_VAR_ARGS, Tix_GrSetSize,
	   "option ?args ...?"},
#ifndef _WINDOWS
	{TIX_DEFAULT_LEN, "sort", 3, TIX_VAR_ARGS, Tix_GrSort,
	   "dimension start end ?args ...?"},
#endif
	{TIX_DEFAULT_LEN, "unset", 2, 2, Tix_GrUnset,
	   "x y"},
	{TIX_DEFAULT_LEN, "xview", 0, 3, Tix_GrView,
	   "args"},
	{TIX_DEFAULT_LEN, "yview", 0, 3, Tix_GrView,
	   "args"},
    };

    static Tix_CmdInfo cmdInfo = {
	Tix_ArraySize(subCmdInfo), 1, TIX_VAR_ARGS, "?option? arg ?arg ...?",
    };

    Tcl_Preserve(clientData);
    code = Tix_HandleSubCmds(&cmdInfo, subCmdInfo, clientData,
	interp, argc, argv);
    Tcl_Release(clientData);

    return code;
}

/*
 *--------------------------------------------------------------
 *
 * WidgetEventProc --
 *
 *	This procedure is invoked by the Tk dispatcher for various
 *	events on Lists.
 *
 * 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;
    int x2, y2;

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

      case ConfigureNotify:
	wPtr->expArea.x1 = 0;
	wPtr->expArea.y1 = 0;
	wPtr->expArea.x2 = Tk_Width (wPtr->dispData.tkwin) - 1;
	wPtr->expArea.y2 = Tk_Height(wPtr->dispData.tkwin) - 1;
	Tix_GrDoWhenIdle(wPtr, TIX_GR_RESIZE);
	break;

      case Expose:
	if (wPtr->expArea.x1 > eventPtr->xexpose.x) {
	    wPtr->expArea.x1 = eventPtr->xexpose.x;
	}
	if (wPtr->expArea.y1 > eventPtr->xexpose.y) {
	    wPtr->expArea.y1 = eventPtr->xexpose.y;
	}
	x2 = eventPtr->xexpose.x + eventPtr->xexpose.width  - 1;
	y2 = eventPtr->xexpose.y + eventPtr->xexpose.height - 1;

	if (wPtr->expArea.x2 < x2) {
	    wPtr->expArea.x2 = x2;
	}
	if (wPtr->expArea.y2 < y2) {
	    wPtr->expArea.y2 = y2;
	}
	wPtr->toRedrawHighlight = 1;
	Tix_GrDoWhenIdle(wPtr, TIX_GR_REDRAW);
	break;

      case FocusIn:
	wPtr->hasFocus = 1;
	wPtr->toRedrawHighlight = 1;
	Tix_GrDoWhenIdle(wPtr, TIX_GR_REDRAW);
	break;

      case FocusOut:
	wPtr->hasFocus = 0;
	wPtr->toRedrawHighlight = 1;
	Tix_GrDoWhenIdle(wPtr, TIX_GR_REDRAW);
	break;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * WidgetDestroy --
 *
 *	This procedure is invoked by Tk_EventuallyFree or Tk_Release
 *	to clean up the internal structure of a List at a safe time
 *	(when no-one is using it anymore).
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Everything associated with the List is freed up.
 *
 *----------------------------------------------------------------------
 */

static void
WidgetDestroy(clientData)
    char *clientData;	/* Info about the Grid widget. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;

    if (wPtr->dataSet) {
	Tix_GrDataRowSearch rowSearch;
	Tix_GrDataCellSearch cellSearch;
	int rowDone, cellDone;

	for (rowDone = TixGrDataFirstRow(wPtr->dataSet, &rowSearch);
	        !rowDone;
	        rowDone = TixGrDataNextRow(&rowSearch)) {


	    for (cellDone = TixGrDataFirstCell(&rowSearch, &cellSearch);
		    !cellDone;
		    cellDone = TixGrDataNextCell(&cellSearch)) {

		TixGridDataDeleteSearchedEntry(&cellSearch);
		Tix_GrFreeElem((TixGrEntry*)cellSearch.data);
	    }
	}

	TixGridDataSetFree(wPtr->dataSet);
    }

    if (wPtr->backgroundGC != None) {
	Tk_FreeGC(wPtr->dispData.display, wPtr->backgroundGC);
    }
    if (wPtr->selectGC != None) {
	Tk_FreeGC(wPtr->dispData.display, wPtr->selectGC);
    }
    if (wPtr->anchorGC != None) {
	Tk_FreeGC(wPtr->dispData.display, wPtr->anchorGC);
    }
    if (wPtr->highlightGC != None) {
	Tk_FreeGC(wPtr->dispData.display, wPtr->highlightGC);
    }

    if (wPtr->mainRB) {
	Tix_GrFreeRenderBlock(wPtr, wPtr->mainRB);
    }

    Tix_GrFreeUnusedColors(wPtr, 1);

    if (!Tix_IsLinkListEmpty(wPtr->mappedWindows)) {
	/*
	 * All mapped windows should have been unmapped when the
	 * the entries were deleted
	 */
	panic("tixGrid: mappedWindows not NULL");
    }

    Tk_FreeOptions(configSpecs, (char *) wPtr, wPtr->dispData.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;	/* Info about Grid 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->dispData.tkwin != NULL) {
	Tk_Window tkwin = wPtr->dispData.tkwin;
	wPtr->dispData.tkwin = NULL;
	Tk_DestroyWindow(tkwin);
    }
}

static void
RecalScrollRegion(wPtr, winW, winH, scrollInfo)
    WidgetPtr wPtr;		/* Info about Grid widget. */
    int winW;
    int winH;
    Tix_GridScrollInfo *scrollInfo;
{
    int gridSize[2];
    int winSize[2];
    int i, k;
    int count;
    int visibleSize;
    int totalSize;
    int pad0, pad1;

    winSize[0] = winW;
    winSize[1] = winH;

    TixGridDataGetGridSize(wPtr->dataSet, &gridSize[0],
	&gridSize[1]);

    for (i=0; i<2; i++) {
	for (k=0; k<wPtr->hdrSize[i] && k<gridSize[i]; k++) {
	    winSize[i] -= TixGridDataGetRowColSize(wPtr, wPtr->dataSet, i,
		k, &wPtr->defSize[i], &pad0, &pad1);
	    winSize[i] -= pad0 + pad1;
	}
	if (winSize[i] <= 0) {
	    /*
	     * The window's contents are not visible.
	     */
	    scrollInfo[i].max    = 0;
	    scrollInfo[i].window = 1.0;
	    continue;
	}
	if (wPtr->hdrSize[i] >= gridSize[i]) {
	    /*
	     * There is no scrollable stuff in this dimension.
	     */
	    scrollInfo[i].max    = 0;
	    scrollInfo[i].window = 1.0;
	    continue;
	}

	visibleSize = winSize[i];

	for (count=0,k=gridSize[i]-1; k>=wPtr->hdrSize[i]&&k>=0; count++,k--) {
	    winSize[i] -= TixGridDataGetRowColSize(wPtr, wPtr->dataSet, i,
		k, &wPtr->defSize[i], &pad0, &pad1);
	    winSize[i] -= pad0 + pad1;

	    if (winSize[i] == 0) {
		++ count;
		break;
	    }
	    else if (winSize[i] < 0) {
		break;
	    }
	}

	if (count == 0) {
	    /*
	     * There is only one scrollable element and it is  *partially*
	     * visible.
	     */
	    count = 1;
	}
	scrollInfo[i].max  = (gridSize[i]-wPtr->hdrSize[i]) - count;

	/*
	 * calculate the total pixel size  (%%SLOOOOOOW)
	 */
	for (totalSize=0,k=wPtr->hdrSize[i];k<gridSize[i];k++) {
	    totalSize += TixGridDataGetRowColSize(wPtr, wPtr->dataSet, i,
	        k, &wPtr->defSize[i], &pad0, &pad1);
	    totalSize += pad0 + pad1;
	}

	/*
	 *we may need some left over spaces after the last element.
	 */
	totalSize += (-winSize[i]);

	scrollInfo[i].window =
	  (double)(visibleSize) / (double)totalSize;
    }
    for (i=0; i<2; i++) {
	if (scrollInfo[i].offset < 0) {
	    scrollInfo[i].offset  = 0;
	}
	if (scrollInfo[i].offset > scrollInfo[i].max) {
	    scrollInfo[i].offset = scrollInfo[i].max;
	}
    }
}


/*
 *--------------------------------------------------------------
 *
 * 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:
 *	none
 *
 *--------------------------------------------------------------
 */
static void
WidgetComputeGeometry(clientData)
    ClientData clientData;
{
    WidgetPtr wPtr = (WidgetPtr)clientData;
    int i, k;
    int gridSize[2];
    int req[2], pad0, pad1;
    Tk_Window tkwin = wPtr->dispData.tkwin;

    TixGridDataGetGridSize(wPtr->dataSet, &gridSize[0],
	&gridSize[1]);

    for (i=0; i<2; i++) {
	int end = wPtr->reqSize[i];
	if (end == 0) {
	    end = gridSize[0] + 1;
	}
	for (req[i]=0,k=0; k<end; k++) {
	    req[i] += TixGridDataGetRowColSize(wPtr, wPtr->dataSet, i,
		k, &wPtr->defSize[i], &pad0, &pad1);
	    req[i] += pad0 + pad1;
	}

	req[i] += 2*(wPtr->highlightWidth + wPtr->borderWidth);
    }

    if (Tk_ReqWidth(tkwin) != req[0] || Tk_ReqHeight(tkwin) != req[0]) {
	Tk_GeometryRequest(tkwin, req[0], req[1]);
    }

    /* arrange for the widget to be redrawn */
    wPtr->toResetRB      = 1;
    wPtr->toComputeSel   = 1;
    wPtr->toRedrawHighlight = 1;

    Tix_GrDoWhenIdle(wPtr, TIX_GR_REDRAW);
}

static void
Tix_GrResetRenderBlocks(wPtr)
    WidgetPtr wPtr;
{
    int winW, winH, exactW, exactH;
    Tk_Window tkwin = wPtr->dispData.tkwin;

    winW = Tk_Width (tkwin) - 2*wPtr->highlightWidth - 2*wPtr->borderWidth;
    winH = Tk_Height(tkwin) - 2*wPtr->highlightWidth - 2*wPtr->borderWidth;

    RecalScrollRegion(wPtr, winW, winH, wPtr->scrollInfo);

    UpdateScrollBars(wPtr, 1);

    if (wPtr->mainRB) {
	Tix_GrFreeRenderBlock(wPtr, wPtr->mainRB);
    }
    wPtr->mainRB = Tix_GrAllocateRenderBlock(wPtr, winW, winH,&exactW,&exactH);

    wPtr->expArea.x1 = 0;
    wPtr->expArea.y1 = 0;
    wPtr->expArea.x2 = Tk_Width (wPtr->dispData.tkwin) - 1;
    wPtr->expArea.y2 = Tk_Height(wPtr->dispData.tkwin) - 1;
}

/*----------------------------------------------------------------------
 * DItemSizeChanged --
 *
 *	This is called whenever the size of one of the HList's items
 *	changes its size.
 *----------------------------------------------------------------------
 */
static void
Tix_GrDItemSizeChanged(iPtr)
    Tix_DItem *iPtr;
{
    WidgetPtr wPtr = (WidgetPtr)iPtr->base.clientData;

    if (wPtr) {
	/* double-check: perhaps we haven't set the clientData yet! */
	Tix_GrDoWhenIdle(wPtr, TIX_GR_RESIZE);
    }
}

/*
 *----------------------------------------------------------------------
 * Tix_GrDoWhenIdle --
 *----------------------------------------------------------------------
 */
void
Tix_GrDoWhenIdle(wPtr, type)
    WidgetPtr wPtr;
    int type;
{
    switch (type) {
      case TIX_GR_RESIZE:
	wPtr->toResize = 1;
	break;
      case TIX_GR_REDRAW:
	wPtr->toRedraw = 1;
	break;
    }

    if (!wPtr->idleEvent) {
	wPtr->idleEvent = 1;
	Tcl_DoWhenIdle(IdleHandler, (ClientData)wPtr);
    }
}

static void
IdleHandler(clientData)
    ClientData clientData;	/* Info about my widget. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;

    if (!wPtr->idleEvent) {	/* sanity check */
	return;
    }
    wPtr->idleEvent = 0;

    if (wPtr->toResize) {
	wPtr->toResize = 0;
	WidgetComputeGeometry(clientData);
    }
    else if (wPtr->toRedraw) {
	wPtr->toRedraw = 0;
	WidgetDisplay(clientData);
    }
}

/*
 *----------------------------------------------------------------------
 * Tix_GrCancelDoWhenIdle --
 *----------------------------------------------------------------------
 */
void
Tix_GrCancelDoWhenIdle(wPtr)
    WidgetPtr wPtr;
{
    wPtr->toResize = 0;
    wPtr->toRedraw = 0;

    if (wPtr->idleEvent) {
	Tcl_CancelIdleCall(IdleHandler, (ClientData)wPtr);
	wPtr->idleEvent = 0;
    }
}

/*----------------------------------------------------------------------
 * WidgetDisplay --
 *
 *	Display the widget: the borders, the background and the entries.
 *
 *----------------------------------------------------------------------
 */
static void
WidgetDisplay(clientData)
    ClientData clientData;	/* Info about my widget. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    Drawable buffer = None;
    Tk_Window tkwin = wPtr->dispData.tkwin;
    int winH, winW, expW, expH;
    GC highlightGC;

    if (!Tk_IsMapped(tkwin)) {
	return;
    }
    wPtr->serial ++;

    winW = Tk_Width(tkwin)  - 2*wPtr->highlightWidth - 2*wPtr->borderWidth;
    winH = Tk_Height(tkwin) - 2*wPtr->highlightWidth - 2*wPtr->borderWidth;

    if (winW <= 0 || winH <= 0) {	/* nothing to draw */
	goto done;
    }

    if (wPtr->toResetRB) {
	Tix_GrResetRenderBlocks(wPtr);
	wPtr->toResetRB = 0;
    }
    if (wPtr->toComputeSel) {
	Tix_GrComputeSelection(wPtr);
	wPtr->toComputeSel = 0;
    }

    /* clip the exposed area to the visible part of the widget,
     * just in case some of the routines had made it larger than
     * it should be
     */
    if (wPtr->expArea.x1 < wPtr->bdPad) {
	wPtr->expArea.x1 = wPtr->bdPad;
    }
    if (wPtr->expArea.y1 < wPtr->bdPad) {
	wPtr->expArea.y1 = wPtr->bdPad;
    }
    if (wPtr->expArea.x2 >= Tk_Width(tkwin)  - wPtr->bdPad) {
	wPtr->expArea.x2  = Tk_Width(tkwin)  - wPtr->bdPad - 1;
    }
    if (wPtr->expArea.y2 >= Tk_Height(tkwin) - wPtr->bdPad) {
	wPtr->expArea.y2  = Tk_Height(tkwin) - wPtr->bdPad - 1;
    }

    expW = wPtr->expArea.x2 - wPtr->expArea.x1 + 1;
    expH = wPtr->expArea.y2 - wPtr->expArea.y1 + 1;

    if (expW <= 0 || expH <= 0) {	/* no cells to draw */
	goto drawBorder;
    }

    buffer = Tix_GetRenderBuffer(wPtr->dispData.display, Tk_WindowId(tkwin),
	expW, expH, Tk_Depth(tkwin));

    if (buffer == Tk_WindowId(tkwin)) {
	/* clear the window directly */
	XFillRectangle(wPtr->dispData.display, buffer, wPtr->backgroundGC,
	    wPtr->expArea.x1, wPtr->expArea.y1, expW, expH);
    } else {
	XFillRectangle(wPtr->dispData.display, buffer, wPtr->backgroundGC,
	    0, 0, expW, expH);
    }

    if (wPtr->mainRB) {
	Tix_GrDisplayMainBody(wPtr, buffer, winW, winH);
    }

    if (buffer != Tk_WindowId(tkwin)) {
	XCopyArea(wPtr->dispData.display, buffer, Tk_WindowId(tkwin),
	    wPtr->backgroundGC, 0, 0, expW, expH,
	    wPtr->expArea.x1, wPtr->expArea.y1);
	Tk_FreePixmap(wPtr->dispData.display, buffer);
    }

  drawBorder:
    Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin), wPtr->border,
	wPtr->highlightWidth,
	wPtr->highlightWidth,
	Tk_Width(tkwin)  - 2*wPtr->highlightWidth,
	Tk_Height(tkwin) - 2*wPtr->highlightWidth,
	wPtr->borderWidth, wPtr->relief);

    if (wPtr->toRedrawHighlight && wPtr->highlightWidth > 0) {
	if (wPtr->hasFocus) {
	    highlightGC = wPtr->highlightGC;
	} else {
	    highlightGC = Tk_3DBorderGC(tkwin, wPtr->border,
	        TK_3D_FLAT_GC);
	}

	Tk_DrawFocusHighlight(tkwin, highlightGC, wPtr->highlightWidth,
	    Tk_WindowId(tkwin));
    }

  done:
    wPtr->expArea.x1 = 10000;
    wPtr->expArea.y1 = 10000;
    wPtr->expArea.x2 = 0;
    wPtr->expArea.y2 = 0;
}

/*
 *----------------------------------------------------------------------
 *
 * Tix_GrDisplayMainBody  --
 *
 *	Draw the background and cells
 *
 * Results:
 *	None.
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */
static void Tix_GrDisplayMainBody(wPtr, buffer, winW, winH)
    WidgetPtr wPtr;
    Drawable buffer;
    int winW;
    int winH;
{
    Tk_Window tkwin = wPtr->dispData.tkwin;
    RenderInfo mainRI;			/* render info for main body */
    int i, j;

    if (buffer == Tk_WindowId(tkwin)) {
	/* rendering directly into the window */
	mainRI.origin[0] = wPtr->highlightWidth + wPtr->borderWidth;
	mainRI.origin[1] = wPtr->highlightWidth + wPtr->borderWidth;

    } else {
	/* rendering into a pixmap */
	mainRI.origin[0] = wPtr->highlightWidth + wPtr->borderWidth
	    - wPtr->expArea.x1;
	mainRI.origin[1] = wPtr->highlightWidth + wPtr->borderWidth
	    - wPtr->expArea.y1;
    }

    mainRI.drawable = buffer;
    wPtr->colorInfoCounter ++;

    wPtr->renderInfo = &mainRI;

    /* 1. Draw the backgrounds
     */
    for (i=0; i<wPtr->mainRB->size[0]; i++) {
	for (j=0; j<wPtr->mainRB->size[1]; j++) {
	    wPtr->mainRB->elms[i][j].borderW[0][0] = 0;
	    wPtr->mainRB->elms[i][j].borderW[1][0] = 0;
	    wPtr->mainRB->elms[i][j].borderW[0][1] = 0;
	    wPtr->mainRB->elms[i][j].borderW[1][1] = 0;
	    wPtr->mainRB->elms[i][j].filled        = 0;
	}
    }
    Tix_GrDrawBackground(wPtr, &mainRI, buffer);

    /* 2. Draw the cells
     */
    Tix_GrDrawCells(wPtr, &mainRI, buffer);

    /* 3. Draw the special sites (anchor, drag, drop).
     */
    Tix_GrDrawSites(wPtr, &mainRI, buffer);

    /* done */
    wPtr->renderInfo = NULL;

    /* Free the unwanted colors: they are left overs from the "format"
     * widget command.
     */
    Tix_GrFreeUnusedColors(wPtr, 0);
}

/*----------------------------------------------------------------------
 * Tix_GrDrawCells --
 *
 *	Redraws the cells of the Grid
 *----------------------------------------------------------------------
 */
static void Tix_GrDrawCells(wPtr, riPtr, drawable)
    WidgetPtr wPtr;
    RenderInfo * riPtr;
    Drawable drawable;
{
    int x, y, i, j;
    int x1, y1, x2, y2;
    TixGrEntry * chPtr;
    int margin = wPtr->borderWidth + wPtr->highlightWidth;

    for (x=0,i=0; i<wPtr->mainRB->size[0]; i++) {
	x1 = x  + margin;
	x2 = x1 - 1 + wPtr->mainRB->dispSize[0][i].total;

	if (x1 > wPtr->expArea.x2) {
	    goto nextCol;
	}
	if (x2 < wPtr->expArea.x1) {
	    goto nextCol;
	}
	/*
	 * iterate over the columns
	 */
	for (y=0,j=0; j<wPtr->mainRB->size[1]; j++) {
	    /*
	     * iterate over each item in the column, from top
	     * to bottom
	     */
	    y1 = y  + margin;
	    y2 = y1 - 1 + wPtr->mainRB->dispSize[1][j].total;

	    if (y1 > wPtr->expArea.y2) {
		goto nextRow;
	    }
	    if (y2 < wPtr->expArea.y1) {
		goto nextRow;
	    }
	    if (!wPtr->mainRB->elms[i][j].filled) {
		if (wPtr->mainRB->elms[i][j].selected) {

		    Tk_Fill3DRectangle(wPtr->dispData.tkwin,
		    	drawable, wPtr->selectBorder,
			x+riPtr->origin[0]+
			    wPtr->mainRB->elms[i][j].borderW[0][0],
			y+riPtr->origin[1]+
			    wPtr->mainRB->elms[i][j].borderW[1][0],
			wPtr->mainRB->dispSize[0][i].total -
			    wPtr->mainRB->elms[i][j].borderW[0][0] -
			    wPtr->mainRB->elms[i][j].borderW[0][1],
			wPtr->mainRB->dispSize[1][j].total -
			    wPtr->mainRB->elms[i][j].borderW[1][0] -
			    wPtr->mainRB->elms[i][j].borderW[1][1],
		    	0, TK_RELIEF_FLAT);
		}
	    }

	    chPtr = wPtr->mainRB->elms[i][j].chPtr;
	    if (chPtr != NULL) {
		if (Tix_DItemType(chPtr->iPtr) == TIX_DITEM_WINDOW) {
		    Tix_DItemDisplay(Tk_WindowId(wPtr->dispData.tkwin), None,
			chPtr->iPtr, x1, y1,
			wPtr->mainRB->dispSize[0][i].size,
	       		wPtr->mainRB->dispSize[1][j].size,
		        TIX_DITEM_NORMAL_FG);
		} else {
		    int drawX, drawY;
		    drawX = x + riPtr->origin[0] +
		        wPtr->mainRB->dispSize[0][i].preBorder;
		    drawY = y + riPtr->origin[1] +
		        wPtr->mainRB->dispSize[1][j].preBorder;

		    Tix_DItemDisplay(drawable, None, chPtr->iPtr,
		    	drawX, drawY,
			wPtr->mainRB->dispSize[0][i].size,
			wPtr->mainRB->dispSize[1][j].size,
			TIX_DITEM_NORMAL_FG);
		}
	    }
	  nextRow:
	    y+= wPtr->mainRB->dispSize[1][j].total;
	}
      nextCol:
	x+= wPtr->mainRB->dispSize[0][i].total;
    }

    for (i=0; i<wPtr->mainRB->size[0]; i++) {
	for (j=0; j<wPtr->mainRB->size[1]; j++) {
	    chPtr = wPtr->mainRB->elms[i][j].chPtr;
	    if (chPtr != NULL) {
		if (Tix_DItemType(chPtr->iPtr) == TIX_DITEM_WINDOW) {

		    Tix_SetWindowItemSerial(&wPtr->mappedWindows,
			chPtr->iPtr, wPtr->serial);
		}
	    }
	}
    }

    /* unmap those windows we mapped the last time */
    Tix_UnmapInvisibleWindowItems(&wPtr->mappedWindows, wPtr->serial);
}

/*----------------------------------------------------------------------
 * Tix_GrDrawSites --
 *
 *	Redraws the special sites (anchor, drag, drop)
 *----------------------------------------------------------------------
 */
static void Tix_GrDrawSites(wPtr, riPtr, drawable)
    WidgetPtr wPtr;
    RenderInfo * riPtr;
    Drawable drawable;
{
    int rect[2][2];
    int visible;

    visible = Tix_GrGetElementPosn(wPtr, wPtr->anchor[0], wPtr->anchor[1],
	rect, 0, 1, 0, 0);
    if (!visible) {
	return;
    }

    Tix_DrawAnchorLines(Tk_Display(wPtr->dispData.tkwin), drawable,
	wPtr->anchorGC,
	rect[0][0] + riPtr->origin[0],
        rect[1][0] + riPtr->origin[1],
	rect[0][1] - rect[0][0] + 1,
	rect[1][1] - rect[1][0] + 1);
}

/*----------------------------------------------------------------------
 *
 * Tix_GrGetElementPosn --
 *
 *	Returns the position of a visible element on the screen.
 *
 * Arguments
 *	x,y:	index of the element.
 *      rect:	stores the return values: four sides of the cell.
 *	clipOK:	if true and element is only partially visible, return only
 *		the visible portion.
 *	isSite:	if (x,y) is a site, the return value depends on the
 *		selectUnit variable.
 *	isScr:  should we return the position within the widget (true)
 *		or within the main display area (false).
 *	nearest:if the element is outside of the widget, should we return
 *		the position of the nearest element?
 *
 *----------------------------------------------------------------------
 */

int
Tix_GrGetElementPosn(wPtr, x, y, rect, clipOK, isSite, isScr, nearest)
    WidgetPtr wPtr;
    int x;
    int y;
    int rect[2][2];
    int clipOK;		/* %% ignored */
    int isSite;
    int isScr;
    int nearest;
{
    int i, j, pos[2];
    int axis = 0;
    int useAxis;

    if (wPtr->selectUnit == tixRowUid) {
	axis = 0;
	useAxis = 1;
    }
    else if (wPtr->selectUnit == tixColumnUid) {
	axis = 1;
	useAxis = 1;
    }
    else {
	useAxis = 0;
    }

    /* %% didn't take care of the headers, etc */

    pos[0] = x;
    pos[1] = y;

    /* clip the anchor site with the visible cells */
    for (i=0; i<2; i++) {
	if (pos[i] == TIX_SITE_NONE) {
	    return 0;
	}

	if (isSite && useAxis && i == axis) {
	    rect[i][0] = 0;
	    rect[i][1] = wPtr->mainRB->visArea[i]-1;
	} else {
	    if (pos[i] >= wPtr->hdrSize[i]) {
		pos[i] -= wPtr->scrollInfo[i].offset;
		if (pos[i] < wPtr->hdrSize[i]) {
		    /* This cell has been scrolled "under the margins" */
		    return 0;
		}
	    }

	    if (pos[i] < 0) {
		if (!nearest) {
		    return 0;
		}
		pos[i] = 0;
	    }
	    if (pos[i] >= wPtr->mainRB->size[i]) {
		if (!nearest) {
		    return 0;
		}
		pos[i] = wPtr->mainRB->size[i] - 1;
	    }
	    rect[i][0] = 0;
	    for (j=0; j<pos[i]; j++) {
		rect[i][0] += wPtr->mainRB->dispSize[i][j].total;
	    }
	    rect[i][1] = rect[i][0] + wPtr->mainRB->dispSize[i][j].total - 1;
	}
    }

    if (isScr) {
	rect[0][0] += wPtr->bdPad;
	rect[1][0] += wPtr->bdPad;
	rect[0][1] += wPtr->bdPad;
	rect[1][1] += wPtr->bdPad;
    }

    return 1;
}

/*----------------------------------------------------------------------
 *
 * "bdtype" sub command --
 *
 *	Returns if the the screen position is at a border. This is useful
 *	for changing the mouse cursor when the user points at a border
 *	area. This indicates that the border can be adjusted interactively.
 *
 *----------------------------------------------------------------------
 */
static int
Tix_GrBdType(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    Tk_Window tkwin = wPtr->dispData.tkwin;
    int i, k, screenPos[2], bd[2], pos[2], in[2], bdWidth[2];
    char buf[100];
    int inX = 0;
    int inY = 0;

    if (argc != 2 && argc != 4) {
	return Tix_ArgcError(interp, argc+2, argv-2, 2,
	    "x y ?xbdWidth ybdWidth?");
    }

    if (Tcl_GetIntFromObj(interp, argv[0], &screenPos[0]) != TCL_OK) {
	return TCL_ERROR;
    }
    if (Tcl_GetIntFromObj(interp, argv[1], &screenPos[1]) != TCL_OK) {
	return TCL_ERROR;
    }
    if (argc == 4) {
	if (Tcl_GetIntFromObj(interp, argv[2], &bdWidth[0]) != TCL_OK) {
	    return TCL_ERROR;
	}
	if (Tcl_GetIntFromObj(interp, argv[3], &bdWidth[1]) != TCL_OK) {
	    return TCL_ERROR;
	}
    } else {
	bdWidth[0] = -1;
	bdWidth[1] = -1;
    }

    if (!Tk_IsMapped(tkwin)) {
	Tcl_ResetResult(interp);
	return TCL_OK;
    }

    if (wPtr->mainRB == NULL || wPtr->toResetRB) {
	Tix_GrResetRenderBlocks(wPtr);
	wPtr->toResetRB = 0;
    }

    screenPos[0] -= wPtr->highlightWidth - wPtr->borderWidth;
    screenPos[1] -= wPtr->highlightWidth - wPtr->borderWidth;

    for (i=0; i<2; i++) {
	bd[i]  = -1;
	pos[i] = 0;
	in[i]  = 0;
	for (k=0; k<wPtr->mainRB->size[i]; k++) {
	    ElmDispSize * elm = &wPtr->mainRB->dispSize[i][k];
	    if (screenPos[i] - elm->total <= 0) {
		if (bdWidth[i] != -1) {
		    if (screenPos[i] < bdWidth[i]) {
			bd[i]  = k - 1;
			pos[i] = k;
		    }
		    else if ((elm->total - screenPos[i]) <= bdWidth[i]) {
			bd[i]  = k;
			pos[i] = k + 1;
		    }
		    else {
			pos[i] = k;
		    }
		} else {
		    if (screenPos[i] < elm->preBorder) {
			bd[i]  = k - 1;
			pos[i] = k;
		    }
		    else if ((screenPos[i] - elm->preBorder - elm->size)>= 0) {
			bd[i]  = k;
			pos[i] = k + 1;
		    }
		    else {
			pos[i] = k;
		    }
		}
		in[i] = k;
		break;
	    } else {
		screenPos[i] -= elm->total;
	    }
	}
    }

    if (in[0] < wPtr->hdrSize[0] && bd[1] >= 0) {
	inY = 1;
    }
    else if (in[1] < wPtr->hdrSize[1] && bd[0] >= 0) {
	inX = 1;
    }

    if (bd[0] < 0) {
	bd[0] = 0;
    }
    if (bd[1] < 0) {
	bd[1] = 0;
    }

    Tcl_ResetResult(interp);
    if (inX && inY) {
	Tcl_AppendElement(interp,"xy");
	Tcl_IntResults(interp,2,1, bd[0], bd[1]);
    } else if (inX) {
	Tcl_AppendElement(interp,"x");
	Tcl_IntResults(interp,2,1, bd[0], bd[1]);
    } else if (inY) {
	Tcl_AppendElement(interp,"y");
	Tcl_IntResults(interp,2,1, bd[0], bd[1]);
    } else {
	/* return nothing */
    }
    return TCL_OK;
}

/*----------------------------------------------------------------------
 * "set" sub command --
 *
 *	Sets the item at the position on the grid. This either creates
 *	a new element or modifies the existing element. (if you don't want
 *	to change the -itemtype of the existing element, it will be more
 *	efficient to call the "itemconfigure" command).
 *----------------------------------------------------------------------
 */
static int
Tix_GrSet(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    TixGrEntry * chPtr = NULL;
    Tix_DItem * iPtr;
    int x, y;
    char * ditemType;
    int code = TCL_OK;

    /*------------------------------------------------------------
     * (0) We need to find out where you want to set
     *------------------------------------------------------------
     */
    if (TixGridDataGetIndex(interp, wPtr, objv[0], objv[1], &x, &y)!=TCL_OK) {
	return TCL_ERROR;
    }

    /*------------------------------------------------------------
     * (1) We need to determine the option: -itemtype.
     *------------------------------------------------------------
     */
    /* (1.0) Find out the -itemtype, if specified */
    ditemType = wPtr->diTypePtr->name;   		 /* default value */
    if (argc > 2) {
	size_t len;
	int i;
	if (argc %2 != 0) {
	    Tcl_AppendResult(interp, "value for \"", argv[argc-1],
		"\" missing", NULL);
	    code = TCL_ERROR;
	    goto done;
	}
	for (i=2; i<argc; i+=2) {
	    len = strlen(argv[i]);
	    if (strncmp(argv[i], "-itemtype", len) == 0) {
		ditemType = argv[i+1];
	    }
	}
    }

    if (Tix_GetDItemType(interp, ditemType) == NULL) {
	code = TCL_ERROR;
	goto done;
    }

    /*
     * (2) Get this item (a new item will be allocated if it does not exist
     *     yet)
     */
    chPtr = Tix_GrFindCreateElem(interp, wPtr, x, y);

    /* (2.1) The Display item data */
    if ((iPtr = Tix_DItemCreate(&wPtr->dispData, ditemType)) == NULL) {
	code = TCL_ERROR;
	goto done;
    }
    iPtr->base.clientData = (ClientData)wPtr;     /* %%%% */

    if (chPtr->iPtr) {
	Tix_DItemFree(chPtr->iPtr);
    }
    chPtr->iPtr = iPtr;

    if (ConfigElement(wPtr, chPtr, argc-2, argv+2, 0, 1) != TCL_OK) {
	code = TCL_ERROR; goto done;
    }
    Tix_GrPropagateSize(wPtr, chPtr);

  done:
    if (code == TCL_ERROR) {
	/* ? */
    } else {
	Tix_GrDoWhenIdle(wPtr, TIX_GR_RESIZE);
    }

    return code;
}

/*----------------------------------------------------------------------
 * "unset" sub command
 *----------------------------------------------------------------------
 */

static int
Tix_GrUnset(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    TixGrEntry * chPtr;
    int x, y;

    if (TixGridDataGetIndex(interp, wPtr, objv[0], objv[1], &x, &y)
	    !=TCL_OK) {
	return TCL_ERROR;
    }

    chPtr = Tix_GrFindElem(interp, wPtr, x, y);
    if (chPtr != NULL) {
	TixGridDataDeleteEntry(wPtr->dataSet, x, y);
	Tix_GrFreeElem(chPtr);
	Tix_GrDoWhenIdle(wPtr, TIX_GR_RESIZE);
    }
    return TCL_OK;
}

/*----------------------------------------------------------------------
 * "cget" sub command --
 *----------------------------------------------------------------------
 */
static int
Tix_GrCGet(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;

    return Tk_ConfigureValue(interp, wPtr->dispData.tkwin, configSpecs,
	(char *)wPtr, argv[0], 0);
}

/*----------------------------------------------------------------------
 * "configure" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_GrConfig(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;

    if (argc == 0) {
	return Tk_ConfigureInfo(interp, wPtr->dispData.tkwin, configSpecs,
	    (char *) wPtr, (char *) NULL, 0);
    } else if (argc == 1) {
	return Tk_ConfigureInfo(interp, wPtr->dispData.tkwin, configSpecs,
	    (char *) wPtr, argv[0], 0);
    } else {
	return WidgetConfigure(interp, wPtr, argc, argv,
	    TK_CONFIG_ARGV_ONLY);
    }
}

/*----------------------------------------------------------------------
 * "delete" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_GrDelete(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    int from, to, which;

    if (TranslateFromTo(interp, wPtr, argc, argv, &from, &to, &which)!=TCL_OK){
	return TCL_ERROR;
    }
    TixGridDataDeleteRange(wPtr, wPtr->dataSet, which, from, to);

    return TCL_OK;
}

/*----------------------------------------------------------------------
 * "edit" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_GrEdit(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    Tcl_Obj *CONST *objv;	/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    int x, y;
    char buff[20];
    int len;
    int code = TCL_OK;
    Tcl_Obj *obj = NULL;


    len = strlen(argv[0]);
    if (strncmp(argv[0], "set", len) == 0) {
	if (argc != 3) {
	    Tcl_AppendResult(interp, "wrong # of arguments, must be: ",
		argv[-2], " edit set x y", NULL);
	}
	if (TixGridDataGetIndex(interp, wPtr, objv[1], objv[2], &x, &y)
		!=TCL_OK) {
	    return TCL_ERROR;
	}
	obj = LangWidgetObj(interp,wPtr->dispData.tkwin);
	code = LangMethodCall(interp,obj, "EditCell",0, 2," %d %d",x, y);
    } else if (strncmp(argv[0], "apply", len) == 0) {
	if (argc != 1) {
	    Tcl_AppendResult(interp, "wrong # of arguments, must be: ",
		argv[-2], " edit apply", NULL);
	}
	obj  = LangWidgetObj(interp,wPtr->dispData.tkwin);
        code = LangMethodCall(interp,obj, "EditApply",0, 0);
    } else {
	Tcl_AppendResult(interp, "unknown option \"", argv[0],
		"\", must be apply or set", NULL);
	return TCL_ERROR;
    }
    if (obj) {
	Tcl_DecrRefCount(obj);
    }
    return code;
}

/*----------------------------------------------------------------------
 * "entrycget" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_GrEntryCget(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    int x, y;
    TixGrEntry * chPtr;

    if (TixGridDataGetIndex(interp, wPtr, objv[0], objv[1], &x, &y)!=TCL_OK) {
	return TCL_ERROR;
    }

    chPtr = Tix_GrFindElem(interp, wPtr, x, y);
    if (!chPtr) {
	Tcl_AppendResult(interp, "entry \"", argv[0], ",", argv[1],
	    "\" does not exist", NULL);
	return TCL_ERROR;
    }

    return Tix_ConfigureValue2(interp, wPtr->dispData.tkwin, (char *)chPtr,
	entryConfigSpecs, chPtr->iPtr, argv[2], 0);
}

/*----------------------------------------------------------------------
 * "entryconfigure" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_GrEntryConfig(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    int x, y;
    TixGrEntry * chPtr;

    if (TixGridDataGetIndex(interp, wPtr, objv[0], objv[1], &x, &y)!=TCL_OK) {
	return TCL_ERROR;
    }

    chPtr = Tix_GrFindElem(interp, wPtr, x, y);
    if (!chPtr) {
	Tcl_AppendResult(interp, "entry \"", argv[0], ",", argv[1],
	    "\" does not exist", NULL);
	return TCL_ERROR;
    }

    if (argc == 2) {
	return Tix_ConfigureInfo2(interp, wPtr->dispData.tkwin,
	    (char*)chPtr, entryConfigSpecs, chPtr->iPtr, (char *) NULL, 0);
    } else if (argc == 3) {
	return Tix_ConfigureInfo2(interp, wPtr->dispData.tkwin,
	    (char*)chPtr, entryConfigSpecs, chPtr->iPtr, (char *) argv[2], 0);
    } else {
	return ConfigElement(wPtr, chPtr, argc-2, argv+2,
	    TK_CONFIG_ARGV_ONLY, 0);
    }
}

/*----------------------------------------------------------------------
 * "geometryinfo" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_GrGeometryInfo(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    int qSize[2];
    double first[2], last[2];
    int i;
    Tix_GridScrollInfo scrollInfo[2];

    if (argc == 2) {
	if (Tcl_GetIntFromObj(interp, argv[0], &qSize[0]) != TCL_OK) {
	    return TCL_ERROR;
	}
	if (Tcl_GetIntFromObj(interp, argv[1], &qSize[1]) != TCL_OK) {
	    return TCL_ERROR;
	}
    } else {
	qSize[0] = Tk_Width (wPtr->dispData.tkwin);
	qSize[1] = Tk_Height(wPtr->dispData.tkwin);
    }
    qSize[0] -= 2*wPtr->borderWidth + 2*wPtr->highlightWidth;
    qSize[1] -= 2*wPtr->borderWidth + 2*wPtr->highlightWidth;

    RecalScrollRegion(wPtr, qSize[0], qSize[1], scrollInfo);

    for (i=0; i<2; i++) {
	qSize[i] -= 2*wPtr->borderWidth + 2*wPtr->highlightWidth;
	GetScrollFractions(wPtr, &scrollInfo[i],
	    &first[i], &last[i]);
    }

    Tcl_DoubleResults(interp,1,4,"{%f %f} {%f %f}", first[0], last[0], first[1], last[1]);
    return TCL_OK;
}

/*----------------------------------------------------------------------
 * "index" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_GrIndex(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    int x, y;

    if (TixGridDataGetIndex(interp, wPtr, objv[0], objv[1], &x, &y)!=TCL_OK) {
	return TCL_ERROR;
    }
    Tcl_IntResults(interp,2,0, x, y);
    return TCL_OK;
}

/*----------------------------------------------------------------------
 * "info" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_GrInfo(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    size_t len = strlen(argv[0]);
    int x, y;

    if (strncmp(argv[0], "bbox", len)==0) {
	if (argc != 3) {
	    return Tix_ArgcError(interp, argc+2, argv-2, 3, "x y");
	}
	if (TixGridDataGetIndex(interp, wPtr, objv[1], objv[2], &x, &y)
		!=TCL_OK) {
	    return TCL_ERROR;
	}

	return Tix_GrBBox(interp, wPtr, x, y);
    }
    else if (strncmp(argv[0], "exists", len)==0) {
	if (argc != 3) {
	    return Tix_ArgcError(interp, argc+2, argv-2, 3, "x y");
	}
	if (TixGridDataGetIndex(interp, wPtr, objv[1], objv[2], &x, &y)
		!=TCL_OK) {
	    return TCL_ERROR;
	}
	if (Tix_GrFindElem(interp, wPtr, x, y)) {
	    Tcl_SetResult(interp, "1", TCL_STATIC);
	} else {
	    Tcl_SetResult(interp, "0", TCL_STATIC);
	}
	return TCL_OK;
    }
    else {
	Tcl_AppendResult(interp, "unknown option \"", argv[0],
	    "\": must be bbox or exists",
	    NULL);
	return TCL_ERROR;
    }
}

/*----------------------------------------------------------------------
 * "move" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_GrMove(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    int from, to, which, by;

    if (TranslateFromTo(interp, wPtr, 3, argv, &from, &to, &which)!=TCL_OK){
	return TCL_ERROR;
    }
    if (Tcl_GetIntFromObj(interp, argv[3], &by) != TCL_OK) {
	return TCL_ERROR;
    }

    TixGridDataMoveRange(wPtr, wPtr->dataSet, which, from, to, by);

    return TCL_OK;
}

/*----------------------------------------------------------------------
 * "nearest" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_GrNearest(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    Tk_Window tkwin = wPtr->dispData.tkwin;
    int i, k, screenPos[2], rbPos[2];
    char buf[100];
    RenderBlockElem* rePtr;

    if (Tcl_GetIntFromObj(interp, argv[0], &screenPos[0]) != TCL_OK) {
	return TCL_ERROR;
    }
    if (Tcl_GetIntFromObj(interp, argv[1], &screenPos[1]) != TCL_OK) {
	return TCL_ERROR;
    }

    if (!Tk_IsMapped(tkwin)) {
	Tcl_ResetResult(interp);
	return TCL_OK;
    }

    if (wPtr->mainRB == NULL || wPtr->toResetRB) {
	Tix_GrResetRenderBlocks(wPtr);
	wPtr->toResetRB = 0;
    }

    screenPos[0] -= wPtr->highlightWidth - wPtr->borderWidth;
    screenPos[1] -= wPtr->highlightWidth - wPtr->borderWidth;

    for (i=0; i<2; i++) {
	for (k=0; k<wPtr->mainRB->size[i]; k++) {
	    screenPos[i] -=  wPtr->mainRB->dispSize[i][k].total;
	    if (screenPos[i]<=0) {
		break;
	    }
	}
	if (k >= wPtr->mainRB->size[i]) {
	    k = wPtr->mainRB->size[i] - 1;
	}
	rbPos[i] = k;
    }
    rePtr = &(wPtr->mainRB->elms[rbPos[0]][rbPos[1]]);

    Tcl_IntResults(interp, 2, 0, rePtr->index[0], rePtr->index[1]);
    return TCL_OK;
}

/*----------------------------------------------------------------------
 * "anchor", "dragsite" and "dropsire" sub commands --
 *
 *	Set/remove the anchor element
 *----------------------------------------------------------------------
 */
static int
Tix_GrSetSite(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    int changed = 0;
    WidgetPtr wPtr = (WidgetPtr) clientData;
    int * changePtr;
    size_t len;
    int changedRect[2][2];

    /*
     * Determine which site should be changed (the last else clause
     * doesn't need to check the string because HandleSubCommand
     * already ensures that only the valid options can be specified.
     */
    len = strlen(argv[-1]);
    if (strncmp(argv[-1], "anchor", len)==0) {
	changePtr = wPtr->anchor;
    }
    else if (strncmp(argv[-1], "dragsite", len)==0) {
	changePtr = wPtr->dragSite;
    }
    else {
	changePtr = wPtr->dropSite;
    }

    len = strlen(argv[0]);
    if (strncmp(argv[0], "get", len)==0) {
        Tcl_IntResults(interp, 2, 0, changePtr[0], changePtr[1]);
	return TCL_OK;
    } else if (strncmp(argv[0], "set", len)==0) {
	if (argc == 3) {
	    int x, y;

	    if (TixGridDataGetIndex(interp, wPtr, objv[1], objv[2],
		     &x, &y)!=TCL_OK) {
		return TCL_ERROR;
	    }
	    if (x != changePtr[0] || y != changePtr[1]) {
		changedRect[0][0] = x;
		changedRect[1][0] = y;
		changedRect[0][1] = changePtr[0];
		changedRect[1][1] = changePtr[1];
		changed = 1;

		changePtr[0] = x;
		changePtr[1] = y;
	    }
	} else {
	    Tcl_AppendResult(interp, "wrong # of arguments, must be: ",
		Tk_PathName(wPtr->dispData.tkwin), " ", argv[-1],
		" set x y", NULL);
	    return TCL_ERROR;
	}
    }
    else if (strncmp(argv[0], "clear", len)==0) {
	if (argc == 1) {
	    if (changePtr[0] !=TIX_SITE_NONE || changePtr[1] !=TIX_SITE_NONE) {
		changedRect[0][0] = TIX_SITE_NONE;
		changedRect[1][0] = TIX_SITE_NONE;
		changedRect[0][1] = changePtr[0];
		changedRect[1][1] = changePtr[1];
		changed = 1;

		changePtr[0] = TIX_SITE_NONE;
		changePtr[1] = TIX_SITE_NONE;
	    }
	} else {
	    Tcl_AppendResult(interp, "wrong # of arguments, must be: ",
		Tk_PathName(wPtr->dispData.tkwin), " ", argv[-1],
		" clear", NULL);
	    return TCL_ERROR;
	}
    }
    else {
	Tcl_AppendResult(interp, "wrong option \"", argv[0], "\", ",
	    "must be clear, get or set", NULL);
	return TCL_ERROR;
    }

    if (changed) {
	Tix_GrAddChangedRect(wPtr, changedRect, 1);
    }

    return TCL_OK;
}

/*----------------------------------------------------------------------
 * Tix_GrAddChangedRect --
 *
 *	Add the "changed" region to the exposedArea structure.
 *----------------------------------------------------------------------
 */
void
Tix_GrAddChangedRect(wPtr, changedRect, isSite)
    WidgetPtr wPtr;
    int changedRect[2][2];
    int isSite;
{
    int rect[2][2];
    int visible;
    int i;
    int changed = 0;

    if (wPtr->mainRB == NULL) {
	/*
	 * The grid will be completely refreshed. Don't do anything
	 */
	return;
    }

    for (i=0; i<2; i++) {
	visible = Tix_GrGetElementPosn(wPtr, changedRect[0][i],
	    changedRect[1][i], rect, 1, isSite, 1, 1);
	if (!visible) {
	    continue;
	}
	if (wPtr->expArea.x1 > rect[0][0]) {
	    wPtr->expArea.x1 = rect[0][0];
	    changed = 1;
	}
	if (wPtr->expArea.x2 < rect[0][1]) {
	    wPtr->expArea.x2 = rect[0][1];
	    changed = 1;
	}
	if (wPtr->expArea.y1 > rect[1][0]) {
	    wPtr->expArea.y1 = rect[1][0];
	    changed = 1;
	}
	if (wPtr->expArea.y2 < rect[1][1]) {
	    wPtr->expArea.y2 = rect[1][1];
	    changed = 1;
	}
    }
    if (changed) {
	Tix_GrDoWhenIdle(wPtr, TIX_GR_REDRAW);
    }
}

void Tix_GrScrollPage(wPtr, count, axis)
    WidgetPtr wPtr;
    int count;
    int axis;
{
    int k, i = axis;
    int winSize, sz, start, num;
    int pad0, pad1;

    Tix_GridScrollInfo * siPtr = &wPtr->scrollInfo[axis];
    int gridSize[2];

    if (count == 0) {
	return;
    }

    TixGridDataGetGridSize(wPtr->dataSet, &gridSize[0],
	&gridSize[1]);

    if (gridSize[i] < wPtr->hdrSize[i]) {	 /* no scrollable data */
	return;
    }

    if (axis == 0) {
	winSize = Tk_Width(wPtr->dispData.tkwin);
    } else {
	winSize = Tk_Height(wPtr->dispData.tkwin);
    }
    winSize -= 2*wPtr->highlightWidth + 2*wPtr->borderWidth;

    for (k=0; k<wPtr->hdrSize[i] && k<gridSize[i]; k++) {
	winSize -= TixGridDataGetRowColSize(wPtr, wPtr->dataSet, i,
		k, &wPtr->defSize[i], &pad0, &pad1);
	winSize -= pad0 + pad1;
    }

    if (winSize <= 0) {
	return;
    }

    if (count > 0) {
	start = siPtr->offset + wPtr->hdrSize[i];
	for (; count > 0; count--) {
	    sz = winSize;

	    for (num=0,k=start; k<gridSize[i]; k++,num++) {
		sz -= TixGridDataGetRowColSize(wPtr, wPtr->dataSet, i,
		    k, &wPtr->defSize[i], &pad0, &pad1);
		sz -= pad0 + pad1;
		if (sz == 0) {
		    num++;
		    break;
		}
		if (sz < 0) {
		    break;
		}
	    }
	    if (num==0) {
		num++;
	    }
	    start += num;
	}
	siPtr->offset = start - wPtr->hdrSize[i];
    }
    else {
	start = siPtr->offset + wPtr->hdrSize[i];

	for (; count < 0; count++) {
	    sz = winSize;

	    for (num=0,k=start-1; k>=wPtr->hdrSize[i]; k--,num++) {
		sz -= TixGridDataGetRowColSize(wPtr, wPtr->dataSet, i,
		    k, &wPtr->defSize[i], &pad0, &pad1);
		sz -= pad0 + pad1;
		if (sz == 0) {
		    num++;
		    break;
		}
		if (sz < 0) {
		    break;
		}
	    }
	    if (num==0) {
		num++;
	    }
	    start -= num;
	}
	siPtr->offset = start - wPtr->hdrSize[i];
    }
}

/*----------------------------------------------------------------------
 * "see" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_GrSee(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    int x, y;
    int code = TCL_OK;
    int oldXOff, oldYOff, lim;

    oldXOff = wPtr->scrollInfo[0].offset;
    oldYOff = wPtr->scrollInfo[1].offset;

    if (TixGridDataGetIndex(interp, wPtr, objv[0], objv[1], &x, &y)!=TCL_OK) {
	return TCL_ERROR;
    }

    x -= wPtr->hdrSize[0];
    if (x >= wPtr->scrollInfo[0].max) {
	x = wPtr->scrollInfo[0].max -1;
    }
    if (x < 0) {
	x = 0;
    }
    lim = wPtr->scrollInfo[0].offset + (int)(wPtr->scrollInfo[0].window * wPtr->scrollInfo[0].max);

    if (x < wPtr->scrollInfo[0].offset || (x+1) > lim) {
	wPtr->scrollInfo[0].offset = x;
    }

    y -= wPtr->hdrSize[1];
    if (y >= wPtr->scrollInfo[1].max) {
	y = wPtr->scrollInfo[1].max -1;
    }
    if (y < 0) {
	y = 0;
    }
    lim = wPtr->scrollInfo[1].offset + (int)(wPtr->scrollInfo[1].window * wPtr->scrollInfo[1].max);

    if (y < wPtr->scrollInfo[1].offset || (y+1) > lim) {
	wPtr->scrollInfo[1].offset = y;
    }

    if (oldXOff != wPtr->scrollInfo[0].offset ||
	oldYOff != wPtr->scrollInfo[1].offset) {
	wPtr->toResetRB    = 1;
	wPtr->toComputeSel = 1;
	Tix_GrDoWhenIdle(wPtr, TIX_GR_REDRAW);
    }

    return code;
}

/*----------------------------------------------------------------------
 * "xview" and "yview" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_GrView(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    int axis, oldXOff, oldYOff;
    Tix_GridScrollInfo * siPtr;

    if (argv[-1][0] == 'x') {
	axis = 0;
    } else {
	axis = 1;
    }

    oldXOff = wPtr->scrollInfo[0].offset;
    oldYOff = wPtr->scrollInfo[1].offset;

    if (argc == 0) {
	char string[100];
	double first, last;

	GetScrollFractions(wPtr, &wPtr->scrollInfo[axis], &first, &last);
        Tcl_DoubleResults(interp,0,2,"%f %f",first, last);
	return TCL_OK;
    }
    else {
	int offset;
	siPtr = &wPtr->scrollInfo[axis];

	if (Tcl_GetIntFromObj(interp, argv[0], &offset) == TCL_OK) {
	    /* backward-compatible mode */
	    siPtr->offset = offset;
	} else {
	    int type, count;
	    double fraction;

	    Tcl_ResetResult(interp);

	    /* Tk_GetScrollInfo () wants strange argc,argv combinations .. */
	    type = Tk_GetScrollInfo(interp, argc+2, argv-2, &fraction, &count);

	    switch (type) {
	      case TK_SCROLL_ERROR:
		return TCL_ERROR;

	      case TK_SCROLL_MOVETO:
		if (siPtr->window < 1.0) {
		    fraction /= (1.0 - siPtr->window);
		}

		siPtr->offset = (int)(fraction * (siPtr->max+1));
		break;

	      case TK_SCROLL_PAGES:
		Tix_GrScrollPage(wPtr, count, axis);
		break;

	      case TK_SCROLL_UNITS:
		siPtr->offset += count * siPtr->unit;
		break;
	    }
	}
	/* check ... */
	if (siPtr->offset < 0) {
	    siPtr->offset  = 0;
	}
	if (siPtr->offset > siPtr->max) {
	    siPtr->offset = siPtr->max;
	}
    }

#if 0
    printf("Configing Scrollbars: (%d %f %d) (%d %f %d)\n",
        wPtr->scrollInfo[0].max,
        wPtr->scrollInfo[0].window,
        wPtr->scrollInfo[0].offset,
        wPtr->scrollInfo[1].max,
        wPtr->scrollInfo[1].window,
        wPtr->scrollInfo[1].offset);
#endif

    if (oldXOff != wPtr->scrollInfo[0].offset ||
	oldYOff != wPtr->scrollInfo[1].offset) {
	wPtr->toResetRB    = 1;
	wPtr->toComputeSel = 1;
	Tix_GrDoWhenIdle(wPtr, TIX_GR_REDRAW);
    }
    return TCL_OK;
}
/*----------------------------------------------------------------------
 *
 *
 * 			Memory Management Section
 *
 *
 *----------------------------------------------------------------------
 */
static int
ConfigElement(wPtr, chPtr, argc, argv, flags, forced)
    WidgetPtr wPtr;
    TixGrEntry *chPtr;
    int argc;
    char ** argv;
    int flags;
    int forced;
{
    int sizeChanged;

    if (Tix_WidgetConfigure2(wPtr->dispData.interp, wPtr->dispData.tkwin,
	(char*)chPtr, entryConfigSpecs, chPtr->iPtr, argc, argv, flags,
	forced, &sizeChanged) != TCL_OK) {
	return TCL_ERROR;
    }

    if (sizeChanged) {
	/* %% be smart here: sometimes the size request doesn't need to
	 * be changed
	 */
	Tix_GrDoWhenIdle(wPtr, TIX_GR_RESIZE);
    } else {
	/* set the exposed area */
	Tix_GrDoWhenIdle(wPtr, TIX_GR_REDRAW);
    }
    return TCL_OK;
}

static char * areaNames[4] = {
    "s_margin",
    "x_margin",
    "y_margin",
    "main"
};

static int
Tix_GrCallFormatCmd(wPtr, which)
    WidgetPtr wPtr;
    int which;
{
    int code;

    wPtr->renderInfo->fmt.whichArea = which;
    code = LangDoCallback(wPtr->dispData.interp, wPtr->formatCmd, 0, 5, "%s %d %d %d %d",
	areaNames[which],
	wPtr->renderInfo->fmt.x1,
	wPtr->renderInfo->fmt.y1,
	wPtr->renderInfo->fmt.x2,
	wPtr->renderInfo->fmt.y2);

    if (code != TCL_OK) {
	Tcl_AddErrorInfo(wPtr->dispData.interp,
	    "\n    (format command executed by tixGrid)");
	Tcl_BackgroundError(wPtr->dispData.interp);
    }

    return code;
}


static void Tix_GrDrawBackground(wPtr, riPtr, drawable)
    WidgetPtr wPtr;
    RenderInfo * riPtr;
    Drawable drawable;
{
    int mainSize[2];
    int visibleHdr[2];

    if (wPtr->formatCmd == NULL) {
	return;
    }

    /* The visible size of the main area
     */
    mainSize[0] = wPtr->mainRB->size[0] - wPtr->hdrSize[0];
    mainSize[1] = wPtr->mainRB->size[1] - wPtr->hdrSize[1];
    if (mainSize[0] < 0) {
	mainSize[0] = 0;
    }
    if (mainSize[1] < 0) {
	mainSize[1] = 0;
    }

    /* the visible header size
     */
    if (wPtr->mainRB->size[0] < wPtr->hdrSize[0]) {
	visibleHdr[0] = wPtr->mainRB->size[0];
    } else {
	visibleHdr[0] = wPtr->hdrSize[0];
    }
    if (wPtr->mainRB->size[1] < wPtr->hdrSize[1]) {
	visibleHdr[1] = wPtr->mainRB->size[1];
    } else {
	visibleHdr[1] = wPtr->hdrSize[1];
    }


    /* the horizontal margin
     */
    if (wPtr->hdrSize[1] > 0 && mainSize[0] > 0) {
	wPtr->renderInfo->fmt.x1 =
	  wPtr->scrollInfo[0].offset + wPtr->hdrSize[0];
	wPtr->renderInfo->fmt.x2 =
	  wPtr->renderInfo->fmt.x1 + mainSize[0] - 1;
	wPtr->renderInfo->fmt.y1 = 0;
	wPtr->renderInfo->fmt.y2 = visibleHdr[1] - 1;

	Tix_GrCallFormatCmd(wPtr, TIX_X_MARGIN);
    }

    /* the vertical margin
     */
    if (wPtr->hdrSize[0] > 0 && mainSize[1] > 0) {
	wPtr->renderInfo->fmt.x1 = 0;
	wPtr->renderInfo->fmt.x2 = visibleHdr[0] - 1;
	wPtr->renderInfo->fmt.y1 =
	  wPtr->scrollInfo[1].offset + wPtr->hdrSize[1];
	wPtr->renderInfo->fmt.y2 =
	  wPtr->renderInfo->fmt.y1 + mainSize[1] - 1;

	Tix_GrCallFormatCmd(wPtr, TIX_Y_MARGIN);
    }

    /* the stationary part of the margin
     */
    if (visibleHdr[0] > 0 && visibleHdr[1] > 0) {
	wPtr->renderInfo->fmt.x1 = 0;
	wPtr->renderInfo->fmt.x2 = visibleHdr[0] - 1;
	wPtr->renderInfo->fmt.y1 = 0;
	wPtr->renderInfo->fmt.y2 = visibleHdr[1] - 1;

	Tix_GrCallFormatCmd(wPtr, TIX_S_MARGIN);
    }

    /* the main area
     */
    if (mainSize[0] > 0 && mainSize[1] > 0) {
	wPtr->renderInfo->fmt.x1 =
	  wPtr->scrollInfo[0].offset + wPtr->hdrSize[0];
	wPtr->renderInfo->fmt.x2 =
	  wPtr->renderInfo->fmt.x1 + mainSize[0] - 1;
	wPtr->renderInfo->fmt.y1 =
	  wPtr->scrollInfo[1].offset + wPtr->hdrSize[1];
	wPtr->renderInfo->fmt.y2 =
	  wPtr->renderInfo->fmt.y1 + mainSize[1] - 1;

	Tix_GrCallFormatCmd(wPtr, TIX_MAIN);
    }
}

static void
Tix_GrComputeSubSelection(wPtr, rect, offs)
    WidgetPtr wPtr;
    int rect[2][2];
    int offs[2];
{
    int iMin, iMax, jMin, jMax;
    Tix_ListIterator li;
    SelectBlock * sbPtr;
    int i, j, x, y;

    Tix_SimpleListIteratorInit(&li);
    for (Tix_SimpleListStart(&wPtr->selList, &li);
	 !Tix_SimpleListDone(&li);
	 Tix_SimpleListNext (&wPtr->selList, &li)) {

	sbPtr = (SelectBlock *)li.curr;

	/* clip the X direction
	 */
	if (rect[0][0] > sbPtr->range[0][0]) {
	    iMin = rect[0][0];
	} else {
	    iMin = sbPtr->range[0][0];
	}

	if (rect[0][1]<sbPtr->range[0][1] || sbPtr->range[0][1]==TIX_GR_MAX) {
	    iMax = rect[0][1];
	} else {
	    iMax = sbPtr->range[0][1];
	}
	if (iMin > iMax) {
	    continue;
	}

	/* clip the Y direction
	 */
	if (rect[1][0] > sbPtr->range[1][0]) {
	    jMin = rect[1][0];
	} else {
	    jMin = sbPtr->range[1][0];
	}
	if (rect[1][1]<sbPtr->range[1][1] || sbPtr->range[1][1]==TIX_GR_MAX) {
	    jMax = rect[1][1];
	} else {
	    jMax = sbPtr->range[1][1];
	}
	if (jMin > jMax) {
	    continue;
	}

	for (i=iMin; i<=iMax; i++) {
	    for (j=jMin; j<=jMax; j++) {
		x = i - offs[0];
		y = j - offs[1];

		switch (sbPtr->type) {
		  case TIX_GR_CLEAR:
		    wPtr->mainRB->elms[x][y].selected = 0;
		    break;
		  case TIX_GR_SET:
		    wPtr->mainRB->elms[x][y].selected = 1;
		    break;
		  case TIX_GR_TOGGLE:
		    wPtr->mainRB->elms[x][y].selected =
		      !wPtr->mainRB->elms[x][y].selected;
		    break;

		}
	    }
	}
    }
}

static void Tix_GrComputeSelection(wPtr)
    WidgetPtr wPtr;
{
    int rect[2][2], offs[2];
    int i, j;
    int mainSize[2];
    int visibleHdr[2];

    for (i=0; i<wPtr->mainRB->size[0]; i++) {
	for (j=0; j<wPtr->mainRB->size[1]; j++) {
	    wPtr->mainRB->elms[i][j].selected = 0;
	}
    }

    /* Get the visible size of the main area
     */
    mainSize[0] = wPtr->mainRB->size[0] - wPtr->hdrSize[0];
    mainSize[1] = wPtr->mainRB->size[1] - wPtr->hdrSize[1];
    if (mainSize[0] < 0) {
	mainSize[0] = 0;
    }
    if (mainSize[1] < 0) {
	mainSize[1] = 0;
    }

    /* Get the visible header size
     */
    if (wPtr->mainRB->size[0] < wPtr->hdrSize[0]) {
	visibleHdr[0] = wPtr->mainRB->size[0];
    } else {
	visibleHdr[0] = wPtr->hdrSize[0];
    }
    if (wPtr->mainRB->size[1] < wPtr->hdrSize[1]) {
	visibleHdr[1] = wPtr->mainRB->size[1];
    } else {
	visibleHdr[1] = wPtr->hdrSize[1];
    }

    /* Compute selection on the stationary part of the margin
     */
    if (visibleHdr[0] > 0 && visibleHdr[1] > 0) {
	rect[0][0] = 0;
	rect[0][1] = visibleHdr[0] - 1;
	rect[1][0] = 0;
	rect[1][1] = visibleHdr[1] - 1;
	offs[0]    = 0;
	offs[1]    = 0;

	Tix_GrComputeSubSelection(wPtr, rect, offs);
    }

    /* Compute selection on the horizontal margin
     */
    if (wPtr->hdrSize[1] > 0 && mainSize[0] > 0) {
	rect[0][0] = wPtr->scrollInfo[0].offset + wPtr->hdrSize[0];
	rect[0][1] = rect[0][0] + mainSize[0] - 1;
	rect[1][0] = 0;
	rect[1][1] = visibleHdr[1] - 1;
	offs[0]    = wPtr->scrollInfo[0].offset;;
	offs[1]    = 0;

	Tix_GrComputeSubSelection(wPtr, rect, offs);
    }

    /* Compute selection on the vertical margin
     */
    if (wPtr->hdrSize[0] > 0 && mainSize[1] > 0) {
	rect[0][0] = 0;
	rect[0][1] = visibleHdr[0] - 1;
	rect[1][0] = wPtr->scrollInfo[1].offset + wPtr->hdrSize[1];
	rect[1][1] = rect[1][0] + mainSize[1] - 1;
	offs[0]    = 0;
	offs[1]    = wPtr->scrollInfo[1].offset;;

	Tix_GrComputeSubSelection(wPtr, rect, offs);
    }

    /* Compute selection on the main area
     */
    if (mainSize[0] > 0 && mainSize[1] > 0) {
	rect[0][0] = wPtr->scrollInfo[0].offset + wPtr->hdrSize[0];
	rect[0][1] = rect[0][0] + mainSize[0] - 1;
	rect[1][0] = wPtr->scrollInfo[1].offset + wPtr->hdrSize[1];
	rect[1][1] = rect[1][0] + mainSize[1] - 1;
	offs[0]    = wPtr->scrollInfo[0].offset;;
	offs[1]    = wPtr->scrollInfo[1].offset;;

	Tix_GrComputeSubSelection(wPtr, rect, offs);
    }
}

/*----------------------------------------------------------------------
 *  UpdateScrollBars
 *----------------------------------------------------------------------
 */
static void
GetScrollFractions(wPtr, siPtr, first_ret, last_ret)
    WidgetPtr wPtr;
    Tix_GridScrollInfo *siPtr;
    double * first_ret;
    double * last_ret;
{
    double first, last;
    double usuable;

    usuable = 1.0 - siPtr->window;

    if (siPtr->max > 0) {
	first = usuable * (double)(siPtr->offset) / (double)(siPtr->max);
	last  = first + siPtr->window;
    } else {
	first = 0.0;
	last  = 1.0;
    }

    *first_ret = first;
    *last_ret  = last;
}

static void UpdateScrollBars(wPtr, sizeChanged)
    WidgetPtr wPtr;
    int sizeChanged;
{
    int i;
    Tix_GridScrollInfo *siPtr;
    Tcl_Interp * interp = wPtr->dispData.interp;

    for (i=0; i<2; i++) {
	double first, last;
	double usuable;

	siPtr = &wPtr->scrollInfo[i];

	usuable = 1.0 - siPtr->window;

	if (siPtr->max > 0) {
	    first = usuable * (double)(siPtr->offset) / (double)(siPtr->max);
	    last  = first + siPtr->window;
	} else {
	    first = 0.0;
	    last  = 1.0;
	}

	if (siPtr->command) {
	    if (LangDoCallback(interp, siPtr->command, 0, 2, " %g %g", first, last)
	        != TCL_OK) {
		Tcl_AddErrorInfo(interp,
		    "\n    (scrolling command executed by tixGrid)");
		Tcl_BackgroundError(interp);
	    }
	}
    }

    if (wPtr->sizeCmd && sizeChanged) {
	if (LangDoCallback(wPtr->dispData.interp, wPtr->sizeCmd, 0, 0) != TCL_OK) {
	    Tcl_AddErrorInfo(wPtr->dispData.interp,
		"\n    (size command executed by tixGrid)");
	    Tcl_BackgroundError(wPtr->dispData.interp);
	}
    }
}

/*----------------------------------------------------------------------
 * Tix_GrFindCreateElem --
 *
 *	Returns the element. If it doesn't exist, create a new one
 *	and return it.
 *----------------------------------------------------------------------
 */

static TixGrEntry *
Tix_GrFindCreateElem(interp, wPtr, x, y)
    Tcl_Interp * interp;
    WidgetPtr wPtr;
    int x;
    int y;
{
    static TixGrEntry * defaultEntry = NULL;
    TixGrEntry * chPtr;

    if (defaultEntry == NULL) {
	defaultEntry = (TixGrEntry*)ckalloc(sizeof(TixGrEntry));
	defaultEntry->iPtr     = NULL;
    }

    chPtr = (TixGrEntry*)TixGridDataCreateEntry(wPtr->dataSet, x, y,
	(char*)defaultEntry);

    if (chPtr == defaultEntry) {
	defaultEntry = NULL;
    }

    return chPtr;
}

/*----------------------------------------------------------------------
 * Tix_GrFindElem --
 *
 *	Return the element if it exists. Otherwise returns 0.
 *----------------------------------------------------------------------
 */
static TixGrEntry *
Tix_GrFindElem(interp, wPtr, x, y)
    Tcl_Interp * interp;	/* Used for error reporting */
    WidgetPtr wPtr;		/* The grid widget */
    int x;			/* X coord of the entry */
    int y;			/* Y coord of the entry */
{
    return (TixGrEntry*)TixGridDataFindEntry(wPtr->dataSet, x, y);
}

/*----------------------------------------------------------------------
 * Tix_GrFreeElem --
 *
 *	Frees the element.
 *
 *----------------------------------------------------------------------
 */

void
Tix_GrFreeElem(chPtr)
    TixGrEntry * chPtr;		/* The element fo free */
{
    if (chPtr->iPtr) {
	Tix_DItemFree(chPtr->iPtr);
    }
    ckfree((char*)chPtr);
}

static void
Tix_GrPropagateSize(wPtr, chPtr)
    WidgetPtr wPtr;
    TixGrEntry * chPtr;
{
#if 0
    int i;

    for (i=0; i<2; i++) {
	TreeListRoot * rPtr;
	GridHeader * hdr;

	rPtr = chPtr->nodes[i].root;
	hdr = (GridHeader*) rPtr->data;

	if (hdr->size < chPtr->size[i]) {
	    hdr->size = chPtr->size[i];
	    hdr->recalSize = 0;
	}
    }
#endif
}

static RenderBlock * Tix_GrAllocateRenderBlock(wPtr, winW, winH, exactW,exactH)
    WidgetPtr wPtr;
    int winW;
    int winH;
    int * exactW;
    int * exactH;
{
    RenderBlock * rbPtr;
    int i, j, k;
    int offset[2];		/* how much the entries were scrolled */
    int winSize[2];
    int exactSize[2];		/* BOOL: are all the visible coloums and rows
				 * displayed in whole */
    int pad0, pad1;

    offset[0] = wPtr->scrollInfo[0].offset + wPtr->hdrSize[0];
    offset[1] = wPtr->scrollInfo[1].offset + wPtr->hdrSize[1];

    winSize[0] = winW;
    winSize[1] = winH;

    rbPtr = (RenderBlock*)ckalloc(sizeof(RenderBlock));

    rbPtr->size[0]=0;
    rbPtr->size[1]=0;
    rbPtr->visArea[0] = winW;
    rbPtr->visArea[1] = winH;

    /* (1) find out the size requirement of each row and column.
     *     The results are stored in rbPtr->size[i] and
     *     rbPtr->dispSize[i][0 .. (rbPtr->size[i]-1)]
     */
    for (i=0; i<2; i++) {
	/* i=0 : handle the column sizes;
	 * i=1 : handle the row    sizes;
	 */
	int index;
	int pixelSize = 0;

	/* The margins */
	for (index=0; index<wPtr->hdrSize[i] && pixelSize<winSize[i]; index++){
	    pixelSize += TixGridDataGetRowColSize(wPtr, wPtr->dataSet, i,
		index, &wPtr->defSize[i], &pad0, &pad1);
	    pixelSize += pad0 + pad1;
	    rbPtr->size[i] ++;
	}

	for (index=offset[i]; pixelSize<winSize[i]; index++) {
	    pixelSize += TixGridDataGetRowColSize(wPtr, wPtr->dataSet, i,
		index, &wPtr->defSize[i], &pad0, &pad1);
	    pixelSize += pad0 + pad1;
	    rbPtr->size[i] ++;
	}
	if (pixelSize == winSize[i]) {
	    exactSize[i] = 1;
	} else {
	    exactSize[i] = 0;
	}
    }

    /* return values */

    *exactW = exactSize[0];
    *exactH = exactSize[1];

    rbPtr->dispSize[0] = (ElmDispSize*)
	ckalloc(sizeof(ElmDispSize)*rbPtr->size[0]);
    rbPtr->dispSize[1] = (ElmDispSize*)
	ckalloc(sizeof(ElmDispSize)*rbPtr->size[1]);

    /*
     * (2) fill the size info of all the visible rows and cols into
     *     the dispSize arrays;
     */

    for (i=0; i<2; i++) {
	/*
	 * i=0 : handle the column sizes;
	 * i=1 : handle the row    sizes;
	 */
	int index;

	for (k=0; k<rbPtr->size[i]; k++) {
	    if (k < wPtr->hdrSize[i]) {			/* The margins */
		index = k;
	    } else {
		index = k + offset[i] - wPtr->hdrSize[i];
	    }

	    rbPtr->dispSize[i][k].size = TixGridDataGetRowColSize(wPtr,
		wPtr->dataSet, i, index, &wPtr->defSize[i], &pad0, &pad1);
	    rbPtr->dispSize[i][k].preBorder  = pad0;
	    rbPtr->dispSize[i][k].postBorder = pad1;
	}
    }

    /*
     * (3) Put the visible elements into the render block array,
     *     rbPtr->elms[*][*].
     */
    rbPtr->elms = (RenderBlockElem**)
        ckalloc(sizeof(RenderBlockElem*)*rbPtr->size[0]);

    for (i=0; i<rbPtr->size[0]; i++) {
	rbPtr->elms[i] = (RenderBlockElem*)
	    ckalloc(sizeof(RenderBlockElem) * rbPtr->size[1]);
	for (j=0; j<rbPtr->size[1]; j++) {
	    rbPtr->elms[i][j].chPtr = NULL;
	    rbPtr->elms[i][j].selected = 0;
	}
    }

    for (i=0; i<rbPtr->size[0]; i++) {
	for (j=0; j<rbPtr->size[1]; j++) {
	    int x, y;

	    if (i<wPtr->hdrSize[0]) {
		x = i;
	    } else {
		x = i+offset[0]-wPtr->hdrSize[0];
	    }

	    if (j<wPtr->hdrSize[1]) {
		y = j;
	    } else {
		y = j+offset[1]-wPtr->hdrSize[1];
	    }

	    rbPtr->elms[i][j].chPtr = (TixGrEntry*) TixGridDataFindEntry(
		wPtr->dataSet, x, y);
	    rbPtr->elms[i][j].index[0] = x;
	    rbPtr->elms[i][j].index[1] = y;
	}
    }

    for (k=0; k<2; k++) {
	for (i=0; i<rbPtr->size[k]; i++) {
	    rbPtr->dispSize[k][i].total =
	          rbPtr->dispSize[k][i].preBorder
	        + rbPtr->dispSize[k][i].size
	        + rbPtr->dispSize[k][i].postBorder;
	}
    }

    return rbPtr;
}

static void
Tix_GrFreeRenderBlock(wPtr, rbPtr)
    WidgetPtr wPtr;
    RenderBlock * rbPtr;
{
    int i;

    for (i=0; i<rbPtr->size[0]; i++) {
	ckfree((char*)rbPtr->elms[i]);
    }
    ckfree((char*)rbPtr->elms);
    ckfree((char*)rbPtr->dispSize[0]);
    ckfree((char*)rbPtr->dispSize[1]);
    ckfree((char*)rbPtr);
}

/*----------------------------------------------------------------------
 * Tix_GrBBox --
 *
 *	Returns the visible bounding box of a entry.
 *
 * Return value:
 *	See user documenetation.
 *
 * Side effects:
 *	None.
 *----------------------------------------------------------------------
 */

static int Tix_GrBBox(interp, wPtr, x, y)
    Tcl_Interp * interp;	/* Interpreter to report the bbox. */
    WidgetPtr wPtr;		/* HList widget. */
    int x;			/* X coordinate of the entry.*/
    int y;			/* Y coordinate of the entry.*/
{
    int rect[2][2];
    int visible;

    if (!Tk_IsMapped(wPtr->dispData.tkwin)) {
	return TCL_OK;
    }

    visible = Tix_GrGetElementPosn(wPtr, wPtr->anchor[0], wPtr->anchor[1],
	rect, 0, 0, 1, 0);
    if (!visible) {
	return TCL_OK;
    }
    Tcl_IntResults(interp,4,0, rect[0][0], rect[1][0],
	rect[0][1] - rect[0][0] + 1,
	rect[1][1] - rect[1][0] + 1);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 * TranslateFromTo --
 *
 *	Translate the "option from ?to?" arguments from string to integer.
 *
 * Results:
 *	Standard Tcl results.
 *
 * Side effects:
 *	On success, *from and *to contains the from and to values.
 *----------------------------------------------------------------------
 */

static int
TranslateFromTo(interp, wPtr, argc, argv, from, to, which)
    Tcl_Interp * interp;
    WidgetPtr wPtr;
    int argc;
    char **argv;
    int *from;
    int *to;
    int * which;
{
    int dummy = 0;
    size_t len = strlen(argv[0]);

    if (strncmp(argv[0], "row", len) == 0) {
	*which = 1;

	if (TixGridDataGetIndex(interp, wPtr, NULL, objv[1], &dummy, from)
		!=TCL_OK) {
	    return TCL_ERROR;
	}
	if (argc == 3) {
	    if (TixGridDataGetIndex(interp, wPtr, NULL, objv[2], &dummy, to)
		    !=TCL_OK) {
		return TCL_ERROR;
	    }
	} else {
	    *to = *from;
	}
    } else if (strncmp(argv[0], "column", len) == 0) {
	*which = 0;
	if (TixGridDataGetIndex(interp, wPtr, objv[1], NULL, from, &dummy)
		!=TCL_OK) {
	    return TCL_ERROR;
	}

	if (argc == 3) {
	    if (TixGridDataGetIndex(interp, wPtr, objv[2], NULL, to, &dummy)
		    !=TCL_OK) {
		return TCL_ERROR;
	    }
	} else {
	    *to = *from;
	}
    }

    return TCL_OK;
}
