The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

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

/*
 * tixTList.c --
 *
 *	This module implements "TList" 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 "tixPort.h"
#include "tixInt.h"
#include "tkInt.h"
#include "tixTList.h"
#include "tixDef.h"

#define TIX_UP		1
#define TIX_DOWN	2
#define TIX_LEFT	3
#define TIX_RIGHT 	4

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

    {TK_CONFIG_COLOR, "-background", "background", "Background",
       DEF_TLIST_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_BORDER, "-highlightbackground", "highlightBackground",
       "HighlightBackground",
       DEF_TLIST_BG_COLOR, Tk_Offset(WidgetRecord, border),
       TK_CONFIG_COLOR_ONLY},

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

    {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
       DEF_TLIST_BORDER_WIDTH, Tk_Offset(WidgetRecord, borderWidth), 0},

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

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

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

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

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

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

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

    {TK_CONFIG_INT, "-height", "height", "Height",
       DEF_TLIST_HEIGHT, Tk_Offset(WidgetRecord, height), 0},

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

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

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

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

    {TK_CONFIG_UID, "-orient", "orient", "Orient",
	DEF_TLIST_ORIENT, Tk_Offset(WidgetRecord, orientUid), 0},

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

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

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

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

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

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

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

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

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

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

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

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

    {TK_CONFIG_INT, "-width", "width", "Width",
	DEF_TLIST_WIDTH, Tk_Offset(WidgetRecord, width), 0},

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

    {TK_CONFIG_CALLBACK, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
       DEF_TLIST_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}
};

#define DEF_TLISTENTRY_STATE	 "normal"
#define DEF_TLISTENTRY_DATA	 NULL

static Tk_ConfigSpec entryConfigSpecs[] = {

    {TK_CONFIG_LANGARG, "-data", (char *) NULL, (char *) NULL,
       DEF_TLISTENTRY_DATA, Tk_Offset(ListEntry, data), TK_CONFIG_NULL_OK},

    {TK_CONFIG_UID, "-state", (char*)NULL, (char*)NULL,
       DEF_TLISTENTRY_STATE, Tk_Offset(ListEntry, state), 0},

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

static Tix_ListInfo entListInfo = {
    Tk_Offset(ListEntry, next),
    TIX_UNDEFINED,
};


/*
 * 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));

	/* Extra procedures for this widget
	 */
static void		CancelRedrawWhenIdle _ANSI_ARGS_((WidgetPtr wPtr));
static void		CancelResizeWhenIdle _ANSI_ARGS_((WidgetPtr wPtr));
static int		ConfigElement _ANSI_ARGS_((WidgetPtr wPtr,
			    ListEntry *chPtr, int argc, char ** argv,
			    int flags, int forced));
static void 		RedrawRows _ANSI_ARGS_((
			    WidgetPtr wPtr, Drawable pixmap));
static void		RedrawWhenIdle _ANSI_ARGS_((WidgetPtr wPtr));
static void 		ResizeRows _ANSI_ARGS_((
			    WidgetPtr wPtr, int winW, int winH));
static void		ResizeWhenIdle _ANSI_ARGS_((WidgetPtr wPtr));
static void		ResizeNow _ANSI_ARGS_((WidgetPtr wPtr));
static void		UpdateScrollBars _ANSI_ARGS_((WidgetPtr wPtr,
			    int sizeChanged));
static int		Tix_TLGetFromTo _ANSI_ARGS_((
			    Tcl_Interp *interp, WidgetPtr wPtr,
			    int argc, char **argv,
			    ListEntry ** fromPtr_ret, ListEntry ** toPtr_ret));
static void		Tix_TLDItemSizeChanged _ANSI_ARGS_((
			    Tix_DItem *iPtr));
static void		MakeGeomRequest _ANSI_ARGS_((
			    WidgetPtr wPtr));
static int		Tix_TLGetNearest _ANSI_ARGS_((
   			    WidgetPtr wPtr, int posn[2]));
static int		Tix_TLGetAt _ANSI_ARGS_((WidgetPtr wPtr,
			    Tcl_Interp *interp, char * spec, int *at));
static int		Tix_TLGetNeighbor _ANSI_ARGS_((
			    WidgetPtr wPtr, Tcl_Interp *interp,
			    int type, int argc, char **argv));
static int		Tix_TranslateIndex _ANSI_ARGS_((
			    WidgetPtr wPtr, Tcl_Interp *interp, Tcl_Obj * string,
			    int * index, int isInsert));
static int		Tix_TLDeleteRange _ANSI_ARGS_((
			    WidgetPtr wPtr, ListEntry * fromPtr,
			    ListEntry *toPtr));
static ListEntry *	AllocEntry _ANSI_ARGS_((WidgetPtr wPtr));
static int		AddElement _ANSI_ARGS_((WidgetPtr wPtr,
			    ListEntry * chPtr, int at));
static void		FreeEntry _ANSI_ARGS_((WidgetPtr wPtr,
			    ListEntry * chPtr));
static int 		Tix_TLSpecialEntryInfo _ANSI_ARGS_((
			    WidgetPtr wPtr, Tcl_Interp *interp,
			    ListEntry * chPtr));
static void		Realloc _ANSI_ARGS_((WidgetPtr wPtr,int new_size));

static TIX_DECLARE_SUBCMD(Tix_TLInsert);
static TIX_DECLARE_SUBCMD(Tix_TLCGet);
static TIX_DECLARE_SUBCMD(Tix_TLConfig);
static TIX_DECLARE_SUBCMD(Tix_TLDelete);
static TIX_DECLARE_SUBCMD(Tix_TLEntryCget);
static TIX_DECLARE_SUBCMD(Tix_TLEntryConfig);
static TIX_DECLARE_SUBCMD(Tix_TLGeometryInfo);
static TIX_DECLARE_SUBCMD(Tix_TLIndex);
static TIX_DECLARE_SUBCMD(Tix_TLInfo);
static TIX_DECLARE_SUBCMD(Tix_TLNearest);
static TIX_DECLARE_SUBCMD(Tix_TLSee);
static TIX_DECLARE_SUBCMD(Tix_TLSelection);
static TIX_DECLARE_SUBCMD(Tix_TLSetSite);
static TIX_DECLARE_SUBCMD(Tix_TLView);

/*
 *--------------------------------------------------------------
 *
 * Tix_TListCmd --
 *
 *	This procedure is invoked to process the "tixTList" Tcl
 *	command.  It creates a new "TixTList" widget.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	A new widget is created and configured.
 *
 *--------------------------------------------------------------
 */
int
Tix_TListCmd(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, "TixTList");

    /*
     * 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_TLDItemSizeChanged;
    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->redrawing 		= 0;
    wPtr->resizing 		= 0;
    wPtr->hasFocus 		= 0;
    wPtr->selectMode		= NULL;
    wPtr->seeElemPtr 		= NULL;
    wPtr->anchor 		= NULL;
    wPtr->active 		= NULL;
    wPtr->dropSite 		= NULL;
    wPtr->dragSite 		= NULL;
    wPtr->sizeCmd		= NULL;
    wPtr->browseCmd		= NULL;
    wPtr->takeFocus		= NULL;
    wPtr->orientUid		= NULL;
    wPtr->serial		= 0;
    wPtr->state			= tixNormalUid;
    wPtr->rows			= (ListRow*)ckalloc(sizeof(ListRow) *1);
    wPtr->numRow		= 1;
    wPtr->numRowAllocd		= 1;
    wPtr->width			= 0;
    wPtr->height		= 0;

    Tix_LinkListInit(&wPtr->entList);
    Tix_InitScrollInfo((Tix_ScrollInfo*)&wPtr->scrollInfo[0], TIX_SCROLL_INT);
    Tix_InitScrollInfo((Tix_ScrollInfo*)&wPtr->scrollInfo[1], TIX_SCROLL_INT);

    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;
    size_t length;
    Tix_StyleTemplate stTmpl;

    oldfont = wPtr->font;

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

    length = strlen(wPtr->orientUid);
    if (strncmp(wPtr->orientUid, "vertical", length) == 0) {
	wPtr->isVertical = 1;
    } else if (strncmp(wPtr->orientUid, "horizontal", length) == 0) {
	wPtr->isVertical = 0;
    } else {
	Tcl_AppendResult(interp, "bad orientation \"", wPtr->orientUid,
	    "\": must be vertical or horizontal", (char *) NULL);
	wPtr->orientUid = Tk_GetUid("vertical");
	wPtr->isVertical = 1;
	return TCL_ERROR;
    }

    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) {
	/* Font has been changed (initialized) */
	TixComputeTextGeometry(wPtr->font, "0", 1,
	    0, &wPtr->scrollInfo[0].unit, &wPtr->scrollInfo[1].unit);
    }

    /*
     * 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);

    /*
     * Probably the -font or -width or -height options have changed. Let's
     * make geometry request
     */
    MakeGeomRequest(wPtr);

    ResizeWhenIdle(wPtr);

    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, "active", 1, 2, Tix_TLSetSite,
	   "option ?index?"},
	{TIX_DEFAULT_LEN, "anchor", 1, 2, Tix_TLSetSite,
	   "option ?index?"},
	{TIX_DEFAULT_LEN, "cget", 1, 1, Tix_TLCGet,
	   "option"},
	{TIX_DEFAULT_LEN, "configure", 0, TIX_VAR_ARGS, Tix_TLConfig,
	   "?option? ?value? ?option value ... ?"},
	{TIX_DEFAULT_LEN, "delete", 1, 2, Tix_TLDelete,
	   "from ?to?"},
	{TIX_DEFAULT_LEN, "dragsite", 1, 2, Tix_TLSetSite,
	   "option ?entryPath?"},
	{TIX_DEFAULT_LEN, "dropsite", 1, 2, Tix_TLSetSite,
	   "option ?entryPath?"},
	{TIX_DEFAULT_LEN, "entrycget", 2, 2, Tix_TLEntryCget,
	   "entryPath option"},
	{TIX_DEFAULT_LEN, "entryconfigure", 1, TIX_VAR_ARGS, Tix_TLEntryConfig,
	   "index ?option? ?value? ?option value ... ?"},
	{TIX_DEFAULT_LEN, "geometryinfo", 0, 2, Tix_TLGeometryInfo,
	   "?width height?"},
	{TIX_DEFAULT_LEN, "index", 1, 1, Tix_TLIndex,
	   "index"},
	{TIX_DEFAULT_LEN, "info", 1, TIX_VAR_ARGS, Tix_TLInfo,
	   "option ?args ...?"},
	{TIX_DEFAULT_LEN, "insert", 1, TIX_VAR_ARGS, Tix_TLInsert,
	   "where ?option value ..."},
	{TIX_DEFAULT_LEN, "nearest", 2, 2, Tix_TLNearest,
	   "x y"},
	{TIX_DEFAULT_LEN, "see", 1, 1, Tix_TLSee,
	   "entryPath"},
	{TIX_DEFAULT_LEN, "selection", 1, 3, Tix_TLSelection,
	   "option arg ?arg ...?"},
	{TIX_DEFAULT_LEN, "xview", 0, 3, Tix_TLView,
	   "args"},
	{TIX_DEFAULT_LEN, "yview", 0, 3, Tix_TLView,
	   "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;

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

      case ConfigureNotify:
	ResizeWhenIdle(wPtr);
	break;

      case Expose:
	RedrawWhenIdle(wPtr);
	break;

      case FocusIn:
	wPtr->hasFocus = 1;
	RedrawWhenIdle(wPtr);
	break;

      case FocusOut:
	wPtr->hasFocus = 0;
	RedrawWhenIdle(wPtr);
	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 my widget. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;


    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->entList.numItems > 0) {
	ListEntry * fromPtr=NULL, *toPtr=NULL;
	Tcl_Obj *objv[2];
	objv[0] = Tcl_NewIntObj(0);
	objv[1] = Tcl_NewStringObj("end",3);

	Tix_TLGetFromTo(wPtr->dispData.interp, wPtr, 2, objv, &fromPtr,&toPtr);
	Tcl_DecrRefCount(objv[0]);
	Tcl_DecrRefCount(objv[1]);
	Tcl_ResetResult(wPtr->dispData.interp);

	if (fromPtr && toPtr) {
	    Tix_TLDeleteRange(wPtr, fromPtr, toPtr);
	}
    }

    if (wPtr->rows) {
	ckfree((char*)wPtr->rows);
    }

    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;	/* Pointer to widget record for widget. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;

    /*
     * This procedure could be invoked either because the window was
     * destroyed and the command was then deleted (in which case tkwin
     * is NULL) or because the command was deleted, and then this procedure
     * destroys the widget.
     */
    if (wPtr->dispData.tkwin != NULL) {
	Tk_Window tkwin = wPtr->dispData.tkwin;
	wPtr->dispData.tkwin = NULL;
	wPtr->dispData.sizeChangedProc = NULL;
	Tk_DestroyWindow(tkwin);
    }
}

/*
 *--------------------------------------------------------------
 *
 * 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 winW, winH;
    Tk_Window tkwin = wPtr->dispData.tkwin;

    wPtr->resizing = 0;

    if (tkwin == NULL) {
	return;
    }

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

    ResizeRows(wPtr, winW, winH);
    /* Update scrollbars */
    UpdateScrollBars(wPtr, 1);

    RedrawWhenIdle(wPtr);
}

static void
MakeGeomRequest(wPtr)
    WidgetPtr wPtr;
{
    int reqW, reqH;

    reqW = wPtr->width  * wPtr->scrollInfo[0].unit;
    reqH = wPtr->height * wPtr->scrollInfo[1].unit;

    Tk_GeometryRequest(wPtr->dispData.tkwin, reqW, reqH);
}



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

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

/*
 *----------------------------------------------------------------------
 * ResizeWhenIdle --
 *----------------------------------------------------------------------
 */
static void
ResizeWhenIdle(wPtr)
    WidgetPtr wPtr;
{
    if (wPtr->redrawing) {
	CancelRedrawWhenIdle(wPtr);
    }
    if (!wPtr->resizing) {
	wPtr->resizing = 1;
	Tcl_DoWhenIdle(WidgetComputeGeometry, (ClientData)wPtr);
    }
}

/*
 *----------------------------------------------------------------------
 * ResizeWhenIdle --
 *----------------------------------------------------------------------
 */
static void
ResizeNow(wPtr)
    WidgetPtr wPtr;
{
    if (wPtr->resizing) {
	Tcl_CancelIdleCall(WidgetComputeGeometry, (ClientData)wPtr);

	WidgetComputeGeometry((ClientData)wPtr);
	wPtr->resizing = 0;
    }
}

/*
 *----------------------------------------------------------------------
 * CancelResizeWhenIdle --
 *----------------------------------------------------------------------
 */
static void
CancelResizeWhenIdle(wPtr)
    WidgetPtr wPtr;
{
    if (wPtr->resizing) {
	wPtr->resizing = 0;
	Tcl_CancelIdleCall(WidgetComputeGeometry, (ClientData)wPtr);
    }
}

/*
 *----------------------------------------------------------------------
 * RedrawWhenIdle --
 *----------------------------------------------------------------------
 */
static void
RedrawWhenIdle(wPtr)
    WidgetPtr wPtr;
{
    if (wPtr->resizing) {
	/*
	 * Resize will eventually call redraw.
	 */
	return;
    }
    if (!wPtr->redrawing && Tk_IsMapped(wPtr->dispData.tkwin)) {
	wPtr->redrawing = 1;
	Tcl_DoWhenIdle(WidgetDisplay, (ClientData)wPtr);
    }
}

/*
 *----------------------------------------------------------------------
 * CancelRedrawWhenIdle --
 *----------------------------------------------------------------------
 */
static void
CancelRedrawWhenIdle(wPtr)
    WidgetPtr wPtr;
{
    if (wPtr->redrawing) {
	wPtr->redrawing = 0;
	Tcl_CancelIdleCall(WidgetDisplay, (ClientData)wPtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * WidgetDisplay --
 *
 *	Draw the widget to the screen.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */
static void
WidgetDisplay(clientData)
    ClientData clientData;	/* Info about my widget. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    Pixmap pixmap;
    Tk_Window tkwin = wPtr->dispData.tkwin;
    int winH, winW;

    wPtr->redrawing = 0;		/* clear the redraw flag */
    wPtr->serial ++;

    pixmap = Tk_GetPixmap(wPtr->dispData.display, Tk_WindowId(tkwin),
	Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));

    /* Fill the background */
    XFillRectangle(wPtr->dispData.display, pixmap, wPtr->backgroundGC,
	0, 0, Tk_Width(tkwin), Tk_Height(tkwin));

    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) {
	RedrawRows(wPtr, pixmap);
    }

    /* Draw the border */
    Tk_Draw3DRectangle(wPtr->dispData.tkwin, pixmap, wPtr->border,
	wPtr->highlightWidth, wPtr->highlightWidth,
	Tk_Width(tkwin)  - 2*wPtr->highlightWidth,
	Tk_Height(tkwin) - 2*wPtr->highlightWidth, wPtr->borderWidth,
	wPtr->relief);

    /* Draw the highlight */
    if (wPtr->highlightWidth > 0) {
	GC gc;

	if (wPtr->hasFocus) {
	    gc = wPtr->highlightGC;
	} else {
	    gc = Tk_3DBorderGC(wPtr->dispData.tkwin, wPtr->border,
		TK_3D_FLAT_GC);
	}
	Tk_DrawFocusHighlight(tkwin, gc, wPtr->highlightWidth, pixmap);
    }

    /*
     * Copy the information from the off-screen pixmap onto the screen,
     * then delete the pixmap.
     */
    XCopyArea(wPtr->dispData.display, pixmap, Tk_WindowId(tkwin),
	wPtr->backgroundGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
    Tk_FreePixmap(wPtr->dispData.display, pixmap);

}

/*----------------------------------------------------------------------
 *  AddElement  --
 *
 *	Add the element at the position indicated by "at".
 *----------------------------------------------------------------------
 */
static int
AddElement(wPtr, chPtr, at)
    WidgetPtr wPtr;
    ListEntry * chPtr;
    int at;
{
    if (at >= wPtr->entList.numItems) {
	/* The "end" position */
	Tix_LinkListAppend(&entListInfo, &wPtr->entList, (char*)chPtr, 0);
    }
    else {
	Tix_ListIterator li;
	Tix_LinkListIteratorInit(&li);

	for (Tix_LinkListStart(&entListInfo, &wPtr->entList, &li);
	     !Tix_LinkListDone(&li);
	     Tix_LinkListNext (&entListInfo, &wPtr->entList, &li)) {

	    if (at == 0) {
		Tix_LinkListInsert(&entListInfo, &wPtr->entList,
		    (char*)chPtr, &li);
		break;
	    } else {
		-- at;
	    }
	}
    }

    return TCL_OK;
}

/*----------------------------------------------------------------------
 *  AllocEntry  --
 *
 *	Allocates memory for a new entry and initializes it to a
 *	proper state.
 *----------------------------------------------------------------------
 */
static ListEntry *
AllocEntry(wPtr)
    WidgetPtr wPtr;
{
    ListEntry * chPtr;

    chPtr = (ListEntry *)ckalloc(sizeof(ListEntry));
    chPtr->state	= NULL;
    chPtr->selected	= 0;
    chPtr->iPtr		= NULL;

    return chPtr;
}

/*----------------------------------------------------------------------
 *  FreeEntry  --
 *
 *	Free the entry and all resources allocated to this entry.
 *	This entry must already be deleted from the list.
 *----------------------------------------------------------------------
 */
static void
FreeEntry(wPtr, chPtr)
    WidgetPtr wPtr;
    ListEntry * chPtr;
{

    if (wPtr->seeElemPtr == chPtr) {
	/*
	 * This is the element that should be visible the next time
	 * we draw the window. Adjust the "to see element" to an element
	 * that is close to it.
	 */
	if (chPtr->next != NULL) {
	    wPtr->seeElemPtr = chPtr->next;
	} else {
	    ListEntry *p;

	    wPtr->seeElemPtr = NULL;
	    for (p=(ListEntry*)wPtr->entList.head; p; p=p->next) {
		if (p->next == chPtr) {
		    wPtr->seeElemPtr = p;
		    break;
		}
	    }
	}
    }

    if (wPtr->anchor == chPtr) {
	wPtr->anchor = NULL;
    }
    if (wPtr->active == chPtr) {
	wPtr->active = NULL;
    }
    if (wPtr->dragSite == chPtr) {
	wPtr->dragSite = NULL;
    }
    if (wPtr->dropSite == chPtr) {
	wPtr->dropSite = NULL;
    }

    if (chPtr->iPtr != NULL) {
	if (Tix_DItemType(chPtr->iPtr) == TIX_DITEM_WINDOW) {
#if 0
	    Tix_WindowItemListRemove(&wPtr->mappedWindows,
		chPtr->iPtr);
#endif
	}
	Tix_DItemFree(chPtr->iPtr);
    }

    Tk_FreeOptions(entryConfigSpecs, (char *)chPtr,wPtr->dispData.display, 0);
    ckfree((char*)chPtr);
}

/*----------------------------------------------------------------------
 * "insert" sub command --
 *
 *	Insert a new item into the list
 *----------------------------------------------------------------------
 */
static int
Tix_TLInsert(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    ListEntry * chPtr = NULL;
    char buff[40];
    char * ditemType;
    int at;
    int added = 0;
    int code = TCL_OK;

    /*------------------------------------------------------------
     * (1) We need to determine the options:
     *     -itemtype and -at.
     *------------------------------------------------------------
     */

    /* (1.1) Find out where */
    if (Tix_TranslateIndex(wPtr, interp, objv[0], &at, 1) != TCL_OK) {
	code = TCL_ERROR; goto done;
    }

    /* (1.2) Find out the -itemtype, if specified */
    ditemType = wPtr->diTypePtr->name;   		 /* default value */
    if (argc > 1) {
	size_t len;
	int i;
	if (argc %2 != 1) {
	    Tcl_AppendResult(interp, "value for \"", argv[argc-1],
		"\" missing", NULL);
	    code = TCL_ERROR; goto done;
	}
	for (i=1; 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) Allocate a new entry
     */
    chPtr = AllocEntry(wPtr);

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

    /*
     * (3) Add the entry into the list
     */
    if (AddElement(wPtr, chPtr, at) != TCL_OK) {
	code = TCL_ERROR; goto done;
    } else {
	added = 1;
    }

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

    ResizeWhenIdle(wPtr);

  done:
    if (code == TCL_ERROR) {
	if (chPtr != NULL) {
	    if (added) {
		Tix_LinkListFindAndDelete(&entListInfo, &wPtr->entList,
		    (char*)chPtr, NULL);
	    }
	    FreeEntry(wPtr, chPtr);
	}
    } else {
	sprintf(buff, "%d", at);
	Tcl_AppendResult(interp, buff, NULL);
    }

    return code;
}

static int
Tix_TLSpecialEntryInfo(wPtr, interp, chPtr)
    WidgetPtr wPtr;
    Tcl_Interp *interp;
    ListEntry * chPtr;
{
    char buff[100];

    if (chPtr) {
	int i;
	Tix_ListIterator li;

	Tix_LinkListIteratorInit(&li);

	for (i=0,Tix_LinkListStart(&entListInfo, &wPtr->entList, &li);
	     !Tix_LinkListDone(&li);
	     Tix_LinkListNext(&entListInfo, &wPtr->entList, &li),i++) {
	    if (li.curr == (char*)chPtr) {
		break;
	    }
	}
	if (li.curr != NULL) {
	    sprintf(buff, "%d", i);
	    Tcl_AppendResult(interp, buff, NULL);
	} else {
	    panic("TList list entry is invalid");
	}
    } else {
	Tcl_ResetResult(interp);
    }
    return TCL_OK;
}

/*----------------------------------------------------------------------
 * "index" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_TLIndex(clientData, interp, argc, argv)
    ClientData clientData;	/* TList widget record. */
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    int index;
    char buff[100];

    if (Tix_TranslateIndex(wPtr, interp, objv[0], &index, 0) != TCL_OK) {
	return TCL_ERROR;
    }

    sprintf(buff, "%d", index);
    Tcl_AppendResult(interp, buff, NULL);
    return TCL_OK;
}

/*----------------------------------------------------------------------
 * "info" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_TLInfo(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]);

    if (strncmp(argv[0], "anchor", len)==0) {
	return Tix_TLSpecialEntryInfo(wPtr, interp, wPtr->anchor);
    }
    else if (strncmp(argv[0], "active", len)==0) {
	return Tix_TLSpecialEntryInfo(wPtr, interp, wPtr->active);
    }
    else if (strncmp(argv[0], "down", len)==0) {
	return Tix_TLGetNeighbor(wPtr, interp, TIX_DOWN,  argc-1, argv+1);
    }
    else if (strncmp(argv[0], "left", len)==0) {
	return Tix_TLGetNeighbor(wPtr, interp, TIX_LEFT,  argc-1, argv+1);
    }
    else if (strncmp(argv[0], "right", len)==0) {
	return Tix_TLGetNeighbor(wPtr, interp, TIX_RIGHT, argc-1, argv+1);
    }
    else if (strncmp(argv[0], "selection", len)==0) {
	ListEntry *chPtr;
	int i;
	char buffer[32];

	for (chPtr=(ListEntry*)wPtr->entList.head, i=0;
	     chPtr;
	     chPtr=chPtr->next, i++) {

	    if (chPtr->selected) {
		Tcl_IntResults(interp,1,1,i);
	    }
	}
	return TCL_OK;
    }
    else if (strncmp(argv[0], "size", len)==0) {
	char buff[100];

	sprintf(buff, "%d", wPtr->entList.numItems);
	Tcl_AppendResult(interp, buff, NULL);

	return TCL_OK;
    }
    else if (strncmp(argv[0], "up", len)==0) {
	return Tix_TLGetNeighbor(wPtr, interp, TIX_UP,    argc-1, argv+1);
    }
    else {
	Tcl_AppendResult(interp, "unknown option \"", argv[0],
	    "\": must be anchor or selection",
	    NULL);
	return TCL_ERROR;
    }
}

static int
Tix_TranslateIndex(wPtr, interp, string, index, isInsert)
    WidgetPtr wPtr;		/* TList widget record. */
    Tcl_Interp *interp;		/* Current interpreter. */
    Tcl_Obj * string;		/* String representation of the index. */
    int * index;		/* Returns the index value (0 = 1st element).*/
    int isInsert;		/* Is this function called by an "insert"
				 * operation? */
{
    if (strcmp(Tcl_GetString(string), "end") == 0) {
	*index = wPtr->entList.numItems;
    }
    else if (Tix_TLGetAt(wPtr, interp, Tcl_GetString(string), index) != TCL_OK) {
	if (Tcl_GetIntFromObj(interp, string, index) != TCL_OK) {
	    return TCL_ERROR;
	}
	else if (*index < 0) {
	    Tcl_AppendResult(interp,"expected non-negative integer but got \"",
	    	string, "\"", NULL);
	    return TCL_ERROR;
	}
    }


    /*
     * The meaning of "end" means:
     *     isInsert:wPtr->entList.numItems
     *    !isInsert:wPtr->entList.numItems-1;
     */

    if (isInsert) {
	if (*index > wPtr->entList.numItems) {
	    /*
	     * By default add it to the end, just to follow what TK
	     * does for the Listbox widget
	     */
	    *index = wPtr->entList.numItems;
	}
    } else {
	if (*index >= wPtr->entList.numItems) {
	    /*
	     * By default add it to the end, just to follow what TK
	     * does for the Listbox widget
	     */
	    *index = wPtr->entList.numItems - 1;
	}
    }

    if (*index < 0) {
	*index = 0;
    }

    return TCL_OK;
}

static int Tix_TLGetNeighbor(wPtr, interp, type, argc, argv)
    WidgetPtr wPtr;
    Tcl_Interp *interp;		/* Current interpreter. */
    int type;
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    int index;
    int dst = 0;
    int xStep, yStep;
    int numPerRow;
    char buff[100];

    if (argc != 1) {
	Tix_ArgcError(interp, argc+3, argv-3, 3, "index");
    }

    if (Tix_TranslateIndex(wPtr, interp, objv[0], &index, 0) != TCL_OK) {
	return TCL_ERROR;
    }

    if (wPtr->entList.numItems == 0) {
	Tcl_ResetResult(interp);
	return TCL_OK;
    }

    numPerRow = wPtr->rows[0].numEnt;

    if (wPtr->isVertical) {
	xStep = numPerRow;
	yStep = 1;
    } else {
	xStep = 1;
	yStep = numPerRow;
    }

    switch (type) {
      case TIX_UP:
	dst = index - yStep;
	break;
      case TIX_DOWN:
	dst = index + yStep;
	break;
      case TIX_LEFT:
	dst = index - xStep;
	break;
      case TIX_RIGHT:
	dst = index + xStep;
	break;
    }

    if (dst < 0) {
	dst = index;
    } else if (dst >= wPtr->entList.numItems) {
	dst = index;
    }

    sprintf(buff, "%d", dst);
    Tcl_AppendResult(interp, buff, NULL);

    return TCL_OK;
}



/*----------------------------------------------------------------------
 * "cget" sub command --
 *----------------------------------------------------------------------
 */
static int
Tix_TLCGet(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_TLConfig(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);
    }
}

/*----------------------------------------------------------------------
 * "geometryinfo" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_TLGeometryInfo(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];
    char string[40];
    int i;

    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;

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

#if 0
    /* FIXME */
    sprintf(string, "{%f %f} {%f %f}", first[0], last[0], first[1], last[1]);
    Tcl_AppendResult(interp, string, NULL);
#else
    /* Not quite right - one list of four rather than two lists of two */
    Tcl_DoubleResults(interp, 4, 1, first[0], last[0], first[1], last[1]);
#endif
    return TCL_OK;
}

/*----------------------------------------------------------------------
 * "delete" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_TLDelete(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    ListEntry * fromPtr, *toPtr;
    int code = TCL_OK;

    if (argc < 1 || argc > 2) {
	Tix_ArgcError(interp, argc+2, argv-2, 2, "from ?to?");
	code = TCL_ERROR;
	goto done;
    }

    if (Tix_TLGetFromTo(interp, wPtr, argc, argv, &fromPtr, &toPtr)!= TCL_OK) {
	code = TCL_ERROR;
	goto done;
    }
    if (fromPtr == NULL) {
	goto done;
    }

    if (Tix_TLDeleteRange(wPtr, fromPtr, toPtr)) {
	ResizeWhenIdle(wPtr);
    }

  done:
    return code;
}

/* returns true if some element has been deleted */
static int Tix_TLDeleteRange(wPtr, fromPtr, toPtr)
    WidgetPtr wPtr;
    ListEntry * fromPtr;
    ListEntry *toPtr;
{
    int started;
    Tix_ListIterator li;

    Tix_LinkListIteratorInit(&li);
    started = 0;
    for (Tix_LinkListStart(&entListInfo, &wPtr->entList, &li);
	 !Tix_LinkListDone(&li);
	 Tix_LinkListNext (&entListInfo, &wPtr->entList, &li)) {

	ListEntry * curr = (ListEntry *)li.curr;
	if (curr == fromPtr) {
	    started = 1;
	}
	if (started) {
	    Tix_LinkListDelete(&entListInfo, &wPtr->entList, &li);
	    FreeEntry(wPtr, curr);
	}
	if (curr == toPtr) {
	    break;
	}
    }

    return started;
}


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

    if (Tix_TLGetFromTo(interp, wPtr, 1, argv, &chPtr, &dummy)
	!= TCL_OK) {
	return TCL_ERROR;
    }

    if (chPtr == NULL) {
	Tcl_AppendResult(interp, "list entry \"", argv[0],
	    "\" does not exist", NULL);
	return TCL_ERROR;
    }

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

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

    if (Tix_TLGetFromTo(interp, wPtr, 1, argv, &chPtr, &dummy)
	!= TCL_OK) {
	return TCL_ERROR;
    }

    if (chPtr == NULL) {
	Tcl_AppendResult(interp, "list entry \"", argv[0],
	    "\" does not exist", NULL);
	return TCL_ERROR;
    }

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

/*----------------------------------------------------------------------
 * "nearest" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_TLNearest(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 posn[2];
    int index;
    char buff[100];

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

    index = Tix_TLGetNearest(wPtr, posn);
    Tcl_ResetResult(interp);

    if (index != -1) {
	sprintf(buff, "%d", index);
	Tcl_AppendResult(interp, buff, NULL);
    }
    return TCL_OK;
}

static int Tix_TLGetAt(wPtr, interp, spec, at)
    WidgetPtr wPtr;
    Tcl_Interp *interp;
    char * spec;
    int *at;
{
    int posn[2];
    char *p, *end;

    if (spec[0] != '@') {
	return TCL_ERROR;
    }

    p = spec+1;
    posn[0] = strtol(p, &end, 0);
    if ((end == p) || (*end != ',')) {
	return TCL_ERROR;
    }
    p = end+1;
    posn[1] = strtol(p, &end, 0);
    if ((end == p) || (*end != 0)) {
	return TCL_ERROR;
    }

    *at = Tix_TLGetNearest(wPtr, posn);
    return TCL_OK;
}

static int Tix_TLGetNearest(wPtr, posn)
    WidgetPtr wPtr;
    int posn[2];
{
    int i, j, index;
    int maxX, maxY;
    int r, c;

    if (wPtr->resizing) {
	ResizeNow(wPtr);
    }

    if (wPtr->entList.numItems == 0) {
	return -1;
    }

    /* clip off the position with the border of the window */

    posn[0] -= wPtr->borderWidth + wPtr->highlightWidth;
    posn[1] -= wPtr->borderWidth + wPtr->highlightWidth;

    maxX = Tk_Width (wPtr->dispData.tkwin);
    maxY = Tk_Height(wPtr->dispData.tkwin);

    maxX -= 2*(wPtr->borderWidth + wPtr->highlightWidth);
    maxY -= 2*(wPtr->borderWidth + wPtr->highlightWidth);

    if (posn[0] >= maxX) {
	posn[0] =  maxX -1;
    }
    if (posn[1] >= maxY) {
	posn[1] =  maxY -1;
    }
    if (posn[0] < 0) {
	posn[0] = 0;
    }
    if (posn[1] < 0) {
	posn[1] = 0;
    }

    i = (wPtr->isVertical == 0);
    j = (wPtr->isVertical == 1);

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

    r = posn[i] / wPtr->maxSize[i];
    c = posn[j] / wPtr->maxSize[j];

    index = (r * wPtr->rows[0].numEnt) + c;

    if (index >= wPtr->entList.numItems) {
	index = wPtr->entList.numItems - 1;
    }

    return index;
}

/*----------------------------------------------------------------------
 * "selection" sub command
 * 	Modify the selection in this HList box
 *----------------------------------------------------------------------
 */
static int
Tix_TLGetFromTo(interp, wPtr, argc, argv, fromPtr_ret, toPtr_ret)
    Tcl_Interp *interp;
    WidgetPtr wPtr;
    int argc;
    char **argv;
    ListEntry ** fromPtr_ret;
    ListEntry ** toPtr_ret;
{
    /*
     * ToDo: make it more efficient by saving the previous from and to
     * pointers and make the list of childrens a doubly-linked list
     */
    ListEntry * fromPtr;
    ListEntry * toPtr;
    int from, to, tmp;

    if (Tix_TranslateIndex(wPtr, interp, objv[0], &from, 0) != TCL_OK) {
	return TCL_ERROR;
    }
    if (argc == 2) {
	if (Tix_TranslateIndex(wPtr, interp, objv[1], &to, 0) != TCL_OK) {
	    return TCL_ERROR;
	}
    } else {
	to = from;
    }

    if (from > to) {
	/* swap from and to */
	tmp = to; to = from; from = tmp;
    }

    fromPtr = NULL;
    toPtr   = NULL;

    if (from >= wPtr->entList.numItems) {
	fromPtr = (ListEntry *)wPtr->entList.tail;
	toPtr   = (ListEntry *)wPtr->entList.tail;
    }
    if (to >= wPtr->entList.numItems) {
	toPtr   = (ListEntry *)wPtr->entList.tail;
    }

    if (fromPtr == NULL) {
	for (fromPtr = (ListEntry*)wPtr->entList.head;
	     from > 0;
	     fromPtr=fromPtr->next) {

	    -- from;
	    -- to;
	}
    }
    if (toPtr == NULL) {
	for (toPtr = fromPtr; to > 0; toPtr=toPtr->next) {
	    -- to;
	}
    }

    * fromPtr_ret = fromPtr;
    if (toPtr_ret) {
	* toPtr_ret = toPtr;
    }
    return TCL_OK;
}

static int
Tix_TLSelection(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 code = TCL_OK;
    int changed = 0;
    ListEntry * chPtr, * fromPtr, * toPtr;

    if (strncmp(argv[0], "clear", len)==0) {
	if (argc == 1) {
	    /*
	     * Clear all entries
	     */
	    for (chPtr=(ListEntry*)wPtr->entList.head;
		 chPtr;
		 chPtr=chPtr->next) {

		chPtr->selected = 0;
	    }
	    changed = 1;
	}
	else {
	    if (Tix_TLGetFromTo(interp, wPtr, argc-1, argv+1, &fromPtr, &toPtr)
		    != TCL_OK) {
		code = TCL_ERROR;
		goto done;
	    }
	    if (fromPtr == NULL) {
		goto done;
	    }
	    else {
		while (1) {
		    fromPtr->selected = 0;
		    if (fromPtr == toPtr) {
			break;
		    } else {
			fromPtr=fromPtr->next;
		    }
		}
		changed = 1;
		goto done;
	    }
	}
    }
    else if (strncmp(argv[0], "includes", len)==0) {
	if (argc != 2) {
	    Tix_ArgcError(interp, argc+2, argv-2, 3, "index");
	    code = TCL_ERROR;
	    goto done;
	}
	if (Tix_TLGetFromTo(interp, wPtr, argc-1, argv+1, &fromPtr, &toPtr)
		!= TCL_OK) {
	    code = TCL_ERROR;
	    goto done;
	}
	if (fromPtr->selected) {
	    Tcl_AppendResult(interp, "1", NULL);
	} else {
	    Tcl_AppendResult(interp, "0", NULL);
	}
    }
    else if (strncmp(argv[0], "set", len)==0) {
	if (argc < 2 || argc > 3) {
	    Tix_ArgcError(interp, argc+2, argv-2, 3, "from ?to?");
	    code = TCL_ERROR;
	    goto done;
	}

	if (Tix_TLGetFromTo(interp, wPtr, argc-1, argv+1, &fromPtr, &toPtr)
		!= TCL_OK) {
	    code = TCL_ERROR;
	    goto done;
	}
	if (fromPtr == NULL) {
	    goto done;
	}
	else {
	    while (1) {
		fromPtr->selected = 1;
		if (fromPtr == toPtr) {
		    break;
		} else {
		    fromPtr=fromPtr->next;
		}
	    }
	    changed = 1;
	    goto done;
	}
    }
    else {
	Tcl_AppendResult(interp, "unknown option \"", argv[0],
	    "\": must be anchor, clear, includes or set", NULL);
	code = TCL_ERROR;
    }

  done:
    if (changed) {
	RedrawWhenIdle(wPtr);
    }

    return code;
}

/*----------------------------------------------------------------------
 * "see" command
 *----------------------------------------------------------------------
 */
static int
Tix_TLSee(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    ListEntry * chPtr, * dummy;

    if (argc == 1) {
	if (Tix_TLGetFromTo(interp, wPtr, 1, argv, &chPtr, &dummy) != TCL_OK) {
	    return TCL_ERROR;
	}
	if (chPtr != NULL) {
	    wPtr->seeElemPtr = chPtr;
	    RedrawWhenIdle(wPtr);
	}
    } else {
	Tcl_AppendResult(interp, "wrong # of arguments, must be: ",
		Tk_PathName(wPtr->dispData.tkwin), " ", argv[-1],
		" index", NULL);
    }

    return TCL_OK;
}

/*----------------------------------------------------------------------
 * "anchor", "dragsite" and "dropsite" sub commands --
 *
 *	Set/remove the anchor element
 *----------------------------------------------------------------------
 */
static int
Tix_TLSetSite(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;
    ListEntry * fromPtr;
    ListEntry * toPtr;			/* unused */
    ListEntry ** changePtr;
    size_t len;

    /* 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], "active", len)==0) {
	changePtr = &wPtr->active;
    }
    else if (strncmp(argv[-1], "dragsite", len)==0) {
	changePtr = &wPtr->dragSite;
    }
    else {
	changePtr = &wPtr->dropSite;
    }

    len = strlen(argv[0]);
    if (strncmp(argv[0], "set", len)==0) {
	if (argc == 2) {
	    if (Tix_TLGetFromTo(interp,wPtr, argc-1, argv+1, &fromPtr, &toPtr)
		    != TCL_OK) {
		return TCL_ERROR;
	    }
	    if (*changePtr != fromPtr) {
		*changePtr = fromPtr;
		changed = 1;
	    }
	} else {
	    Tcl_AppendResult(interp, "wrong # of arguments, must be: ",
		Tk_PathName(wPtr->dispData.tkwin), " ", argv[-1],
		" set index", NULL);
	    return TCL_ERROR;
	}
    }
    else if (strncmp(argv[0], "clear", len)==0) {
	if (*changePtr != NULL) {
	    *changePtr = NULL;
	    changed = 1;
	}
    }
    else {
	Tcl_AppendResult(interp, "wrong option \"", argv[0], "\", ",
	    "must be clear or set", NULL);
	return TCL_ERROR;
    }

    if (changed) {
	RedrawWhenIdle(wPtr);
    }

    return TCL_OK;
}

/*----------------------------------------------------------------------
 * "xview" and "yview" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_TLView(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;

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

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

	Tix_GetScrollFractions((Tix_ScrollInfo*)&wPtr->scrollInfo[axis],
	    &first, &last);

#if 0
	sprintf(string, "{%f %f}", first, last);
	Tcl_AppendResult(interp, string, NULL);
#else
	Tcl_DoubleResults(interp, 2, 1, first, last);
#endif
	return TCL_OK;
    }
    else if (Tix_SetScrollBarView(interp,
	(Tix_ScrollInfo*)&wPtr->scrollInfo[axis], argc, argv, 0) != TCL_OK) {

	return TCL_ERROR;
    }

    UpdateScrollBars(wPtr, 0);
    RedrawWhenIdle(wPtr);
    return TCL_OK;
}
/*----------------------------------------------------------------------
 *
 *
 * 			Memory Management Section
 *
 *
 *----------------------------------------------------------------------
 */
static int
ConfigElement(wPtr, chPtr, argc, argv, flags, forced)
    WidgetPtr wPtr;
    ListEntry *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) {
	chPtr->size[0] = chPtr->iPtr->base.size[0];
	chPtr->size[1] = chPtr->iPtr->base.size[1];
	ResizeWhenIdle(wPtr);
    } else {
	RedrawWhenIdle(wPtr);
    }
    return TCL_OK;
}

static void
Realloc(wPtr, new_size)
    WidgetPtr wPtr;
    int new_size;
{
    if (new_size < 1) {
	new_size = 1;
    }
    if (new_size == wPtr->numRowAllocd) {
	return;
    }
    wPtr->rows = (ListRow*)ckrealloc( (char *)wPtr->rows,
                                      sizeof(ListRow)*new_size);
    wPtr->numRowAllocd = new_size;
}

static void ResizeRows(wPtr, winW, winH)
    WidgetPtr wPtr;
    int winW;			/* -1 == current width */
    int winH;			/* -1 == current height */
{
    ListEntry * chPtr;
    ListEntry * rowHead;
    int n, c, r;
    int maxI;			/* max of width in the current column */
    int maxJ;			/* max of height among all elements */
    int curRow;
    int i, j;
    int sizeJ;
    int winSize[2];

    if (wPtr->isVertical) {
	i = 0;		/* Column major, 0,0 -> 0,1 -> 0,2 ... -> 1,0 */
	j = 1;
    } else {
	i = 1;		/* Row major, 0,0 -> 1,0 -> 2,0 ... -> 0,1 */
	j = 0;
    }

    if (winW == -1) {
	winW = Tk_Width (wPtr->dispData.tkwin);
    }
    if (winH == -1) {
	winH = Tk_Height(wPtr->dispData.tkwin);
    }

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

    if (wPtr->entList.numItems == 0) {
	wPtr->rows[0].chPtr = NULL;
	wPtr->rows[0].size[0] = 1;
	wPtr->rows[0].size[1] = 1;
	wPtr->rows[0].numEnt = 0;

	wPtr->numRow = 1;
	goto done;
    }

    /* 	    --  The following verbal description follows the "Column Major"
     *     	model. Row major are similar, just the i,j incides are swapped
     *
     * (1) (a) Search for the tallest element, use it as the height of all
     *         the elements;
     *     (b) Search for the widest element, use it as the width of all
     *         the elements;
     */
    for (maxJ=1, maxI=1, chPtr = (ListEntry*)wPtr->entList.head;
	 chPtr;
	 chPtr=chPtr->next) {

	if (maxJ < chPtr->iPtr->base.size[j]) {
	    maxJ = chPtr->iPtr->base.size[j];
	}
	if (maxI < chPtr->iPtr->base.size[i]) {
	    maxI = chPtr->iPtr->base.size[i];
	}
    }
    wPtr->maxSize[i] = maxI;
    wPtr->maxSize[j] = maxJ;

    /* (2) Calculate how many elements can be in each column
     *
     */
    n = winSize[j] / maxJ;
    if (n <=0) {
	n = 1;
    }

    wPtr->numRow = 0;
    curRow = 0;
    c = 0;
    sizeJ = 0;
    rowHead = (ListEntry*)wPtr->entList.head;
    for(chPtr = (ListEntry*)wPtr->entList.head; chPtr; chPtr=chPtr->next) {
	sizeJ += chPtr->iPtr->base.size[j];
	++ c;
	if (c == n || chPtr->next == NULL) {
	    if (curRow >= wPtr->numRowAllocd) {
		Realloc(wPtr, curRow*2);
	    }
	    wPtr->rows[curRow].chPtr   = rowHead;
	    wPtr->rows[curRow].size[i] = maxI;
	    wPtr->rows[curRow].size[j] = sizeJ;
	    wPtr->rows[curRow].numEnt  = c;
	    ++ curRow;
	    ++ wPtr->numRow;
	    c = 0;
	    rowHead = chPtr->next;
	    sizeJ = 0;
	}
    }

  done:
    /* calculate the size of the total and visible area */
    wPtr->scrollInfo[i].total = 0;
    wPtr->scrollInfo[j].total = 0;

    for (r=0; r<wPtr->numRow; r++) {
	wPtr->scrollInfo[i].total += wPtr->rows[r].size[i];
	if (wPtr->scrollInfo[j].total < wPtr->rows[r].size[j]) {
	    wPtr->scrollInfo[j].total = wPtr->rows[r].size[j];
	}
    }

    wPtr->scrollInfo[i].window = winSize[i];
    wPtr->scrollInfo[j].window = winSize[j];

    if (wPtr->scrollInfo[i].total < 1) {
	wPtr->scrollInfo[i].total = 1;
    }
    if (wPtr->scrollInfo[j].total < 1) {
	wPtr->scrollInfo[j].total = 1;
    }
    if (wPtr->scrollInfo[i].window < 1) {
	wPtr->scrollInfo[i].window = 1;
    }
    if (wPtr->scrollInfo[j].window < 1) {
	wPtr->scrollInfo[j].window = 1;
    }

    /* If we have much fewer rows now, adjust the size of the rows list */
    if (wPtr->numRowAllocd > (2*wPtr->numRow)) {
	Realloc(wPtr, 2*wPtr->numRow);
    }

    /* Update the scrollbars */

    UpdateScrollBars(wPtr, 1);
}
/*----------------------------------------------------------------------
 * RedrawRows --
 *
 *	Redraw the rows, according to the "offset: in both directions
 *----------------------------------------------------------------------
 */

static void
RedrawRows(wPtr, pixmap)
    WidgetPtr wPtr;
    Drawable pixmap;
{
    int r, n;
    int p[2];
    ListEntry * chPtr;
    int i, j;
    int total;
    int windowSize;

    if (wPtr->entList.numItems == 0) {
	return;
    }

    if (wPtr->isVertical) {
	i = 0;		/* Column major, 0,0 -> 0,1 -> 0,2 ... -> 1,0 */
	j = 1;
	windowSize = Tk_Width(wPtr->dispData.tkwin);
    } else {
	i = 1;		/* Row major, 0,0 -> 1,0 -> 2,0 ... -> 0,1 */
	j = 0;
	windowSize = Tk_Height(wPtr->dispData.tkwin);
    }

    p[i] = wPtr->highlightWidth + wPtr->borderWidth;
    windowSize -= 2*p[i];

    if (windowSize < 1) {
	windowSize = 1;
    }

    if (wPtr->seeElemPtr != NULL) {
	/*
	 * Adjust the scrolling so that the given entry is visible.
	 */
	int start = 0;		/* x1 position of the element to see. */
	int size = 0;		/* width of the element to see. */
	int old = wPtr->scrollInfo[i].offset;

	for (r=0, n=0, chPtr=(ListEntry*)wPtr->entList.head; chPtr;
		chPtr=chPtr->next, n++) {
	    if (n == wPtr->rows[r].numEnt) {
		n=0;
		r++;
		start += wPtr->rows[r].size[i];
	    }
	    if (chPtr == wPtr->seeElemPtr) {
		size = wPtr->rows[r].size[i];
		break;
	    }
	}

	if ((wPtr->scrollInfo[i].offset + windowSize) < (start + size)) {
	    wPtr->scrollInfo[i].offset = start + size - windowSize;
	}
	if (wPtr->scrollInfo[i].offset > start) {
	    wPtr->scrollInfo[i].offset = start;
	}
	if (wPtr->scrollInfo[i].offset != old) {
	    UpdateScrollBars(wPtr, 0);
	}
	wPtr->seeElemPtr = NULL;
    }

    /* Search for a row that is (possibly partially) visible*/
    total=0; r=0;
    if (wPtr->scrollInfo[i].offset != 0) {
	for (; r<wPtr->numRow; r++) {
	    total += wPtr->rows[r].size[i];

	    if (total > wPtr->scrollInfo[i].offset) {
		p[i] -= wPtr->scrollInfo[i].offset -
		        (total - wPtr->rows[r].size[i]);
		break;
	    }
	    if (total == wPtr->scrollInfo[i].offset) {
		r++;
		break;
	    }
	}
    }

    /* Redraw all the visible rows */
    for (; r<wPtr->numRow; r++) {

	p[j] = wPtr->highlightWidth + wPtr->borderWidth;

	total=0; n=0; chPtr=wPtr->rows[r].chPtr;
	if (wPtr->scrollInfo[j].offset > 0)  {
	    /* Search for a column that is (possibly partially) visible*/
	    for (;
		 n<wPtr->rows[r].numEnt;
		 n++, chPtr = chPtr->next) {

		total += chPtr->iPtr->base.size[j];
		if (total > wPtr->scrollInfo[j].offset) {
		    /* Adjust for the shift due to partially visible elements*/
		    p[j] -= wPtr->scrollInfo[j].offset -
		      (total - chPtr->iPtr->base.size[j]);
		    break;
		}
		if (total == wPtr->scrollInfo[j].offset) {
		    n++; chPtr = chPtr->next;
		    break;
		}
	    }
	}

	/* Redraw all the visible columns in this row */
	for (; n<wPtr->rows[r].numEnt; n++, chPtr = chPtr->next) {
	    int flags = TIX_DITEM_NORMAL_FG;
	    int W, H;

	    if (chPtr->selected) {
		flags |= TIX_DITEM_SELECTED_FG;
		flags |= TIX_DITEM_SELECTED_BG;
	    }

#if 0
	    if (wPtr->isVertical) {
		W = wPtr->rows[r].size[0];
		H = chPtr->iPtr->base.size[1];
	    } else {
		H = wPtr->rows[r].size[1];
		W = chPtr->iPtr->base.size[0];
	    }
#else
	    W = wPtr->maxSize[0];
	    H = wPtr->maxSize[1];
#endif

	    Tix_DItemDisplay(pixmap, None, chPtr->iPtr, p[0], p[1], W, H,
		flags);

	    if (chPtr == wPtr->anchor) {
		Tix_DrawAnchorLines(Tk_Display(wPtr->dispData.tkwin), pixmap,
	            wPtr->anchorGC, p[0], p[1], W-1, H-1);
	    }
	    p[j] += wPtr->maxSize[j];
	}

	/* advance to the next row */
	p[i]+= wPtr->rows[r].size[i];
    }
}

/*----------------------------------------------------------------------
 *  UpdateScrollBars
 *----------------------------------------------------------------------
 */
static void UpdateScrollBars(wPtr, sizeChanged)
    WidgetPtr wPtr;
    int sizeChanged;
{
    Tix_UpdateScrollBar(wPtr->dispData.interp,
	(Tix_ScrollInfo*)&wPtr->scrollInfo[0]);
    Tix_UpdateScrollBar(wPtr->dispData.interp,
	(Tix_ScrollInfo*)&wPtr->scrollInfo[1]);

    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 tixTList)");
	    Tcl_BackgroundError(wPtr->dispData.interp);
	}
    }
}