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

/*	$Id: tixHList.c,v 1.3 2000/10/17 16:38:11 idiscovery Exp $	*/

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

/*
 * Information used for argv parsing.
 */

static Tk_ConfigSpec configSpecs[] = {
    {TK_CONFIG_COLOR, "-background", "background", "Background",
       DEF_HLIST_BG_COLOR, Tk_Offset(WidgetRecord, normalBg),
       TK_CONFIG_COLOR_ONLY},

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

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

    {TK_CONFIG_INT, "-columns", "columns", "Columns",
       DEF_HLIST_COLUMNS, Tk_Offset(WidgetRecord, numColumns),
       TK_CONFIG_NULL_OK},

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

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

    {TK_CONFIG_CALLBACK, "-dragcmd", "dragCmd", "DragCmd",
	DEF_HLIST_DRAG_COMMAND, Tk_Offset(WidgetRecord, dragCmd),
	TK_CONFIG_NULL_OK},

    {TK_CONFIG_BOOLEAN, "-drawbranch", "drawBranch", "DrawBranch",
       DEF_HLIST_DRAW_BRANCH, Tk_Offset(WidgetRecord, drawBranch), 0},

    {TK_CONFIG_CALLBACK, "-dropcmd", "dropCmd", "DropCmd",
	DEF_HLIST_DROP_COMMAND, Tk_Offset(WidgetRecord, dropCmd),
	TK_CONFIG_NULL_OK},

    {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
	"ExportSelection", DEF_HLIST_EXPORT_SELECTION,
	Tk_Offset(WidgetRecord, exportSelection), 0},

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

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

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

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

    {TK_CONFIG_PIXELS, "-gap", "gap", "Gap",
       DEF_HLIST_GAP, Tk_Offset(WidgetRecord, gap), 0},

    {TK_CONFIG_BOOLEAN, "-header", "header", "Header",
       DEF_HLIST_HEADER, Tk_Offset(WidgetRecord, useHeader), 0},

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

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

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

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

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

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

    {TK_CONFIG_PIXELS, "-indent", "indent", "Indent",
       DEF_HLIST_INDENT, Tk_Offset(WidgetRecord, indent), 0},

    {TK_CONFIG_BOOLEAN, "-indicator", "indicator", "Indicator",
       DEF_HLIST_INDICATOR, Tk_Offset(WidgetRecord, useIndicator), 0},

    {TK_CONFIG_CALLBACK, "-indicatorcmd", "indicatorCmd", "IndicatorCmd",
	DEF_HLIST_INDICATOR_CMD, Tk_Offset(WidgetRecord, indicatorCmd),
	TK_CONFIG_NULL_OK},

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

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

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

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

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

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

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

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

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

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

    {TK_CONFIG_STRING, "-separator", "separator", "Separator",
       DEF_HLIST_SEPARATOR, Tk_Offset(WidgetRecord, separator), 0},

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

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

    {TK_CONFIG_BOOLEAN, "-wideselection", "wideSelection", "WideSelection",
       DEF_HLIST_WIDE_SELECT, Tk_Offset(WidgetRecord, wideSelect), 0},

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

    {TK_CONFIG_CALLBACK, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
	DEF_HLIST_X_SCROLL_COMMAND, Tk_Offset(WidgetRecord, xScrollCmd),
	TK_CONFIG_NULL_OK},

    {TK_CONFIG_CALLBACK, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
	DEF_HLIST_Y_SCROLL_COMMAND, Tk_Offset(WidgetRecord, yScrollCmd),
	TK_CONFIG_NULL_OK},

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

static Tk_ConfigSpec entryConfigSpecs[] = {
    {TK_CONFIG_LANGARG, "-data", (char *) NULL, (char *) NULL,
       DEF_HLISTENTRY_DATA, Tk_Offset(HListElement, data), TK_CONFIG_NULL_OK},

    {TK_CONFIG_UID, "-state", (char*)NULL, (char*)NULL,
       DEF_HLISTENTRY_STATE, Tk_Offset(HListElement, state), 0},

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

/*
 * Forward declarations for procedures defined later in this file:
 */
	/* These are standard procedures for TK widgets
	 * implemeted in C
	 */

static void		WidgetCmdDeletedProc _ANSI_ARGS_((
			    ClientData clientData));
static int		WidgetConfigure _ANSI_ARGS_((Tcl_Interp *interp,
			    WidgetPtr wPtr, int argc, char **argv,
			    int flags));
static 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));

	/* Extra procedures for this widget
	 */
static HListElement *	AllocElement _ANSI_ARGS_((WidgetPtr wPtr,
			    HListElement * parent, char * pathName,
			    char * name, char * ditemType));
static void		AppendList _ANSI_ARGS_((WidgetPtr wPtr,
			    HListElement *parent, HListElement *chPtr, int at,
			    HListElement *afterPtr,
			    HListElement *beforePtr));
static void		CancelRedrawWhenIdle _ANSI_ARGS_((
			    WidgetPtr wPtr));
static void		CheckScrollBar _ANSI_ARGS_((WidgetPtr wPtr,
			    int which));
static void		ComputeBranchPosition _ANSI_ARGS_((
			    WidgetPtr wPtr, HListElement *chPtr));
static void		ComputeElementGeometry _ANSI_ARGS_((WidgetPtr wPtr,
			    HListElement *chPtr, int indent));
static void		ComputeOneElementGeometry _ANSI_ARGS_((WidgetPtr wPtr,
			    HListElement *chPtr, int indent));
static int		ConfigElement _ANSI_ARGS_((WidgetPtr wPtr,
			    HListElement *chPtr, int argc, char ** argv,
			    int flags, int forced));
static int		CurSelection _ANSI_ARGS_((Tcl_Interp * interp,
			    WidgetPtr wPtr, HListElement * chPtr));
static void		DeleteNode _ANSI_ARGS_((WidgetPtr wPtr,
			    HListElement * chPtr));
static void		DeleteOffsprings _ANSI_ARGS_((WidgetPtr wPtr,
			    HListElement * chPtr));
static void		DeleteSiblings _ANSI_ARGS_((WidgetPtr wPtr,
			    HListElement * chPtr));
static void		DrawElements _ANSI_ARGS_((WidgetPtr wPtr,
			    Pixmap pixmap, GC gc, HListElement * chPtr,
			    int x, int y, int xOffset));
static void		DrawOneElement _ANSI_ARGS_((WidgetPtr wPtr,
			    Pixmap pixmap, GC gc, HListElement * chPtr,
			    int x, int y, int xOffset));
static HListElement *	FindElementAtPosition _ANSI_ARGS_((WidgetPtr wPtr,
			    int y));
static HListElement *	FindNextEntry  _ANSI_ARGS_((WidgetPtr wPtr,
			    HListElement * chPtr));
static HListElement *	FindPrevEntry  _ANSI_ARGS_((WidgetPtr wPtr,
			    HListElement * chPtr));
static void		FreeElement _ANSI_ARGS_((WidgetPtr wPtr,
			    HListElement * chPtr));
static HListElement *	NewElement _ANSI_ARGS_((Tcl_Interp *interp,
			    WidgetPtr wPtr, int argc, char ** argv,
			    char * pathName, char * defParentName,
			    int * newArgc, Tcl_Obj *** newArgv));
static void		RedrawWhenIdle _ANSI_ARGS_((WidgetPtr wPtr));
static int		XScrollByPages _ANSI_ARGS_((WidgetPtr wPtr,
			    int count));
static int		XScrollByUnits _ANSI_ARGS_((WidgetPtr wPtr,
			    int count));
static int		YScrollByPages _ANSI_ARGS_((WidgetPtr wPtr,
			    int count));
static int		YScrollByUnits _ANSI_ARGS_((WidgetPtr wPtr,
			    int count));
static int		SelectionModifyRange _ANSI_ARGS_((WidgetPtr wPtr,
			    HListElement * from, HListElement * to,
			    int select));
static void		SelectionAdd _ANSI_ARGS_((WidgetPtr wPtr,
			    HListElement * chPtr));
static void		HL_SelectionClear _ANSI_ARGS_((WidgetPtr wPtr,
			    HListElement * chPtr));
static void		HL_SelectionClearAll _ANSI_ARGS_((WidgetPtr wPtr,
			    HListElement * chPtr, int *changed_ret));
static void		HL_SelectionClearNotifyAncestors _ANSI_ARGS_((
			    WidgetPtr wPtr, HListElement * chPtr));
static void		SelectionNotifyAncestors _ANSI_ARGS_((
			    WidgetPtr wPtr, HListElement * chPtr));
static void		UpdateOneScrollBar _ANSI_ARGS_((WidgetPtr wPtr,
			    LangCallback *command, int total, int window, int first));
static void		UpdateScrollBars _ANSI_ARGS_((WidgetPtr wPtr,
			    int sizeChanged));
static void		DItemSizeChangedProc _ANSI_ARGS_((
			    Tix_DItem *iPtr));
static void		SubWindowEventProc _ANSI_ARGS_((ClientData clientData,
			    XEvent *eventPtr));
static void		GetScrollFractions _ANSI_ARGS_((int total,
			    int window, int first, double * first_ret,
			    double * last_ret));
static int		Tix_HLSeeElement _ANSI_ARGS_((
			    WidgetPtr wPtr, HListElement * chPtr,
			    int callRedraw));
static int		Tix_HLBBox _ANSI_ARGS_((Tcl_Interp * interp,
			    WidgetPtr wPtr, HListElement * chPtr));
static void		GetSelectedText _ANSI_ARGS_((WidgetPtr wPtr,
			    HListElement * chPtr, Tcl_DString * selection));

static int		HListFetchSelection _ANSI_ARGS_((
			    ClientData clientData, int offset, char *buffer,
			    int maxBytes));
static void		HListLostSelection _ANSI_ARGS_((ClientData clientData));

static TIX_DECLARE_SUBCMD(Tix_HLAdd);
static TIX_DECLARE_SUBCMD(Tix_HLAddChild);
static TIX_DECLARE_SUBCMD(Tix_HLCGet);
static TIX_DECLARE_SUBCMD(Tix_HLConfig);
static TIX_DECLARE_SUBCMD(Tix_HLDelete);
static TIX_DECLARE_SUBCMD(Tix_HLEntryCget);
static TIX_DECLARE_SUBCMD(Tix_HLEntryConfig);
static TIX_DECLARE_SUBCMD(Tix_HLGeometryInfo);
static TIX_DECLARE_SUBCMD(Tix_HLHide);
static TIX_DECLARE_SUBCMD(Tix_HLInfo);
static TIX_DECLARE_SUBCMD(Tix_HLNearest);
static TIX_DECLARE_SUBCMD(Tix_HLSee);
static TIX_DECLARE_SUBCMD(Tix_HLSelection);
static TIX_DECLARE_SUBCMD(Tix_HLSetSite);
static TIX_DECLARE_SUBCMD(Tix_HLShow);
static TIX_DECLARE_SUBCMD(Tix_HLXView);
static TIX_DECLARE_SUBCMD(Tix_HLYView);



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

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

    /*
     * Allocate the main window for this window. Then allocate a subwindow
     * to act as the header. The subwidget will always be raised to the top
     * so that it won't be obscured by any window items
     */
    tkwin = Tk_CreateWindowFromPath(interp, mainw, argv[1], (char *) NULL);
    if (tkwin == NULL) {
	return TCL_ERROR;
    }
    subwin = Tix_CreateSubWindow(interp, tkwin, "header");
    if (subwin == NULL) {
	Tk_DestroyWindow(tkwin);
	return TCL_ERROR;
    }

    Tk_SetClass(tkwin, "TixHList");
    Tk_SetClass(subwin, "TixHListHeader");

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

    /* Init the hash table first (needed before calling AllocElement) */
    Tcl_InitHashTable(&wPtr->childTable, TCL_STRING_KEYS);

    wPtr->dispData.tkwin	= tkwin;
    wPtr->dispData.display	= Tk_Display(tkwin);
    wPtr->dispData.interp	= interp;
    wPtr->dispData.sizeChangedProc = DItemSizeChangedProc;
    wPtr->font			= NULL;
    wPtr->normalBg		= NULL;
    wPtr->normalFg		= NULL;
    wPtr->border		= NULL;
    wPtr->borderWidth		= 0;
    wPtr->selectBorder		= NULL;
    wPtr->selBorderWidth	= 0;
    wPtr->selectFg		= NULL;
    wPtr->backgroundGC		= None;
    wPtr->normalGC		= None;
    wPtr->selectGC		= None;
    wPtr->anchorGC		= None;
    wPtr->dropSiteGC		= None;
    wPtr->highlightWidth	= 0;
    wPtr->highlightColorPtr	= NULL;
    wPtr->highlightGC		= None;
    wPtr->relief		= TK_RELIEF_FLAT;
    wPtr->cursor		= None;
    wPtr->indent		= 0;
    wPtr->resizing		= 0;
    wPtr->redrawing		= 0;
    wPtr->hasFocus		= 0;
    wPtr->topPixel		= 0;
    wPtr->leftPixel		= 0;
    wPtr->separator		= NULL;
    wPtr->selectMode		= NULL;
    wPtr->anchor		= NULL;
    wPtr->dragSite		= NULL;
    wPtr->dropSite		= NULL;
    wPtr->command		= NULL;
    wPtr->browseCmd		= NULL;
    wPtr->sizeCmd		= NULL;
    wPtr->dragCmd		= NULL;
    wPtr->dropCmd		= NULL;
    wPtr->takeFocus		= NULL;
    wPtr->xScrollCmd		= NULL;
    wPtr->yScrollCmd		= NULL;
    wPtr->scrollUnit[0]		= 1;
    wPtr->scrollUnit[1]		= 1;
    wPtr->serial		= 0;
    wPtr->numColumns		= 1;
    wPtr->initialized		= 0;
    wPtr->allDirty		= 0;
    wPtr->headerDirty		= 0;
    wPtr->needToRaise		= 0;
    wPtr->drawBranch		= 1;
    wPtr->wideSelect		= 0;
    wPtr->diTypePtr		= NULL;
    wPtr->reqSize		= NULL;
    wPtr->actualSize		= NULL;
    wPtr->root			= NULL;
    wPtr->totalSize[0]		= 1;
    wPtr->totalSize[1]		= 1;
    wPtr->useIndicator		= 0;
    wPtr->indicatorCmd		= NULL;
    wPtr->headers		= NULL;
    wPtr->useHeader		= 0;
    wPtr->headerHeight		= 0;
    wPtr->headerWin		= subwin;
    wPtr->elmToSee		= 0;

    Tix_LinkListInit(&wPtr->mappedWindows);

    Tk_CreateEventHandler(wPtr->dispData.tkwin,
	ExposureMask|StructureNotifyMask|FocusChangeMask,
	WidgetEventProc, (ClientData) wPtr);
    Tk_CreateEventHandler(wPtr->headerWin,
	ExposureMask|StructureNotifyMask,
	SubWindowEventProc, (ClientData) wPtr);
    Tk_CreateSelHandler(wPtr->dispData.tkwin, XA_PRIMARY, XA_STRING,
	HListFetchSelection, (ClientData) wPtr, XA_STRING);

    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;
    }
    if (Tix_HLCreateHeaders(interp, wPtr) != TCL_OK) {
	Tk_DestroyWindow(wPtr->dispData.tkwin);
	return TCL_ERROR;
    }

    /* Must call this **after** wPtr->numColumns is set */
    wPtr->reqSize    = Tix_HLAllocColumn(wPtr, NULL);
    wPtr->actualSize = Tix_HLAllocColumn(wPtr, NULL);
    wPtr->root	     = AllocElement(wPtr, 0, 0, 0, 0);

    wPtr->initialized = 1;

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


/*
 *--------------------------------------------------------------
 *
 * WidgetCommand --
 *
 *	This procedure is invoked to process the Tcl command
 *	that corresponds to a widget managed by this module.
 *	See the user documentation for details on what it does.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the user documentation.
 *
 *--------------------------------------------------------------
 */

static int
WidgetCommand(clientData, interp, argc, argv)
    ClientData clientData;		/* Information about the widget. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
    int code;

    static Tix_SubCmdInfo subCmdInfo[] = {
	{TIX_DEFAULT_LEN, "add", 1, TIX_VAR_ARGS, Tix_HLAdd,
	   "entryPath"},
	{TIX_DEFAULT_LEN, "addchild", 1, TIX_VAR_ARGS, Tix_HLAddChild,
	   "parentEntryPath"},
	{TIX_DEFAULT_LEN, "anchor", 1, 2, Tix_HLSetSite,
	   "option ?entryPath?"},
	{TIX_DEFAULT_LEN, "cget", 1, 1, Tix_HLCGet,
	   "option"},
	{TIX_DEFAULT_LEN, "column", 0, TIX_VAR_ARGS, Tix_HLColumn,
	   "?option? ?args ...?"},
	{TIX_DEFAULT_LEN, "configure", 0, TIX_VAR_ARGS, Tix_HLConfig,
	   "?option? ?value? ?option value ... ?"},
	{TIX_DEFAULT_LEN, "delete", 1, 2, Tix_HLDelete,
	   "option ?entryPath?"},
	{TIX_DEFAULT_LEN, "dragsite", 1, 2, Tix_HLSetSite,
	   "option ?entryPath?"},
	{TIX_DEFAULT_LEN, "dropsite", 1, 2, Tix_HLSetSite,
	   "option ?entryPath?"},
	{TIX_DEFAULT_LEN, "entrycget", 2, 2, Tix_HLEntryCget,
	   "entryPath option"},
	{TIX_DEFAULT_LEN, "entryconfigure", 1, TIX_VAR_ARGS, Tix_HLEntryConfig,
	   "entryPath ?option? ?value? ?option value ... ?"},
	{TIX_DEFAULT_LEN, "geometryinfo", 0, 2, Tix_HLGeometryInfo,
	   "?width height?"},
	{TIX_DEFAULT_LEN, "header", 1, TIX_VAR_ARGS, Tix_HLHeader,
	   "option ?args ...?"},
	{TIX_DEFAULT_LEN, "hide", 2, 2, Tix_HLHide,
	   "option entryPath"},
	{TIX_DEFAULT_LEN, "item", 0, TIX_VAR_ARGS, Tix_HLItem,
	   "?option? ?args ...?"},
	{TIX_DEFAULT_LEN, "indicator", 1, TIX_VAR_ARGS, Tix_HLIndicator,
	   "option ?args ...?"},
	{TIX_DEFAULT_LEN, "info", 1, TIX_VAR_ARGS, Tix_HLInfo,
	   "option ?args ...?"},
	{TIX_DEFAULT_LEN, "nearest", 1, 1, Tix_HLNearest,
	   "y"},
	{TIX_DEFAULT_LEN, "see", 1, 1, Tix_HLSee,
	   "entryPath"},
	{TIX_DEFAULT_LEN, "selection", 1, 3, Tix_HLSelection,
	   "option arg ?arg ...?"},
	{TIX_DEFAULT_LEN, "show", 2, 2, Tix_HLShow,
	   "option entryPath"},
	{TIX_DEFAULT_LEN, "xview", 0, 3, Tix_HLXView,
	   "args"},
	{TIX_DEFAULT_LEN, "yview", 0, 3, Tix_HLYView,
	   "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;
}

/*----------------------------------------------------------------------
 * "add" sub command --
 *
 *	Add a new item into the list
 *----------------------------------------------------------------------
 */
static int
Tix_HLAdd(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    HListElement * chPtr;
    char * pathName = argv[0];
    int code = TCL_ERROR;
    Tcl_Obj ** newArgv = NULL;
    int newArgc = 0;

    argc --;
    argv ++;

    if ((chPtr = NewElement(interp, wPtr, argc, argv, pathName,
	 NULL, &newArgc, &newArgv)) == NULL) {
	goto cleanup;
    }

    if (newArgc > 0) {
	if (ConfigElement(wPtr, chPtr, newArgc, newArgv, 0, 1) != TCL_OK) {
	    DeleteNode(wPtr, chPtr);
	    goto cleanup;
	}
    } else {
	if (Tix_DItemConfigure(chPtr->col[0].iPtr, 0, 0, 0) != TCL_OK) {
	    DeleteNode(wPtr, chPtr);
	    goto cleanup;
	}
    }

    Tcl_AppendResult(interp, chPtr->pathName, NULL);
    code = TCL_OK;

cleanup:
    if (newArgv) {
	ckfree((void*) newArgv);
    }
    return code;
}

/*----------------------------------------------------------------------
 * "addchild" sub command --
 *
 *	Replacement for "add" sub command: it is more flexible and
 *	you can have default names for entries.
 *
 *	Add a new item into the list
 *----------------------------------------------------------------------
 */
static int
Tix_HLAddChild(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    HListElement * chPtr;
    char * parentName;
    int code = TCL_ERROR;
    Tcl_Obj ** newArgv = NULL;
    int newArgc = 0;

    parentName = argv[0];
    if (argv[0] && strcmp(argv[0], "") == 0) {
	parentName = NULL;
    }

    argc --;
    argv ++;
    if ((chPtr = NewElement(interp, wPtr, argc, argv, NULL,
	 parentName, &newArgc, &newArgv)) == NULL) {
	goto cleanup;
    }

    if (newArgc > 0) {
	if (ConfigElement(wPtr, chPtr, newArgc, newArgv, 0, 1) != TCL_OK) {
	    DeleteNode(wPtr, chPtr);
	    goto cleanup;
	}
    } else {
	if (Tix_DItemConfigure(chPtr->col[0].iPtr, 0, 0, 0) != TCL_OK) {
	    DeleteNode(wPtr, chPtr);
	    goto cleanup;
	}
    }

    Tcl_AppendResult(interp, chPtr->pathName, NULL);
    code = TCL_OK;

cleanup:
    if (newArgv) {
	ckfree((void*) newArgv);
    }
    return code;
}

/*----------------------------------------------------------------------
 * "anchor", "dragsite" and "dropsire" sub commands --
 *
 *	Set/remove the anchor element
 *----------------------------------------------------------------------
 */
static int
Tix_HLSetSite(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;
    HListElement * chPtr;
    HListElement ** changePtr;
    size_t len ;

    /*
     * Determine which site should be changed.
     */
    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], "set", len)==0) {
	if (argc == 2) {
	    if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[1])) == NULL) {
		return TCL_ERROR;
	    }
	    if (*changePtr != chPtr) {
		*changePtr = chPtr;
		changed = 1;
	    }
	} else {
	    Tcl_AppendResult(interp, "wrong # of arguments, must be: ",
		Tk_PathName(wPtr->dispData.tkwin), " ", argv[-1],
		" set entryPath", 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;
}

/*----------------------------------------------------------------------
 * "cget" sub command --
 *----------------------------------------------------------------------
 */
static int
Tix_HLCGet(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_HLConfig(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_HLDelete(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    HListElement * chPtr;
    size_t len;

    if (strcmp(argv[0], "all") == 0) {
	Tix_HLMarkElementDirty(wPtr, wPtr->root);
	DeleteOffsprings(wPtr, wPtr->root);

	Tix_HLResizeWhenIdle(wPtr);
	return TCL_OK;
    }
    len = strlen(argv[0]);

    if (argc != 2) {
	if ((strncmp(argv[0], "entry", len) == 0) ||
	    (strncmp(argv[0], "offsprings", len) == 0) ||
	    (strncmp(argv[0], "siblings", len) == 0)) {

	    goto wrong_arg;
	}
	else {
	    goto wrong_option;
	}
    }

    if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[1])) == NULL) {
	return TCL_ERROR;
    }

    if (strncmp(argv[0], "entry", len) == 0) {
	Tix_HLMarkElementDirty(wPtr, chPtr->parent);
	DeleteNode(wPtr, chPtr);
    }
    else if (strncmp(argv[0], "offsprings", len) == 0) {
	Tix_HLMarkElementDirty(wPtr, chPtr);
	DeleteOffsprings(wPtr, chPtr);
    }
    else if (strncmp(argv[0], "siblings", len) == 0) {
	Tix_HLMarkElementDirty(wPtr, chPtr);
	DeleteSiblings(wPtr, chPtr);
    }
    else {
	goto wrong_arg;
    }

    Tix_HLResizeWhenIdle(wPtr);
    return TCL_OK;

wrong_arg:

    Tcl_AppendResult(interp,
	"wrong # of arguments, should be pathName delete ", argv[0],
	" entryPath", NULL);
    return TCL_ERROR;

wrong_option:

    Tcl_AppendResult(interp, "unknown option \"", argv[0],
	"\" must be all, entry, offsprings or siblings", NULL);
    return TCL_ERROR;

}

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

    if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[0])) == NULL) {
	return TCL_ERROR;
    }
    if (chPtr->col[0].iPtr == NULL) {
	Tcl_AppendResult(interp, "Item \"", argv[0],
	    "\" does not exist", (char*)NULL);
	return TCL_ERROR;
    }
    return Tix_ConfigureValue2(interp, wPtr->dispData.tkwin, (char *)chPtr,
	entryConfigSpecs, chPtr->col[0].iPtr, argv[1], 0);
}

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

    if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[0])) == NULL) {
	return TCL_ERROR;
    }

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

/*----------------------------------------------------------------------
 * "geometryinfo" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_HLGeometryInfo(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[80];

    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;

    if (wPtr->useHeader) {
	qSize[1] -= wPtr->headerHeight;
    }

    GetScrollFractions(wPtr->totalSize[0], qSize[0], wPtr->leftPixel,
	&first[0], &last[0]);
    GetScrollFractions(wPtr->totalSize[1], qSize[1], wPtr->topPixel,
	&first[1], &last[1]);

#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;
}

/*----------------------------------------------------------------------
 * "hide" sub command
 *----------------------------------------------------------------------
 */

/* %% ToDo: implement the siblings ... etc options, to match those of "delete"
 */
static int
Tix_HLHide(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    HListElement * chPtr;

    if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[1])) == NULL) {
	return TCL_ERROR;
    }

    Tix_HLMarkElementDirty(wPtr, chPtr->parent);
    chPtr->hidden = 1;

    Tix_HLResizeWhenIdle(wPtr);
    return TCL_OK;
}

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

    if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[1])) == NULL) {
	return TCL_ERROR;
    }

    Tix_HLMarkElementDirty(wPtr, chPtr->parent);
    chPtr->hidden = 0;

    Tix_HLResizeWhenIdle(wPtr);
    return TCL_OK;
}

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

    if (strncmp(argv[0], "anchor", len)==0) {
	if (wPtr->anchor) {
	    Tcl_AppendResult(interp, wPtr->anchor->pathName, NULL);
	}
	return TCL_OK;
    }
    else if (strncmp(argv[0], "bbox", len)==0) {
	HListElement * chPtr;

	if (argc != 2) {
	    return Tix_ArgcError(interp, argc+2, argv-2, 3, "entryPath");
	}
	if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[1])) == NULL) {
	    return TCL_ERROR;
	}

	return Tix_HLBBox(interp, wPtr, chPtr);
    }
    else if (strncmp(argv[0], "children", len)==0) {
	HListElement * ptr;

	if (argc != 1 && argc != 2) {
	    return Tix_ArgcError(interp, argc+2, argv-2, 3, "?entryPath?");
	}
	if (argc == 1 || (argc == 2 && *argv[1]==0)) {
	    chPtr = wPtr->root;
	} else {
	    if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[1])) == NULL) {
		return TCL_ERROR;
	    }
	}

	for (ptr=chPtr->childHead; ptr; ptr=ptr->next) {
	    Tcl_AppendElement(interp, ptr->pathName);
	}
	return TCL_OK;
    }
    else if (strncmp(argv[0], "data", len)==0) {
	if (argc != 2) {
	    return Tix_ArgcError(interp, argc+2, argv-2, 3, "entryPath");
	}
	if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[1])) == NULL) {
	    return TCL_ERROR;
	}
        Tcl_IncrRefCount(chPtr->data);
	Tcl_SetObjResult(interp, chPtr->data);
	return TCL_OK;
    }
    else if (strncmp(argv[0], "dragsite", len)==0) {
	if (wPtr->dragSite) {
	    Tcl_AppendResult(interp, wPtr->dragSite->pathName, NULL);
	}
	return TCL_OK;
    }
    else if (strncmp(argv[0], "dropsite", len)==0) {
	if (wPtr->dropSite) {
	    Tcl_AppendResult(interp, wPtr->dropSite->pathName, NULL);
	}
	return TCL_OK;
    }
    else if (strncmp(argv[0], "exists", len)==0) {
	if (argc != 2) {
	    return Tix_ArgcError(interp, argc+2, argv-2, 3, "entryPath");
	}
	chPtr = Tix_HLFindElement(interp, wPtr, argv[1]);

	if (chPtr) {
	    Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
	} else {
	    Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
	}
	return TCL_OK;
    }
    else if (strncmp(argv[0], "hidden", len)==0) {
	if (argc != 2) {
	    return Tix_ArgcError(interp, argc+2, argv-2, 3, "entryPath");
	}
	if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[1])) == NULL) {
	    return TCL_ERROR;
	}
	if (chPtr->hidden) {
	    Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
	} else {
	    Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
	}

	return TCL_OK;
    }
    else if (strncmp(argv[0], "item", len)==0) {
	return Tix_HLItemInfo(interp, wPtr, argc-1, argv+1);
    }
    else if (strncmp(argv[0], "next", len)==0) {
	HListElement * nextPtr;

	if (argc != 2) {
	    return Tix_ArgcError(interp, argc+2, argv-2, 3, "entryPath");
	}

	if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[1])) == NULL) {
	    return TCL_ERROR;
	}

	nextPtr=FindNextEntry(wPtr, chPtr);

	if (nextPtr) {
	    Tcl_AppendResult(interp, nextPtr->pathName, NULL);
	}

	return TCL_OK;
    }
    else if (strncmp(argv[0], "parent", len)==0) {
	if (argc != 2) {
	    return Tix_ArgcError(interp, argc+2, argv-2, 3, "entryPath");
	}
	if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[1])) == NULL) {
	    return TCL_ERROR;
	}

	Tcl_AppendResult(interp, chPtr->parent->pathName, NULL);
	return TCL_OK;
    }
    else if (strncmp(argv[0], "prev", len)==0) {
	HListElement * prevPtr;

	if (argc != 2) {
	    return Tix_ArgcError(interp, argc+2, argv-2, 3, "entryPath");
	}
	if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[1])) == NULL) {
	    return TCL_ERROR;
	}
	prevPtr = FindPrevEntry(wPtr, chPtr);
	if (prevPtr) {
	    Tcl_AppendResult(interp, prevPtr->pathName, NULL);
	}

	return TCL_OK;
    }
    else if (strncmp(argv[0], "selection", len)==0) {
	return CurSelection(interp, wPtr, wPtr->root);
    }
    else {
	Tcl_AppendResult(interp, "unknown option \"", argv[0],
	    "\": must be anchor, bbox, children, data, dragsite, dropsite, ",
	    "exists, hidden, item, next, parent, prev or selection",
	    NULL);
	return TCL_ERROR;
    }
}

/*----------------------------------------------------------------------
 * "info item" sub-sub command
 * argv[0] = x
 * argv[1] = y
 *
 *	returns {entryPath (indicator|column#) type component}
 *----------------------------------------------------------------------
 */
int
Tix_HLItemInfo(interp, wPtr, argc, argv)
    Tcl_Interp *interp;		/* Current interpreter. */
    WidgetPtr wPtr;		/* HList widget */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    HListElement * chPtr;
    int itemX, itemY;
    int listX, listY;
    int widX,  widY;
    int i, m, n;
    char column[20];

    /* A hack to make result a list */
    Tcl_Obj *result = Tcl_NewListObj(0,NULL);
    Tcl_SetObjResult(interp,result);

    if (argc != 2) {
	return Tix_ArgcError(interp, argc+3, argv-3, 3, "x y");
    }
    if (Tcl_GetIntFromObj(interp, argv[0], &widX) != TCL_OK) {
	return TCL_ERROR;
    }
    if (Tcl_GetIntFromObj(interp, argv[1], &widY) != TCL_OK) {
	return TCL_ERROR;
    }
    if (wPtr->root->dirty || wPtr->allDirty) {
	/*
	 * We must update the geometry NOW otherwise we will get a wrong entry
	 */
	Tix_HLCancelResizeWhenIdle(wPtr);
	Tix_HLComputeGeometry((ClientData)wPtr);
    }
    if ((chPtr = FindElementAtPosition(wPtr, widY)) == NULL) {
	goto none;
    }

    listX = widX - wPtr->borderWidth - wPtr->highlightWidth + wPtr->leftPixel;
    listY = widY - wPtr->borderWidth - wPtr->highlightWidth + wPtr->topPixel;

    if (wPtr->useHeader) {
	listY -= wPtr->headerHeight;
    }

    itemX = listX - Tix_HLElementLeftOffset(wPtr, chPtr);
    itemY = listY - Tix_HLElementTopOffset (wPtr, chPtr);

    if (itemY < 0 || itemY >= chPtr->height) {
	goto none;
    }
    if (itemX < 0) {
	goto none;
    }

    if (wPtr->useIndicator && itemX < wPtr->indent) {
	if (chPtr->indicator) {
	    int indCenterX;
	    int indOffX, indOffY;
	    int indX, indY;

	    /* This "if" is a BIG HACK */
	    if (chPtr->parent == wPtr->root) {
		indCenterX = wPtr->indent/2;
	    }
	    else if (chPtr->parent->parent == wPtr->root) {
		indCenterX = chPtr->parent->branchX - wPtr->indent;
	    } else {
		indCenterX = chPtr->parent->branchX;
	    }

	    indOffX = indCenterX   - Tix_DItemWidth (chPtr->indicator)/2;
	    indOffY = chPtr->iconY - Tix_DItemHeight(chPtr->indicator)/2;

	    indX = itemX - indOffX;
	    indY = itemY - indOffY;

	    /* Is it outside of the indicator? */
	    if (indX < 0 || indX >= Tix_DItemWidth (chPtr->indicator)) {
		goto none;
	    }
	    if (indY < 0 || indY >= Tix_DItemHeight(chPtr->indicator)) {
		goto none;
	    }
	    Tcl_AppendElement(interp, chPtr->pathName);
	    Tcl_AppendElement(interp, "indicator");
	    Tcl_AppendElement(interp, Tix_DItemTypeName(chPtr->indicator));
	    Tcl_AppendElement(interp,
		Tix_DItemComponent(chPtr->indicator, indX, indY));
	    return TCL_OK;
	} else {
	    goto none;
	}
    }

    /* skip the indent part */

    if (!wPtr->useIndicator && chPtr->parent == wPtr->root) {
	/* indent not used only in this case */
    } else {
	itemX -= wPtr->indent;
    }

    for (m=n=0,i=0; i<wPtr->numColumns; i++) {
	n += wPtr->actualSize[i].width;
	if (listX < n) {
	    if (n > 1) {
		itemX = listX - m;
	    }
	    goto _column;
	}
	m += wPtr->actualSize[i].width;
    }
    goto none;

_column:
    sprintf(column, "%d", i);
    Tcl_AppendElement(interp, chPtr->pathName);
    Tcl_AppendElement(interp, column);

    if (chPtr->col[i].iPtr != NULL) {
	Tcl_AppendElement(interp, Tix_DItemTypeName(chPtr->col[i].iPtr));
	Tcl_AppendElement(interp,
	    Tix_DItemComponent(chPtr->col[i].iPtr, itemX, itemY));
    }
    return TCL_OK;

none:
    Tcl_ResetResult(interp);
    return TCL_OK;
}

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

    if (Tcl_GetIntFromObj(interp, argv[0], &y) != TCL_OK) {
	return TCL_ERROR;
    }
    if (wPtr->root->dirty || wPtr->allDirty) {
	/*
	 * We must update the geometry NOW otherwise we will get a
	 * wrong entry.
	 */
	Tix_HLCancelResizeWhenIdle(wPtr);
	Tix_HLComputeGeometry((ClientData)wPtr);
    }

    if ((chPtr = FindElementAtPosition(wPtr, y)) != NULL) {
	Tcl_AppendResult(interp, chPtr->pathName, NULL);
    }
    return TCL_OK;
}

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

    if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[0])) == NULL) {
	return TCL_ERROR;
    }
    if (wPtr->resizing || wPtr->redrawing) {
	if (wPtr->elmToSee) {
	    ckfree(wPtr->elmToSee);
	}
	wPtr->elmToSee = tixStrDup(argv[0]);
	return TCL_OK;
    } else {
	Tix_HLSeeElement(wPtr, chPtr, 1);

	return TCL_OK;
    }
}

/*----------------------------------------------------------------------
 * Tix_HLBBox --
 *
 *	Returns the visible bounding box of an HList element (x1, y1, x2, y2).
 *	Currently only y1 and y2 matters. x1 and x2 are always the left
 *	and right edges of the window.
 *
 * Return value:
 *	See user documenetation.
 *
 * Side effects:
 *	None.
 *----------------------------------------------------------------------
 */

static int Tix_HLBBox(interp, wPtr, chPtr)
    Tcl_Interp * interp;	/* Interpreter to report the bbox. */
    WidgetPtr wPtr;		/* HList widget. */
    HListElement * chPtr;	/* Get the BBox for this element.*/
{
    int y, height;
    int wXSize, wYSize;		/* size of the listbox window area */
    int pad;

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

    if (wPtr->root->dirty || wPtr->allDirty) {
	/*
	 * We must update the geometry NOW otherwise we will wrong geometry
	 * info
	 */
	Tix_HLCancelResizeWhenIdle(wPtr);
	Tix_HLComputeGeometry((ClientData)wPtr);
    }

    y = Tix_HLElementTopOffset(wPtr, chPtr) - wPtr->topPixel;
    pad = wPtr->borderWidth + wPtr->highlightWidth;
    wXSize = Tk_Width(wPtr->dispData.tkwin ) - 2*pad;
    wYSize = Tk_Height(wPtr->dispData.tkwin) - 2*pad;

    if (wXSize <= 0) {
	wXSize = 1;
    }
    if (wYSize <= 0) {
	wYSize = 1;
    }

    height = chPtr->height;
    if (height <= 0) {
	height = 1;
    }

    if (y >= wYSize || (y+height) <= 0) {
	/*
	 * The element is not visible
	 */
	return TCL_OK;
    } else {
	int x1;
	int y1, y2;
	char buff[100];

	/*
	 * The bounding box is clipped with the visible area of the widget.
	 */

	x1 = pad;
	y1 = y + wPtr->borderWidth + wPtr->highlightWidth;
	y2 = y1 + height-1;

	if (y1 < pad) {
	    y1 = pad;
	}
	if (y2 >= pad+wYSize) {
	    y2 = pad+wYSize -1;
	}

	if (y2 >= y1) {
#ifdef _LANG
	    Tcl_Obj *result = Tcl_NewListObj(0,NULL);
	    Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(x1));
	    Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(y1));
	    Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(x1+wXSize-1));
	    Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(y2));
	    Tcl_SetObjResult(interp, result);
#else
	    sprintf(buff, "%d %d %d %d", x1, y1, x1+wXSize-1, y2);
	    Tcl_SetResult(interp, buff, TCL_VOLATILE);
#endif
	}
	return TCL_OK;
    }
}

static int Tix_HLSeeElement(wPtr, chPtr, callRedraw)
    WidgetPtr wPtr;
    HListElement * chPtr;
    int callRedraw;
{
    int x, y;
    int cXSize, cYSize;		/* element size */
    int wXSize, wYSize;		/* size of the listbox window area */
    int top, left;		/* new top and left offset of the HLIst */
    int oldTop, oldLeft;

    oldLeft = wPtr->leftPixel;
    oldTop  = wPtr->topPixel;

    x = Tix_HLElementLeftOffset(wPtr, chPtr);
    y = Tix_HLElementTopOffset(wPtr, chPtr);
    if (chPtr->col[0].iPtr) {
	cXSize = Tix_DItemWidth(chPtr->col[0].iPtr);
    } else {
	cXSize = chPtr->col[0].width;
    }
    cYSize = chPtr->height;
    wXSize = Tk_Width(wPtr->dispData.tkwin) -
      (2*wPtr->borderWidth + 2*wPtr->highlightWidth);
    wYSize = Tk_Height(wPtr->dispData.tkwin) -
      (2*wPtr->borderWidth + 2*wPtr->highlightWidth);

    if (wPtr->useHeader) {
	wYSize -= wPtr->headerHeight;
    }

    if (wXSize < 0 || wYSize < 0) {
	/* The window is probably not visible */
	return TCL_OK;
    }

    if (cXSize < wXSize && wPtr->numColumns == 1) {
	/* Align on the X direction */
	left = wPtr->leftPixel;
	if ((x < wPtr->leftPixel) || (x+cXSize > wPtr->leftPixel+wXSize)) {
	    if (wXSize > cXSize) {
		left = x - (wXSize-cXSize)/2;
	    } else {
		left = x;
	    }
	}
    } else {
	left = wPtr->leftPixel;
    }

    /* Align on the Y direction */
    top = wPtr->topPixel;

    if (cYSize < wYSize) {
	if ((wPtr->topPixel-y)>wYSize || (y-wPtr->topPixel-wYSize) > wYSize) {
	    /* far away, make it middle */
	    top = y - (wYSize-cYSize)/2;
	}
	else if (y < wPtr->topPixel) {
	    top = y;
	}
	else if (y+cYSize > wPtr->topPixel+wYSize){
	    top = y+cYSize - wYSize ;
	}
	if (top < 0) {
	    top = 0;
	}
    }

    if (oldLeft != left || oldTop != top) {
	wPtr->leftPixel = left;
	wPtr->topPixel	= top;

	UpdateScrollBars(wPtr, 0);
	if (callRedraw) {
	    RedrawWhenIdle(wPtr);
	}
	return 1;
    } else {
	return 0;
    }
}


/*----------------------------------------------------------------------
 * "selection" sub command
 *	Modify the selection in this HList box
 *----------------------------------------------------------------------
 */
static int
Tix_HLSelection(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    HListElement * chPtr;
    size_t len = strlen(argv[0]);
    int code = TCL_OK;
    int changed = 0;
    int oldSelect = (wPtr->root != NULL &&
		(wPtr->root->selected || (wPtr->root->numSelectedChild > 0)));

    if (strncmp(argv[0], "clear", len)==0) {
	if (argc == 1) {
	    HL_SelectionClearAll(wPtr, wPtr->root, &changed);
	}
	else {
	    HListElement * from, * to;

	    from = Tix_HLFindElement(interp, wPtr, argv[1]);
	    if (from == NULL) {
		code = TCL_ERROR;
		goto done;
	    }

	    if (argc == 3) {
		to = Tix_HLFindElement(interp, wPtr, argv[2]);
		if (to == NULL) {
		    code = TCL_ERROR;
		    goto done;
		}
		changed = SelectionModifyRange(wPtr, from, to, 0);
	    }
	    else {
		if (from->selected == 1) {
		    HL_SelectionClear(wPtr, from);
		    changed = 1;
		}
	    }
	}
    }
    else if (strncmp(argv[0], "includes", len)==0) {
	if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[1])) == NULL) {
	    code = TCL_ERROR;
	    goto done;
	}
	if (chPtr->selected) {
	    Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
	} else {
	    Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
	}
    }
    else if (strncmp(argv[0], "get", len)==0) {
	if (argc != 1) {
	    Tix_ArgcError(interp, argc+2, argv-2, 3, "");
	    code = TCL_ERROR;
	} else {
	    code = CurSelection(interp, wPtr, wPtr->root);
	}
    }
    else if (strncmp(argv[0], "set", len)==0) {
	HListElement * from, * to;

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

	from = Tix_HLFindElement(interp, wPtr, argv[1]);
	if (from == NULL) {
	    code = TCL_ERROR;
	    goto done;
	}

	if (argc == 3) {
	    to = Tix_HLFindElement(interp, wPtr, argv[2]);
	    if (to == NULL) {
		code = TCL_ERROR;
		goto done;
	    }
	    changed = SelectionModifyRange(wPtr, from, to, 1);
	} else {
	    if (!from->selected && !from->hidden) {
		SelectionAdd(wPtr, from);
		changed = 1;
	    }
	}
    }
    else {
	Tcl_AppendResult(interp, "unknown option \"", argv[0],
	    "\": must be anchor, clear, get, includes or set", NULL);
	code = TCL_ERROR;
    }

  done:
    if (changed) {
    /*
     * Claim the selection if we've suddenly started exporting it and
     * there is a selection to export.
     */

	if (wPtr->exportSelection && !oldSelect && wPtr->root != NULL &&
		(wPtr->root->selected || (wPtr->root->numSelectedChild > 0))) {
		    Tk_OwnSelection(wPtr->dispData.tkwin, XA_PRIMARY, HListLostSelection,
			(ClientData) wPtr);
	}
	RedrawWhenIdle(wPtr);
    }

    return code;
}

/*----------------------------------------------------------------------
 * "xview" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_HLXView(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    HListElement * chPtr;
    int leftPixel;
    int oldLeft = wPtr->leftPixel;
    if (argc == 0) {
	Tcl_IntResults(interp, 1, 1, wPtr->leftPixel);
	return TCL_OK;
    }
    else if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[0])) != NULL) {
	leftPixel = Tix_HLElementLeftOffset(wPtr, chPtr);
    }
    else if (Tcl_GetIntFromObj(interp, argv[0], &leftPixel) == TCL_OK) {
	/* %% todo backward-compatible mode */

    }
    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:
	    leftPixel = (int)(fraction * (double)wPtr->totalSize[0]);
	    break;

	  case TK_SCROLL_PAGES:
	    leftPixel = XScrollByPages(wPtr, count);
	    break;

	  case TK_SCROLL_UNITS:
	    leftPixel = XScrollByUnits(wPtr, count);
	    break;
	}
    }

    if (oldLeft != leftPixel) {
	wPtr->leftPixel = leftPixel;
	UpdateScrollBars(wPtr, 0);

	RedrawWhenIdle(wPtr);
    }

    Tcl_ResetResult(interp);
    return TCL_OK;
}

/*----------------------------------------------------------------------
 * "yview" sub command
 *----------------------------------------------------------------------
 */
static int
Tix_HLYView(clientData, interp, argc, argv)
    ClientData clientData;
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    HListElement * chPtr;
    int topPixel;
    int oldTop = wPtr->topPixel;

    if (argc == 0) {
        Tcl_IntResults(interp, 1, 1, wPtr->topPixel);
	return TCL_OK;
    }
    else if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[0])) != NULL) {
	topPixel = Tix_HLElementTopOffset(wPtr, chPtr);
    }
    else if (Tcl_GetIntFromObj(interp, argv[0], &topPixel) == TCL_OK) {
	/* %% todo backward-compatible mode */
    }
    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:
	    topPixel = (int)(fraction * (double)wPtr->totalSize[1]);
	    break;

	  case TK_SCROLL_PAGES:
	    topPixel = YScrollByPages(wPtr, count);
	    break;

	  case TK_SCROLL_UNITS:
	    topPixel = YScrollByUnits(wPtr, count);
	    break;
	}
    }

    if (oldTop != topPixel) {
	wPtr->topPixel = topPixel;
	UpdateScrollBars(wPtr, 0);

	RedrawWhenIdle(wPtr);
    }

    Tcl_ResetResult(interp);
    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 HList 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;
    int oldColumns;
    Tix_StyleTemplate stTmpl;
    int oldExport;

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

    if (wPtr->initialized && oldColumns != wPtr->numColumns) {
	Tcl_AppendResult(interp, "Cannot change the number of columns ",
	    (char *) NULL);
	wPtr->numColumns = oldColumns;
	return TCL_ERROR;
    }
    if (wPtr->numColumns < 1) {
	wPtr->numColumns = 1;
    }

    if (wPtr->separator == 0 || wPtr->separator[0] == 0) {
	if (wPtr->separator != 0) {
	    ckfree(wPtr->separator);
	}
	wPtr->separator = tixStrDup(".");
    }

    if (oldfont != wPtr->font) {
	/*
	 * Font has been changed (initialized)
	 */
	TixComputeTextGeometry(wPtr->font, "0", 1,
	    0, &wPtr->scrollUnit[0], &wPtr->scrollUnit[1]);
    }

    /*
     * 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 normal text GC */
    gcValues.font		= TixFontId(wPtr->font);
    gcValues.foreground		= wPtr->normalFg->pixel;
    gcValues.background		= wPtr->normalBg->pixel;
    gcValues.graphics_exposures = False;

    newGC = Tk_GetGC(wPtr->dispData.tkwin,
	GCForeground|GCBackground|GCFont|GCGraphicsExposures, &gcValues);
    if (wPtr->normalGC != None) {
	Tk_FreeGC(wPtr->dispData.display, wPtr->normalGC);
    }
    wPtr->normalGC = 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 sloid dropsite lines */
    gcValues.foreground		= wPtr->normalFg->pixel;
    gcValues.background		= wPtr->normalBg->pixel;
    gcValues.graphics_exposures = False;
    gcValues.subwindow_mode	= IncludeInferiors;

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

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

    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 size of the elements in this has changed */
    Tix_HLResizeWhenIdle(wPtr);

    /*
     * Claim the selection if we've suddenly started exporting it and
     * there is a selection to export.
     */

    if (wPtr->exportSelection && !oldExport &&
	    wPtr->root != NULL &&
            (wPtr->root->selected || (wPtr->root->numSelectedChild > 0))) {
	Tk_OwnSelection(wPtr->dispData.tkwin, XA_PRIMARY, HListLostSelection,
		(ClientData) wPtr);
    }

    return TCL_OK;
}

/*
 *--------------------------------------------------------------
 *
 * WidgetEventProc --
 *
 *	This procedure is invoked by the Tk dispatcher for various
 *	events on HLists.
 *
 * 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));
	}
	Tix_HLCancelResizeWhenIdle(wPtr);
	CancelRedrawWhenIdle(wPtr);
	Tcl_EventuallyFree((ClientData) wPtr, WidgetDestroy);
	break;

      case ConfigureNotify:
	RedrawWhenIdle(wPtr);
	UpdateScrollBars(wPtr, 1);
	break;

      case Expose:
	RedrawWhenIdle(wPtr);
	break;

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

      case FocusOut:
	wPtr->hasFocus = 0;
	RedrawWhenIdle(wPtr);
	break;
    }
}

/*
 *--------------------------------------------------------------
 *
 * SubWindowEventProc --
 *
 *	This procedure is invoked by the Tk dispatcher for various
 *	events on the header subwindow.
 *--------------------------------------------------------------
 */
static void
SubWindowEventProc(clientData, eventPtr)
    ClientData clientData;	/* Information about window. */
    XEvent *eventPtr;		/* Information about event. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    Tk_FakeWin * fw;

    switch (eventPtr->type) {
      case DestroyNotify:

#ifdef TK_PARENT_DESTROYED
	/*
	 * The TK_PARENT_DESTROYED symbol is no longer defined in Tk 8.0
	 */
	fw = (Tk_FakeWin *) (wPtr->headerWin);
	if (fw->flags & TK_PARENT_DESTROYED) {
	    break;
	}
	if (wPtr->headerWin != NULL) {
	    panic("HList: header subwindow deleted illegally\n");
	}
#endif
	break;

      case Expose:
	if (wPtr->headerWin != NULL) {
	    RedrawWhenIdle(wPtr);
	}
	break;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * WidgetDestroy --
 *
 *	This procedure is invoked by Tk_EventuallyFree or Tk_Release
 *	to clean up the internal structure of a HList at a safe time
 *	(when no-one is using it anymore).
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Everything associated with the HList is freed up.
 *
 *----------------------------------------------------------------------
 */
static void
WidgetDestroy(clientData)
    char *clientData;	/* Info about my widget. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;

    if (wPtr->root != NULL) {
	DeleteOffsprings(wPtr, wPtr->root);
	FreeElement(wPtr, wPtr->root);
    }

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

    /* the following two members will be NULL if the widget was destroyed
     * during its creation (e.g., wrong arguments during creation
     */
    if (wPtr->reqSize != NULL) {
	ckfree((char*)wPtr->reqSize);
    }
    if (wPtr->actualSize != NULL) {
	ckfree((char*)wPtr->actualSize);
    }
    if (wPtr->elmToSee != NULL) {
	ckfree(wPtr->elmToSee);
	wPtr->elmToSee = NULL;
    }

    Tix_HLFreeHeaders(wPtr->dispData.interp, wPtr);

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

    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);
    }
}

/*
 *--------------------------------------------------------------
 *
 * Tix_HLComputeGeometry --
 *
 *	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
 *
 *--------------------------------------------------------------
 */
void
Tix_HLComputeGeometry(clientData)
    ClientData clientData;
{
    WidgetPtr wPtr = (WidgetPtr)clientData;
    int i, reqW, reqH;
    int sizeChanged = 0;
    int width = 0;

    if (wPtr->dispData.tkwin == NULL) {
	panic("No tkwin");
	return;
    }

    wPtr->resizing = 0;

    /* Update geometry request */
    if (wPtr->useHeader && wPtr->headerDirty) {
	Tix_HLComputeHeaderGeometry(wPtr);
    }

    if (wPtr->root->dirty || wPtr->allDirty) {
	if (wPtr->useIndicator) {
	    /*
	     * If we use indicator, then the toplevel elements are indented
	     * by wPtr->indent. Otherwise they are indented by 0 pixels
	     */
	    ComputeElementGeometry(wPtr, wPtr->root, wPtr->indent);
	} else {
	    ComputeElementGeometry(wPtr, wPtr->root, 0);
	}
    }
    width = 0;
    for (i=0; i<wPtr->numColumns; i++) {
	if (wPtr->reqSize[i].width != UNINITIALIZED) {
	    wPtr->actualSize[i].width = wPtr->reqSize[i].width;
	}
	else {
	    /* This is the req size of the entry columns */
	    int entReq = wPtr->root->col[i].width;

	    /* This is the req size of the header columns */
	    int hdrReq = wPtr->headers[i]->width;

	    if (wPtr->useHeader && (hdrReq > entReq)) {
		wPtr->actualSize[i].width = hdrReq;
	    } else {
		wPtr->actualSize[i].width = entReq;
	    }
	}
	width += wPtr->actualSize[i].width;
    }
    sizeChanged = 1;
    wPtr->allDirty = 0;

    wPtr->totalSize[0] = width;
    wPtr->totalSize[1] = wPtr->root->allHeight;

    if (wPtr->width > 0) {
	reqW = wPtr->width * wPtr->scrollUnit[0];
    } else {
	reqW = width;
    }
    if (wPtr->height > 0) {
	reqH = wPtr->height * wPtr->scrollUnit[1];
    } else {
	reqH = wPtr->root->allHeight;
    }

    wPtr->totalSize[0] += 2*wPtr->borderWidth + 2*wPtr->highlightWidth;
    wPtr->totalSize[1] += 2*wPtr->borderWidth + 2*wPtr->highlightWidth;
    reqW	       += 2*wPtr->borderWidth + 2*wPtr->highlightWidth;
    reqH	       += 2*wPtr->borderWidth + 2*wPtr->highlightWidth;

    if (wPtr->useHeader) {
	reqH	       += wPtr->headerHeight;
    }

    /* Now we need to handle the multiple columns mode */

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

    /* Update scrollbars */
    UpdateScrollBars(wPtr, sizeChanged);

    RedrawWhenIdle(wPtr);
}

/*
 *----------------------------------------------------------------------
 * Tix_HLResizeWhenIdle --
 *----------------------------------------------------------------------
 */
void
Tix_HLResizeWhenIdle(wPtr)
    WidgetPtr wPtr;
{
    if (wPtr->dispData.tkwin == NULL) {
	panic("No tkwin");
	return;
    }

    if (!wPtr->resizing) {
	wPtr->resizing = 1;
	Tcl_DoWhenIdle(Tix_HLComputeGeometry, (ClientData)wPtr);
    }
    if (wPtr->redrawing) {
	CancelRedrawWhenIdle(wPtr);
    }
}

/*
 *----------------------------------------------------------------------
 * Tix_HLResizeNow --
 *----------------------------------------------------------------------
 */
void
Tix_HLResizeNow(wPtr)
    WidgetPtr wPtr;
{
    if (wPtr->resizing) {
	wPtr->resizing = 0;
	Tcl_CancelIdleCall(Tix_HLComputeGeometry, (ClientData)wPtr);
	Tix_HLComputeGeometry((ClientData)wPtr);
    }
}

/*
 *----------------------------------------------------------------------
 * Tix_HLCancelResizeWhenIdle --
 *----------------------------------------------------------------------
 */
void
Tix_HLCancelResizeWhenIdle(wPtr)
    WidgetPtr wPtr;
{
    if (wPtr->resizing) {
	wPtr->resizing = 0;
	Tcl_CancelIdleCall(Tix_HLComputeGeometry, (ClientData)wPtr);
    }
}

/*
 *----------------------------------------------------------------------
 * RedrawWhenIdle --
 *----------------------------------------------------------------------
 */
static void
RedrawWhenIdle(wPtr)
    WidgetPtr wPtr;
{
    if (wPtr->dispData.tkwin == NULL) {
	panic("No tkwin");
	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);
    }
}

/*----------------------------------------------------------------------
 * DItemSizeChangedProc --
 *
 *	This is called whenever the size of one of the HList's items
 *	changes its size.
 *----------------------------------------------------------------------
 */
static void DItemSizeChangedProc(iPtr)
    Tix_DItem *iPtr;
{
    HLItemTypeInfo * info = (HLItemTypeInfo *)iPtr->base.clientData;
    HListColumn * colPtr;
    HListElement * chPtr;
    HListHeader * hPtr;
    WidgetPtr wPtr;

    if (info == NULL) {
	/* Perhaps we haven't set the clientData yet! */
	return;
    }

    switch (info->type) {
      case HLTYPE_COLUMN:
	colPtr = (HListColumn*) info;
	chPtr = colPtr->chPtr;

	if (chPtr) {	/* Sanity check */
	    Tix_HLMarkElementDirty(chPtr->wPtr, chPtr);
	    Tix_HLResizeWhenIdle(chPtr->wPtr);
	}
	break;
      case HLTYPE_HEADER:
	hPtr = (HListHeader*)info;
	wPtr = hPtr->wPtr;
	wPtr->headerDirty = 1;
	if (wPtr->useHeader) {
	    Tix_HLResizeWhenIdle(wPtr);
	}
	break;
      case HLTYPE_ENTRY:
	chPtr = (HListElement*)info;

	if (chPtr) {	/* Sanity check */
	    Tix_HLMarkElementDirty(chPtr->wPtr, chPtr);
	    Tix_HLResizeWhenIdle(chPtr->wPtr);
	}
	break;
    }
}

/*
 *--------------------------------------------------------------
 *
 * AllocElement --
 *
 *	Allocates a new structure for the new element and record it
 *	in the hash table
 *
 * Results:
 *	a pointer to the new element's structure
 *
 * Side effects:
 *	Has table is changed
 *--------------------------------------------------------------
 */
static HListElement *
AllocElement(wPtr, parent, pathName, name, ditemType)
    WidgetPtr wPtr;
    HListElement * parent;
    char * pathName;
    char * name;
    char * ditemType;
{
    HListElement      * chPtr;
    Tcl_HashEntry     * hashPtr;
    int			dummy;
    Tix_DItem	      * iPtr;

    if (ditemType == NULL) {
	iPtr = NULL;
    } else {
	if ((iPtr = Tix_DItemCreate(&wPtr->dispData, ditemType)) == NULL) {
	    return NULL;
	}
    }

    chPtr = (HListElement*)ckalloc(sizeof(HListElement));

    if (pathName) {
	/* pathName == 0 is the root element */
	hashPtr = Tcl_CreateHashEntry(&wPtr->childTable, pathName, &dummy);
	Tcl_SetHashValue(hashPtr, (char*)chPtr);
    }

    if (parent) {
	++ parent->numCreatedChild;
    }

    if (wPtr->numColumns > 1) {
	chPtr->col		= Tix_HLAllocColumn(wPtr, chPtr);
    } else {
	chPtr->col		= &chPtr->_oneCol;
	chPtr->_oneCol.type	= HLTYPE_COLUMN;
	chPtr->_oneCol.self	= (char*) &chPtr->_oneCol;
	chPtr->_oneCol.chPtr	= chPtr;
	chPtr->_oneCol.iPtr	= NULL;
	chPtr->_oneCol.width	= 0;
    }
    if (pathName) {
	chPtr->pathName		= tixStrDup(pathName);
    } else {
	chPtr->pathName		= NULL;
    }

    if (name) {
	chPtr->name		= tixStrDup(name);
    } else {
	chPtr->name		= NULL;
    }

    chPtr->type = HLTYPE_ENTRY;
    chPtr->self = (char*)chPtr;
    chPtr->wPtr			= wPtr;
    chPtr->parent		= parent;
    chPtr->prev			= NULL;
    chPtr->next			= NULL;
    chPtr->childHead		= NULL;
    chPtr->childTail		= NULL;
    chPtr->numSelectedChild	= 0;
    chPtr->numCreatedChild	= 0;
    chPtr->col[0].iPtr		= iPtr;
    chPtr->indicator		= NULL;

    chPtr->height		= 0;
    chPtr->allHeight		= 0;
    chPtr->selected		= 0;
    chPtr->dirty		= 0;
    chPtr->hidden		= 0;
    chPtr->state		= tixNormalUid;
    chPtr->data			= NULL;
    chPtr->branchX		= 0;
    chPtr->branchY		= 0;

    if (iPtr) {
	/* The clientdata is usedful for the DItemSizeChangedProc() */
	iPtr->base.clientData = (ClientData)&chPtr->col[0];
    }

    return chPtr;
}

static void
FreeElement(wPtr, chPtr)
    WidgetPtr wPtr;
    HListElement * chPtr;
{
    Tcl_HashEntry * hashPtr;
    int i;

    if (chPtr->selected) {
	HL_SelectionClear(wPtr, chPtr);
    }
    if (wPtr->anchor == chPtr) {
	wPtr->anchor = NULL;
    }
    if (wPtr->dragSite == chPtr) {
	wPtr->dragSite = NULL;
    }
    if (wPtr->dropSite == chPtr) {
	wPtr->dropSite = NULL;
    }

    /*
     * Free all the display items
     */
    for (i=0; i<wPtr->numColumns; i++) {
	if (chPtr->col[i].iPtr) {
	    if (Tix_DItemType(chPtr->col[i].iPtr) == TIX_DITEM_WINDOW) {
		Tix_WindowItemListRemove(&wPtr->mappedWindows,
		    chPtr->col[i].iPtr);
	    }
	    Tix_DItemFree(chPtr->col[i].iPtr);
	}
    }
    if (chPtr->indicator != NULL) {
	if (Tix_DItemType(chPtr->indicator) == TIX_DITEM_WINDOW) {
	    Tix_WindowItemListRemove(&wPtr->mappedWindows,
		chPtr->indicator);
	}
	Tix_DItemFree(chPtr->indicator);
    }

    if (chPtr->col != &chPtr->_oneCol) {
	/*
	 * This space was allocated dynamically
	 */
	ckfree((char*)chPtr->col);
    }

    if (chPtr->pathName) {
	/*
	 * Root does not have an entry in the hash table
	 */
	if ((hashPtr = Tcl_FindHashEntry(&wPtr->childTable, chPtr->pathName))){
	    Tcl_DeleteHashEntry(hashPtr);
	}
    }
    if (chPtr->name != NULL) {
	ckfree(chPtr->name);
    }
    if (chPtr->pathName != NULL) {
	ckfree(chPtr->pathName);
    }
    Tk_FreeOptions(entryConfigSpecs, (char *)chPtr, wPtr->dispData.display, 0);

    ckfree((char*)chPtr);
}

static void
AppendList(wPtr, parent, chPtr, at, afterPtr, beforePtr)
    WidgetPtr wPtr;
    HListElement *parent;
    HListElement *chPtr;
    int at;			/* At what position should this entry be added
				 * default is "-1": add at the end */
    HListElement *afterPtr;	/* after which entry should this entry be
				 * added. Default is NULL : ignore */
    HListElement *beforePtr;	/* before which entry should this entry be
				 * added. Default is NULL : ignore */
{
    if (parent->childHead == NULL) {
	parent->childHead = chPtr;
	parent->childTail = chPtr;
	chPtr->prev = NULL;
	chPtr->next = NULL;
    }
    else {
	if (at >= 0) {
	    /*
	     * Find the current element at the "at" position
	     */
	    HListElement *ptr;
	    for (ptr=parent->childHead;
		 ptr!=NULL && at > 0;
		 ptr=ptr->next, --at) {
		; /* do nothing, just keep counting */
	    }
	    if (ptr != NULL) {
		/*
		 * We need to insert the new element *before* ptr.E.g,
		 * if at == 0, then the new element should be the first
		 * of the list
		 */
		beforePtr = ptr;
	    } else {
		/* Seems like we walked past the end of the list. Well, do
		 * nothing here. By default, the new element will be
		 * append to the end of the list
		 */
	    }
	}
	if (afterPtr != NULL) {
	    if (afterPtr == parent->childTail) {
		parent->childTail = chPtr;
	    } else {
		afterPtr->next->prev = chPtr;
	    }
	    chPtr->prev = afterPtr;
	    chPtr->next = afterPtr->next;
	    afterPtr->next = chPtr;
	    return;
	}
	if (beforePtr !=NULL) {
	    if (beforePtr == parent->childHead) {
		parent->childHead = chPtr;
	    } else {
		beforePtr->prev->next = chPtr;
	    }
	    chPtr->prev = beforePtr->prev;
	    chPtr->next = beforePtr;
	    beforePtr->prev = chPtr;
	    return;
	}

	/*
	 * By default, append it at the end of the list
	 */
	parent->childTail->next = chPtr;
	chPtr->prev = parent->childTail;
	chPtr->next = NULL;
	parent->childTail = chPtr;
    }
}

/*
 *--------------------------------------------------------------
 *
 * NewElement --
 *
 *	This procedure is creates a new element and record it both
 *	the hash table and in the tree.
 *
 * Results:
 *	pointer to new element
 *
 * Side effects:
 *	Hash table and tree changed if successful
 *--------------------------------------------------------------
 */
static HListElement *
NewElement(interp, wPtr, argc, argv, pathName, defParentName, newArgc, newArgv)
    Tcl_Interp *interp;
    WidgetPtr wPtr;
    int argc;
    Tcl_Obj *CONST *objv;
    char * pathName;		/* Default pathname, if -pathname is not
				 * specified in the options */
    char * defParentName;	/* Default parent name (will NULL if pathName
				 * is not NULL */
    int * newArgc;
    Tcl_Obj *** newArgv;
{
#define FIXED_SPACE 20
    char fixedSpace[FIXED_SPACE+1];
    char *p, *parentName = NULL;
    char *name;				/* Last part of the name */
    int i, n, numChars;
    HListElement *parent;
    HListElement *chPtr;
    char sep = wPtr->separator[0];
    int allocated = 0;
    char * ditemType = NULL;
    HListElement *afterPtr  = NULL;
    HListElement *beforePtr = NULL;
    int at = -1;
    int numSwitches = 0;		/* counter on how many of the
					 * -after, -before and -at switches
					 * have been used. No more than one
					 * of then can be used */

    /*
     * (1) We need to determine the options:
     *	   -itemtype, -after, -before and/or -at.
     *
     */
    if (argc > 0) {
	size_t len;
	if (argc %2 != 0) {
	    Tcl_AppendResult(interp, "value for \"", argv[argc-1],
		"\" missing", NULL);
	    chPtr = NULL;
	    goto done;
	}
	for (n=i=0; i<argc; i+=2) {
	    len = strlen(argv[i]);
	    if (strncmp(argv[i], "-itemtype", len) == 0) {
		ditemType = argv[i+1];
		goto copy;
	    }
	    else if (strncmp(argv[i], "-after", len) == 0) {
		afterPtr = Tix_HLFindElement(interp, wPtr, argv[i+1]);
		if (afterPtr == NULL) {
		    chPtr = NULL;
		    goto done;
		}
		++ numSwitches;
		continue;
	    }
	    else if (strncmp(argv[i], "-before", len) == 0) {
		beforePtr = Tix_HLFindElement(interp, wPtr, argv[i+1]);
		if (beforePtr == NULL) {
		    chPtr = NULL;
		    goto done;
		}
		++ numSwitches;
		continue;
	    }
	    else if (strncmp(argv[i], "-at", len) == 0) {
		if (Tcl_GetIntFromObj(interp, objv[i+1], &at) != TCL_OK) {
		    chPtr = NULL;
		    goto done;
		}
		++ numSwitches;
		continue;
	    }

	  copy:
	    *newArgv = (Tcl_Obj**) ckrealloc((void*)*newArgv, (sizeof(Tcl_Obj*)) * (n+2));
	    (*newArgv)[n] = objv[i];
	    (*newArgv)[n+1] = objv[i+1];
	    n+=2;
	}
	* newArgc = n;
    } else {
	* newArgc = 0;
    }
    if (numSwitches > 1) {
	Tcl_AppendResult(interp, "No more than one of the -after, -before ",
	    "and -at options can be used", NULL);
	chPtr = NULL;
	goto done;
    }
    if (ditemType == NULL) {
	ditemType = wPtr->diTypePtr->name;
    }
    if (Tix_GetDItemType(interp, ditemType) == NULL) {
	chPtr = NULL;
	goto done;
    }

    /*------------------------------------------------------------
     * (2) Create the new entry. The method depends on whether
     *	   the "add" or "addchild" command has been called
     *------------------------------------------------------------
     */
    if (pathName == NULL) {
	/* (2.a) Called by the "addchild" command. We need to generate
	 *     a default name for the child
	 *
	 */
	char buff[40];

	parentName = defParentName;
	if (parentName == NULL) {
	    parent = wPtr->root;
	} else {
	    if ((parent=Tix_HLFindElement(interp, wPtr, parentName))== NULL) {
		Tcl_ResetResult(interp);
		Tcl_AppendResult(interp, "parent element \"", parentName,
		    "\" does not exist", (char *) NULL);
		chPtr = NULL;
		goto done;
	    }
	}

	/* Generate a default name for this entry */
	sprintf(buff, "%d", parent->numCreatedChild);
	name = buff;

	if (parentName == NULL) {
	    pathName = tixStrDup(name);
	    allocated = 1;
	}
	else {
	    pathName = ckalloc(strlen(parentName)+1+ strlen(name)+1);
	    allocated = 1;
	    sprintf(pathName, "%s%c%s", parentName, sep, name);
	}
    }
    else {
	/* (2.b) Called by the "add" command.
	 *
	 * Strip the parent's name out of pathName (it's everything up
	 * to the last dot).  There are two tricky parts: (a) must
	 * copy the parent's name somewhere else to avoid modifying
	 * the pathName string (for large names, space for the copy
	 * will have to be malloc'ed);	(b) must special-case the
	 * situation where the parent is ".".
	 */

	if ((p = strrchr(pathName, (int)sep)) == NULL) {
	    /* This is a toplevel element  (no "." in it) */
	    name = pathName;
	    parentName = NULL;
	}
	else {
	    name = p+1;
	    numChars = p-pathName;
	    if (numChars > FIXED_SPACE) {
		parentName = (char *) ckalloc((unsigned)(numChars+1));
	    } else {
		parentName = fixedSpace;
	    }
	    if (numChars == 0) {
		if ((pathName[0] == sep) && (pathName[1] == '\0')) {
		    /*
		     * The separator by itself is also a toplevel entry
		     */
		    parentName = 0;
		} else {
		    parentName[0] = sep;
		    parentName[1] = '\0';
		}
	    }
	    else {
		strncpy(parentName, pathName, (size_t) numChars);
		parentName[numChars] = '\0';
	    }
	}

	if (parentName == NULL) {
	    parent = wPtr->root;
	} else {
	    if ((parent = Tix_HLFindElement(interp, wPtr, parentName))==NULL) {
		Tcl_ResetResult(interp);
		Tcl_AppendResult(interp, "parent element \"", parentName,
		"\" does not exist", (char *) NULL);
		chPtr = NULL;
		goto done;
	    }
	}

    }
    if (Tix_HLFindElement(interp, wPtr, pathName) != NULL) {
	Tcl_AppendResult(interp, "element \"", pathName,
	    "\" already exists", (char *) NULL);
	chPtr = NULL;
	goto done;
    }
    else {
	if (afterPtr != NULL && afterPtr->parent != parent) {
	    Tcl_AppendResult(interp, "cannot add entry after \"",
		afterPtr->pathName, "\"", NULL);
	    chPtr = NULL;
	    goto done;
	}
	if (beforePtr != NULL && beforePtr->parent != parent) {
	    Tcl_AppendResult(interp, "cannot add entry before \"",
		beforePtr->pathName, "\"", NULL);
	    chPtr = NULL;
	    goto done;
	}

	Tcl_ResetResult(interp);
	if ((chPtr = AllocElement(wPtr, parent, pathName, name, ditemType))
	     == NULL) {
	    /* Some error, now chPtr == NULL */
	    goto done;
	}
	AppendList(wPtr, parent, chPtr, at, afterPtr, beforePtr);
	Tix_HLMarkElementDirty(wPtr, chPtr);
	Tix_HLResizeWhenIdle(wPtr);
	goto done;		/* success */
    }

  done:
    if (allocated) {
	ckfree((char*)pathName);
    }
    if (parentName && parentName != fixedSpace && parentName !=defParentName) {
	ckfree((char*)parentName);
    }
    return chPtr;
}

/*--------------------------------------------------------------
 * ConfigElement --
 *
 *	This procedure configures the element according to the
 *	options.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	Hash table and tree changed if successful
 *--------------------------------------------------------------
 */

static int
ConfigElement(wPtr, chPtr, argc, argv, flags, forced)
    WidgetPtr wPtr;
    HListElement *chPtr;
    int argc;
    char ** argv;
    int flags;
    int forced;			/* We need a "forced" configure to ensure that
				 * the DItem is initialized properly */
{
    int sizeChanged;

    if (wPtr->dispData.tkwin == NULL)
	panic("No tkwin");

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

    if (sizeChanged) {
	Tix_HLMarkElementDirty(wPtr, chPtr);
	Tix_HLResizeWhenIdle(wPtr);
    } else {
	RedrawWhenIdle(wPtr);
    }

    return TCL_OK;
}

/*
 *--------------------------------------------------------------
 *
 * FindElementAtPosition --
 *
 *	Finds a visible element nearest to a Y position
 *
 * Results:
 *	Pointer to the element.
 *
 * Side effects:
 *	None
 *--------------------------------------------------------------
 */
static HListElement * FindElementAtPosition(wPtr, y)
    WidgetPtr wPtr;
    int y;
{
    HListElement * chPtr = wPtr->root;
    int top = 0;

    y -= wPtr->borderWidth + wPtr->highlightWidth;
    y += wPtr->topPixel;

    if (wPtr->useHeader) {
	y -= wPtr->headerHeight;
    }

    if (y < 0) {
	/*
	 * Position is above the top of the list, return the first element in
	 * the list of toplevel entries.
	 */
	if (wPtr->root != NULL) {
	    for (chPtr=wPtr->root->childHead; chPtr!=NULL; chPtr=chPtr->next) {
		if (!chPtr->hidden) {
		    return chPtr;
		}
	    }
	}
	return NULL;
    }
    if (y >= chPtr->allHeight) {
	/*
	 * Position is past the end of the list, return the last element.
	 */
	HListElement * vis;

	chPtr=wPtr->root;
	while (1) {
	    if (chPtr->childTail == NULL) {
		break;
	    }
	    for (vis = chPtr->childTail; vis && vis->hidden; vis=vis->prev) {
		;
	    }
	    if (vis == NULL) {
		break;
	    } else {
		chPtr = vis;
		continue;
	    }
	}
	if (chPtr == wPtr->root) {
	    /*
	     * There is either no element, or all elements are not visible
	     */
	    return NULL;
	} else {
	    return chPtr;
	}
    }

    /*
     * The following is a tail-recursive function flatten out in a while
     * loop.
     */

    while (1) {
    again:
	for (chPtr=chPtr->childHead; chPtr!=NULL; chPtr=chPtr->next) {
	    if (!chPtr->hidden) {
		if (top <= y && y < top + chPtr->allHeight) {
		    if (y < top + chPtr->height) {
			return chPtr;
		    } else {
			top += chPtr->height;
			goto again;
		    }
		} else {
		    top += chPtr->allHeight;
		}
	    }
	}
	/* FIXME: If we fall out of for loop chPtr is NULL, so we
	 * cannot do chPtr->childHead as while loop implies
	 * this is a quick-fix.
	 */
	return NULL;
    }
}

/*
 *--------------------------------------------------------------
 *
 * Tix_HLFindElement --
 *
 *	Finds an element according to its pathname.
 *
 * Results:
 *	Pointer to the element if found. Otherwise NULL.
 *
 * Side effects:
 *	None
 *--------------------------------------------------------------
 */
HListElement * Tix_HLFindElement(interp, wPtr, pathName)
    Tcl_Interp * interp;
    WidgetPtr wPtr;
    char * pathName;
{
    Tcl_HashEntry     * hashPtr;

    if (pathName) {
	hashPtr = Tcl_FindHashEntry(&wPtr->childTable, pathName);

	if (hashPtr) {
	    return (HListElement*) Tcl_GetHashValue(hashPtr);
	} else {
	    Tcl_AppendResult(interp, "Entry \"", pathName,
		"\" not found", NULL);
	    return NULL;
	}
    }
    else {
	/* pathName == 0 is the root element */
	return wPtr->root;
    }
}

/*
 *--------------------------------------------------------------
 *
 * SelectionModifyRange --
 *
 *	Select or de-select all the elements between from and to
 *	(inclusive), according to the "select" argument.
 *
 *	select == 1 : select
 *	select == 0 : de-select
 *
 * Return value:
 *	Whether the selection was actually changed
 *--------------------------------------------------------------
 */
static int SelectionModifyRange(wPtr, from, to, select)
    WidgetPtr wPtr;
    HListElement * from;
    HListElement * to;
    int select;
{
    int changed = 0;

    if (Tix_HLElementTopOffset(wPtr, from) > Tix_HLElementTopOffset(wPtr, to)){
	HListElement * tmp;
	tmp  = to;
	to   = from;
	from = tmp;
    }

    while (1) {
	if (!from->hidden && (int)from->selected != select) {
	    changed = 1;
	    if (select) {
		SelectionAdd(wPtr, from);
	    } else {
		HL_SelectionClear(wPtr, from);
	    }
	}

	if (from == to) {
	    /*
	     * Iterated to the end of the region
	     */
	    break;
	}

	/*
	 * Go to the next list entry
	 */
	if (from->childHead) {
	    from = from->childHead;
	}
	else if (from->next) {
	    from = from->next;
	}
	else {
	    /*
	     * go to a different branch
	     */
	    while (from->parent->next == NULL && from != wPtr->root) {
		from = from->parent;
	    }
	    if (from == wPtr->root) {
		/*
		 * Iterated over all list entries
		 */
		break;
	    } else {
		from = from->parent->next;
	    }
	}
    }

    return changed;
}

/*
 *--------------------------------------------------------------
 *
 * Tix_HLElementTopOffset --
 *
 *--------------------------------------------------------------
 */
int Tix_HLElementTopOffset(wPtr, chPtr)
    WidgetPtr wPtr;
    HListElement * chPtr;
{
    int top;
    HListElement * ptr;

    if (chPtr == wPtr->root) {
	return 0;
    }
    top = Tix_HLElementTopOffset(wPtr, chPtr->parent);
    top += chPtr->parent->height;

    for (ptr=chPtr->parent->childHead; ptr!=NULL; ptr=ptr->next) {
	if (ptr == chPtr) {
	    break;
	}
	if (ptr->hidden) {
	    continue;
	}
	top += ptr->allHeight;
    }
    return top;
}

/*
 *--------------------------------------------------------------
 *
 * Tix_HLElementLeftOffset --
 *
 *--------------------------------------------------------------
 */
int Tix_HLElementLeftOffset(wPtr, chPtr)
    WidgetPtr wPtr;
    HListElement * chPtr;
{
    int left;

    if (chPtr == wPtr->root || chPtr->parent == wPtr->root) {
	return 0;
    }

    left = Tix_HLElementLeftOffset(wPtr, chPtr->parent);
    left += wPtr->indent;

    return left;
}

/*
 *--------------------------------------------------------------
 *
 * CurSelection --
 *
 *	returns the current selection in the result of interp;
 *
 *--------------------------------------------------------------
 */
static int CurSelection(interp, wPtr, chPtr)
    Tcl_Interp * interp;
    WidgetPtr wPtr;
    HListElement * chPtr;
{
    HListElement * ptr;

    /* Since this recursion starts with wPtr->root, we determine
     * whether a node is selected when its *parent* is called. This
     * will save one level of recursion (otherwise all leave nodes will
     * be recursed once and will be slow ...
     */
    for (ptr=chPtr->childHead; ptr; ptr=ptr->next) {
	if (ptr->selected && !(ptr->hidden)) {
	    Tcl_AppendElement(interp, ptr->pathName);
	}
	if (ptr->childHead) {
	    CurSelection(interp, wPtr, ptr);
	}
    }
    return TCL_OK;
}

/*
 *--------------------------------------------------------------
 *
 * Tix_HLMarkElementDirty --
 *
 *	Marks a element "dirty", i.e., its geometry needs to be
 *	recalculated.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The element and all its ancestores are marked dirty
 *--------------------------------------------------------------
 */
void Tix_HLMarkElementDirty(wPtr, chPtr)
    WidgetPtr wPtr;
    HListElement *chPtr;
{
    HListElement *ptr;

    for (ptr=chPtr; ptr!= NULL && ptr->dirty == 0; ptr=ptr->parent) {
	ptr->dirty = 1;
    }
}

/*
 *--------------------------------------------------------------
 *
 * ComputeElementGeometry --
 *
 *	Compute the geometry of this element (if its dirty) and the
 *	geometry of all its dirty child elements
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The element and all its decendants are marked non-dirty
 *--------------------------------------------------------------
 */

static void ComputeElementGeometry(wPtr, chPtr, indent)
    WidgetPtr wPtr;
    HListElement *chPtr;
    int indent;
{
    HListElement *ptr;
    int i;

    if (!chPtr->dirty && !wPtr->allDirty) {
	return;
    } else {
	chPtr->dirty = 0;
    }

    if (chPtr == wPtr->root) {
	int i;
	chPtr->height = 0;
	chPtr->indent = 0;
	for (i=0; i<wPtr->numColumns; i++) {
	    chPtr->col[i].width = 0;
	}
    } else {
	ComputeOneElementGeometry(wPtr, chPtr, indent);
	indent += wPtr->indent;
    }

    chPtr->allHeight = chPtr->height;

    for (ptr=chPtr->childHead; ptr!=NULL; ptr=ptr->next) {
	if (ptr->hidden) {
	    continue;
	}
	if (ptr->dirty || wPtr->allDirty) {
	    ComputeElementGeometry(wPtr, ptr, indent);
	}

	/* Propagate the child's size to the parent
	 *
	 */
	for (i=0; i<wPtr->numColumns; i++) {
	    if (chPtr->col[i].width < ptr->col[i].width) {
		chPtr->col[i].width = ptr->col[i].width;
	    }
	}
	chPtr->allHeight += ptr->allHeight;
    }
}

/*
 *--------------------------------------------------------------
 *
 * ComputeOneElementGeometry --
 *
 *	Compute the geometry of the element itself, not including
 *	its children, according to its current display type.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The chPtr->height fields are updated.
 *--------------------------------------------------------------
 */
static void ComputeOneElementGeometry(wPtr, chPtr, indent)
    WidgetPtr wPtr;
    HListElement *chPtr;
    int indent;
{
    int i;

    chPtr->indent = indent;
    chPtr->height = 0;

    ComputeBranchPosition(wPtr, chPtr);

    for (i=0; i<wPtr->numColumns; i++) {
	Tix_DItem * iPtr = chPtr->col[i].iPtr;
	int width  = 2*wPtr->selBorderWidth;
	int height = 2*wPtr->selBorderWidth;

	if (iPtr != NULL) {
	    Tix_DItemCalculateSize(iPtr);
	    /* Tix_DItemWidth() and Tix_DItemHeight() already include padding
	     */
	    width  += Tix_DItemWidth (iPtr);
	    height += Tix_DItemHeight(iPtr);
	}
	if (chPtr->height < height) {
	    chPtr->height = height;
	}
	chPtr->col[i].width = width;
    }
    chPtr->col[0].width += indent;
}

/*
 *--------------------------------------------------------------
 *
 * ComputeBranchPosition --
 *
 *	Compute the position of the branches
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The chPtr->branchX and chPtr->branchY fields are updated.
 *--------------------------------------------------------------
 */
static void ComputeBranchPosition(wPtr, chPtr)
    WidgetPtr wPtr;
    HListElement *chPtr;
{
    Tix_DItem * iPtr = chPtr->col[0].iPtr;
    int branchX, branchY;
    int iconX;
    int iconY;
    int diff;

    if (iPtr) {
	if (Tix_DItemType(iPtr) == TIX_DITEM_IMAGETEXT) {
	    /*
	     * Calculate the bottom-middle position of the bitmap/image branch
	     */
	    if (iPtr->imagetext.image != NULL) {
		branchX = iPtr->imagetext.imageW / 2;
		branchY = iPtr->imagetext.imageH;
		if (Tix_DItemHeight(iPtr) > iPtr->imagetext.imageH) {
		    branchY +=	(Tix_DItemHeight(iPtr) -
			iPtr->imagetext.imageH) /2;
		}
	    }
	    else if (iPtr->imagetext.bitmap != None) {
		branchX = iPtr->imagetext.bitmapW / 2;
		branchY = iPtr->imagetext.bitmapH;
		if (Tix_DItemHeight(iPtr) >iPtr->imagetext.bitmapH) {
		    branchY += (Tix_DItemHeight(iPtr) -
			iPtr->imagetext.bitmapH) /2;
		}
	    }
	    else {
		branchX = wPtr->indent/2;
		branchY = Tix_DItemHeight(iPtr);
	    }
	} else {
	    branchX = wPtr->indent/2;
	    branchY = Tix_DItemHeight(iPtr);
	}


	/* X adjustment
	 */
	iconX = Tix_DItemPadX(iPtr);
	branchX += Tix_DItemPadX(iPtr);

	/* Y adjustment
	 */
	iconY = Tix_DItemHeight(iPtr) / 2;
	diff = chPtr->height - Tix_DItemHeight(iPtr);
	if (diff > 0) {
	    switch (iPtr->base.stylePtr->anchor) {
	      case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
		diff = 0;
		break;
	      case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
		diff /= 2;
		break;
	      default:
		/* Do nothing */
		;
	    }
	    branchY += diff;
	    iconY   += diff;
	}
    }
    else {
	branchX = wPtr->indent/2;
	branchY = chPtr->height;
	iconX	= 0;
	iconY	= chPtr->height/2;
    }

    if (wPtr->useIndicator && chPtr->parent == wPtr->root) {
	branchX += wPtr->indent;
    }

    chPtr->branchX = branchX - 1;
    chPtr->branchY = branchY - 1;
    chPtr->iconX   = iconX   - 1;
    chPtr->iconY   = iconY   - 1;

    if (chPtr->branchX < 0) {
	chPtr->branchX = 0;
    }
    if (chPtr->branchY < 0) {
	chPtr->branchY = 0;
    }
    if (chPtr->iconX < 0) {
	chPtr->iconX = 0;
    }
    if (chPtr->iconY < 0) {
	chPtr->iconY = 0;
    }

    chPtr->branchX += wPtr->selBorderWidth;
    chPtr->branchY += wPtr->selBorderWidth;
    chPtr->iconX   += wPtr->selBorderWidth;
    chPtr->iconY   += wPtr->selBorderWidth;
}
/*
 *----------------------------------------------------------------------
 *
 * 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;
    Drawable buffer;
    Tk_Window tkwin = wPtr->dispData.tkwin;
    int elmX, elmY;
    Tcl_Interp *interp = wPtr->dispData.interp;

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

    if (wPtr->elmToSee != NULL) {
	HListElement *chPtr;

	if ((chPtr = Tix_HLFindElement(interp, wPtr,
		wPtr->elmToSee)) == NULL) {
	    Tcl_ResetResult(interp);
	} else {
	    Tix_HLSeeElement(wPtr, chPtr, 0);
	    UpdateScrollBars(wPtr, 0);
	}

	ckfree(wPtr->elmToSee);
	wPtr->elmToSee = NULL;
    }


    /*
     *	STEP (1)
     *		Calculate the drawing parameters
     */
    if (wPtr->wideSelect) {
	wPtr->selectWidth = Tk_Width(wPtr->dispData.tkwin) -
	  (2*wPtr->borderWidth + 2*wPtr->highlightWidth);
	if (wPtr->selectWidth < wPtr->totalSize[0]) {
	    wPtr->selectWidth = wPtr->totalSize[0];
	}
    }

    /* Used to clip off elements that are too low to see */
    wPtr->bottomPixel = Tk_Height(wPtr->dispData.tkwin) - 2*wPtr->borderWidth
      - 2*wPtr->highlightWidth;

    elmX = wPtr->borderWidth + wPtr->highlightWidth - wPtr->leftPixel;
    elmY = wPtr->borderWidth + wPtr->highlightWidth - wPtr->topPixel;

    if (wPtr->useHeader) {
	elmY += wPtr->headerHeight;
    }

    /*
     *	STEP (2)
     *		Draw the list body
     */
    buffer = Tix_GetRenderBuffer(wPtr->dispData.display, Tk_WindowId(tkwin),
	Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));

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

    DrawElements(wPtr, buffer, wPtr->normalGC, wPtr->root,
	elmX, elmY,
	wPtr->borderWidth + wPtr->highlightWidth - wPtr->leftPixel);

    if (wPtr->borderWidth > 0) {
	/* Draw the border */
	Tk_Draw3DRectangle(wPtr->dispData.tkwin, buffer, wPtr->border,
	    wPtr->highlightWidth, wPtr->highlightWidth,
	    Tk_Width(tkwin)  - 2*wPtr->highlightWidth,
	    Tk_Height(tkwin) - 2*wPtr->highlightWidth, wPtr->borderWidth,
	    wPtr->relief);
    }

    if (wPtr->highlightWidth > 0) {
	/* Draw the highlight */
	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, buffer);
    }

    if (buffer != Tk_WindowId(tkwin)) {
	/*
	 * Copy the information from the off-screen pixmap onto the screen,
	 * then delete the pixmap.
	 */

	XCopyArea(wPtr->dispData.display, buffer, Tk_WindowId(tkwin),
	    wPtr->normalGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
	Tk_FreePixmap(wPtr->dispData.display, buffer);
    }

    /*
     *	STEP (3)
     *		Draw the header
     */
    if (wPtr->useHeader) {
	/* We need to draw the header after the elements, because some
	 * half-scrolled elements may overwrite the space for the header
	 */
	int hdrX, hdrY, hdrW, hdrH, pad, xOffset;
	Drawable buffer;

	pad  = wPtr->borderWidth + wPtr->highlightWidth;
	hdrX = pad;
	hdrY = pad;
	hdrW = Tk_Width(tkwin) - 2*pad;
	hdrH = wPtr->headerHeight;
	xOffset = wPtr->leftPixel;

	Tk_MoveResizeWindow(wPtr->headerWin, hdrX, hdrY, hdrW, hdrH);
	Tk_MapWindow(wPtr->headerWin);

	buffer = Tix_GetRenderBuffer(wPtr->dispData.display,
	    Tk_WindowId(wPtr->headerWin), hdrW, hdrH,
	    Tk_Depth(wPtr->headerWin));

	XFillRectangle(wPtr->dispData.display, buffer,
	    wPtr->backgroundGC, 0, 0, hdrW, hdrH);

	Tix_HLDrawHeader(wPtr, buffer, wPtr->normalGC,
	    0, 0, hdrW, hdrH, xOffset);

	if (buffer != Tk_WindowId(wPtr->headerWin)) {
	    XCopyArea(wPtr->dispData.display, buffer,
		Tk_WindowId(wPtr->headerWin), wPtr->normalGC,
		0, 0, hdrW, hdrH, 0, 0);

	    Tk_FreePixmap(wPtr->dispData.display, buffer);
	}

	/* If we map the header window, that may change the size requirement
	 * of the HList
	 * %% Call only when geometry is *really* changed
	 */
	if (wPtr->sizeCmd) {
	    if (LangDoCallback(wPtr->dispData.interp, wPtr->sizeCmd, 0, 0) != TCL_OK) {
		Tcl_AddErrorInfo(wPtr->dispData.interp,
		    "\n    (size command executed by tixHList)");
		Tcl_BackgroundError(wPtr->dispData.interp);
	    }
	}
    } else {
	Tk_UnmapWindow(wPtr->headerWin);
    }

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

/*
 *----------------------------------------------------------------------
 *
 * DrawElements --
 *--------------------------------------------------------------
 */
static void DrawElements(wPtr, pixmap, gc, chPtr, x, y, xOffset)
    WidgetPtr wPtr;
    Pixmap pixmap;
    GC gc;
    HListElement * chPtr;
    int x;
    int y;
    int xOffset;
{
    HListElement * ptr, * lastVisible;
    int myIconX = 0, myIconY = 0;		/* center of my icon */
    int childIconX, childIconY;		/* center of child's icon */
    int childY, childX;
    int oldY;
    int top    = wPtr->useHeader ? wPtr->headerHeight : 0,
	left   = 0,
	bottom = Tk_Height(wPtr->dispData.tkwin),
	right  = Tk_Width(wPtr->dispData.tkwin);

    if (chPtr != wPtr->root) {
	if (bottom > y  && (y + chPtr->height) >= top) {
	    /* Otherwise element is not see at all */
	    DrawOneElement(wPtr, pixmap, gc, chPtr, x, y, xOffset);
	}
	myIconX = x + chPtr->branchX;
	myIconY = y + chPtr->branchY;

	if (wPtr->useIndicator && chPtr->parent == wPtr->root) {
	    childX = x + 2 * wPtr->indent;
	} else {
	    childX = x +  wPtr->indent;
	}
	childY = y + chPtr->height;
	if (myIconX > childX) {
	    /* Can't shift the vertical branch too much to the right */
	    myIconX = childX;
	}
    } else {
	childX = x;
	childY = y;
    }

    oldY = childY;		/* saved for 2nd iteration */

    /* find the last non-hidden element,
     * to determine when to draw the vertical line
     */
    lastVisible = NULL;
    for (ptr = chPtr->childTail; ptr!=NULL; ptr=ptr->prev) {
	if (! ptr->hidden) {
	    lastVisible = ptr;
	    break;
	}
    }

    if (lastVisible == NULL) {
	/* No child is visible */
	return;
    }

    /* First iteration : draw the entries and branches */
    for (ptr = chPtr->childHead; ptr!=NULL; ptr=ptr->next) {
	if (ptr->hidden) {
	    continue;
	}

	childIconX = childX + ptr->iconX;
	childIconY = childY + ptr->iconY;

	if (bottom > childY && (childY + ptr->allHeight) >= top) {

	    /* Otherwise all descendants of ptr are not seen at all
	     */
	    DrawElements(wPtr, pixmap, gc, ptr, childX, childY, xOffset);

	    if (wPtr->drawBranch && chPtr != wPtr->root
		&& top <= childIconY && childIconY <= bottom
	    ) {
		/* Draw a horizontal branch to the child */
		XDrawLine(wPtr->dispData.display, pixmap, gc, myIconX,
		    childIconY, childIconX, childIconY);
	    }
	}

	/*
	 * -- no branches for toplevel elements
	 * -- for last element, draw a vertical branch, even if element
	 *    is not seen
	 */
	if (ptr == lastVisible      && wPtr->drawBranch
	    && chPtr != wPtr->root  && childIconY >= top
	    && left <= myIconX      && myIconX <= right
	) {
	    /* clip vertical lines to avoid wrap-around */
	    int y0 = myIconY < 0 ? 0 : myIconY,
		y1 = childIconY > bottom ? bottom : childIconY;

	    XDrawLine(wPtr->dispData.display, pixmap, gc, myIconX, y0,
		myIconX, y1);
	}
	childY += ptr->allHeight;
    }

    if (!wPtr->useIndicator) {
	return;
    }
    childY = oldY;

    /* Second iteration : draw the indicators */
    for (ptr = chPtr->childHead; ptr!=NULL; ptr=ptr->next) {
	int cY = childY;

	if (ptr->hidden) {
	    continue;
	}
	childY += ptr->allHeight;
	childIconY = cY + ptr->iconY;

	if (bottom > cY && (cY + ptr->allHeight) >= top
	    && ptr->indicator != NULL
	) {
	    /* Otherwise all descendants of ptr are not seen at all
	     */
	    int justMapped,
	        indW = Tix_DItemWidth (ptr->indicator),
	        indH = Tix_DItemHeight(ptr->indicator),
	        indY = childIconY - indH/2,
	        indX = (chPtr == wPtr->root
		       ? (wPtr->indent / 2 + wPtr->borderWidth
		         + wPtr->highlightWidth - wPtr->leftPixel)
		       : myIconX) - indW/2;
	    if ( indX > right || (indX + indW) < left
		 || indY > bottom ||(indY + indH) < top
	    ) {
		continue;  /* indicator not visible */
	    }

	    justMapped = 0;
	    if (Tix_DItemType(ptr->indicator) == TIX_DITEM_WINDOW) {
		Tix_SetWindowItemSerial(&wPtr->mappedWindows,
		    ptr->indicator, wPtr->serial);
		if (!Tk_IsMapped(ptr->indicator->window.tkwin)) {
		    justMapped = 1;
		}
	    }

	    /* Put down the indicator */
	    Tix_DItemDisplay(pixmap, gc, ptr->indicator,
		indX, indY, indW, indH,
		TIX_DITEM_NORMAL_FG|TIX_DITEM_NORMAL_BG);

	    if (justMapped) {
#if 1
		Tk_RestackWindow(ptr->indicator->window.tkwin, Below, NULL);
#else
		XLowerWindow(Tk_Display(ptr->indicator->window.tkwin),
		    Tk_WindowId(ptr->indicator->window.tkwin));
#endif
	    }
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * DrawOneElement --
 *--------------------------------------------------------------
 */
static void DrawOneElement(wPtr, pixmap, gc, chPtr, x, y, xOffset)
    WidgetPtr wPtr;
    Pixmap pixmap;
    GC gc;
    HListElement * chPtr;
    int x;
    int y;
    int xOffset;
{
    int i;
    int flags = TIX_DITEM_NORMAL_FG, bgFlags = 0;
    int selectWidth, selectX;

    x = xOffset + chPtr->indent;

    if (wPtr->wideSelect) {
	selectWidth = wPtr->selectWidth;
	selectX = xOffset;
    } else {
	selectWidth = Tix_DItemWidth(chPtr->col[0].iPtr)
	  + 2*wPtr->selBorderWidth;
	selectX = x;
    }

    if (chPtr->selected) {
	/*
	 * When the ditem is selected, we have already drawn the
	 * selection background ourself, so we don't want
	 * DitemDisplay() to draw any background for us. So in this
	 * case both TIX_DITEM_NORMAL_BG and TIX_DITEM_SELECTED_BG are
	 * *not* set
	 */
	Tk_Fill3DRectangle(wPtr->dispData.tkwin, pixmap, wPtr->selectBorder,
	    selectX, y, selectWidth, chPtr->height, wPtr->selBorderWidth,
	    TK_RELIEF_RAISED);
	gc = wPtr->selectGC;
	flags |= TIX_DITEM_SELECTED_FG;
    } else {
	/*
	 * Set the TIX_DITEM_NORMAL_BG. This will be used unless
	 * ACTIVE_BG and/or DISABLE_BG are set
	 */
	bgFlags |= TIX_DITEM_NORMAL_BG;
    }

    if (chPtr == wPtr->anchor) {
	flags |= TIX_DITEM_ACTIVE_FG;

	if (!chPtr->selected) {
	    /* don't set any background when the item is selected (otherwise
	     * it looks messed up when wideSelect is false
	     */
	    bgFlags |= TIX_DITEM_ACTIVE_BG;
	}
    }
    if (chPtr == wPtr->dropSite) {
	XDrawRectangle(Tk_Display(wPtr->dispData.tkwin), pixmap,
	    wPtr->dropSiteGC, selectX, y, selectWidth-1, chPtr->height-1);
    }

    /*
     * Now Draw the display items in each column
     *
     * %% ToDo: clip off the non-visible items
     */
    x = xOffset;
    for (i=0; i<wPtr->numColumns; i++) {
	int drawX = x;
	Tix_DItem * iPtr = chPtr->col[i].iPtr;
	int itemWidth;

	itemWidth = wPtr->actualSize[i].width - 2*wPtr->selBorderWidth;

	/*
	 * Draw the background: this is tricky because we have idented the
	 * first column. If we call Tix_DItemDisplay() with the background
	 * flags set, the first column will look ugly
	 */
	if (iPtr != NULL) {
	    Tix_DItemDrawBackground(pixmap, gc, iPtr,
		drawX + wPtr->selBorderWidth, y + wPtr->selBorderWidth,
		itemWidth,
		chPtr->height - 2*wPtr->selBorderWidth, bgFlags);
	}

	if (i == 0) {
	    drawX += chPtr->indent;
	    itemWidth -= chPtr->indent;
	}

	if (iPtr != NULL) {
	    int justMapped = 0;

	    if (Tix_DItemType(iPtr) == TIX_DITEM_WINDOW) {
		Tix_SetWindowItemSerial(&wPtr->mappedWindows,iPtr,
		    wPtr->serial);
		if (!Tk_IsMapped(iPtr->window.tkwin)) {
		    justMapped = 1;
		}
	    }

	    Tix_DItemDisplay(pixmap, gc, iPtr,
		drawX + wPtr->selBorderWidth, y + wPtr->selBorderWidth,
		itemWidth,
		chPtr->height - 2*wPtr->selBorderWidth, flags);

	    if (justMapped) {
		/*
		 * We need to lower it so that it doesn't
		 * overlap the header subwindow
		 */
#if 1
		Tk_RestackWindow(iPtr->window.tkwin, Below, NULL);
#else
		XLowerWindow(Tk_Display(iPtr->window.tkwin),
		    Tk_WindowId(iPtr->window.tkwin));
#endif

	    }
	}

	x += wPtr->actualSize[i].width;
    }

    if (chPtr == wPtr->anchor) {
	int ancW, ancH;
	ancW = selectWidth-1;
	ancH = chPtr->height-1;

	Tix_DrawAnchorLines(Tk_Display(wPtr->dispData.tkwin), pixmap,
	    wPtr->anchorGC, selectX, y, ancW, ancH);
    }
}

/*
 *----------------------------------------------------------------------
 * SelectionAdd --
 *--------------------------------------------------------------
 */
static void SelectionAdd(wPtr, chPtr)
    WidgetPtr wPtr;
    HListElement * chPtr;
{
    if (chPtr->selected) {		/* sanity check */
	return;
    }

    chPtr->selected = 1;
    SelectionNotifyAncestors(wPtr, chPtr->parent);
}

/*
 *----------------------------------------------------------------------
 * HL_SelectionClear --
 *--------------------------------------------------------------
 */
static void HL_SelectionClear(wPtr, chPtr)
    WidgetPtr wPtr;
    HListElement * chPtr;
{
    if (! chPtr->selected) {		/* sanity check */
	return;
    }

    chPtr->selected = 0;
    HL_SelectionClearNotifyAncestors(wPtr, chPtr->parent);
}

/*
 *----------------------------------------------------------------------
 * HL_SelectionClearAll --
 *--------------------------------------------------------------
 */
static void HL_SelectionClearAll(wPtr, chPtr, changed_ret)
    WidgetPtr wPtr;
    HListElement * chPtr;
    int * changed_ret;
{
    HListElement * ptr;

    if (chPtr->selected) {
	*changed_ret = 1;
	chPtr->selected = 0;
    }

    if (chPtr->numSelectedChild == 0) {
	return;
    } else {
	chPtr->numSelectedChild = 0;

	for (ptr=chPtr->childHead; ptr; ptr=ptr->next) {
	    HL_SelectionClearAll(wPtr, ptr, changed_ret);
	}
    }
}

/*
 *----------------------------------------------------------------------
 * SelectionNotifyAncestors --
 *
 *	!!This has nothing to do with SelectionNotify in X!!
 *
 *	HList keeps a counter in every entry on how many of its
 *	child entries has been selected. This will make the
 *	"selection clear" very efficient. To keep this counter
 *	up-to-date, we must call SelectionNotifyAncestors() or
 *	HL_SelectionClearNotifyAncestors every time the selection
 *	has changed.
 *--------------------------------------------------------------
 */
static void SelectionNotifyAncestors(wPtr, chPtr)
    WidgetPtr wPtr;
    HListElement * chPtr;
{
    chPtr->numSelectedChild ++;

    if (chPtr->selected || (chPtr->numSelectedChild > 1)) {
	/* My ancestors already know that I have selections */
	return;
    } else {
	if (chPtr != wPtr->root) {
	    SelectionNotifyAncestors(wPtr, chPtr->parent);
	}
    }
}

static void HL_SelectionClearNotifyAncestors(wPtr, chPtr)
    WidgetPtr wPtr;
    HListElement * chPtr;
{
    chPtr->numSelectedChild --;

    if (chPtr->selected || (chPtr->numSelectedChild > 0)) {
	/* I still have selections, don't need to notify parent */
	return;
    } else {
	if (chPtr != wPtr->root) {
	    SelectionNotifyAncestors(wPtr, chPtr->parent);
	}
    }
}
/*
 *--------------------------------------------------------------
 * DeleteOffsprings --
 *--------------------------------------------------------------
 */
static void DeleteOffsprings(wPtr, chPtr)
    WidgetPtr wPtr;
    HListElement * chPtr;
{
    HListElement * ptr;
    HListElement * toFree;

    ptr=chPtr->childHead;
    while (ptr) {
	DeleteOffsprings(wPtr, ptr);
	toFree = ptr;
	ptr=ptr->next;
	FreeElement(wPtr, toFree);
    }

    chPtr->childHead = 0;
    chPtr->childTail = 0;
}

/*
 *--------------------------------------------------------------
 * DeleteSiblings --
 *--------------------------------------------------------------
 */
static void DeleteSiblings(wPtr, chPtr)
    WidgetPtr wPtr;
    HListElement * chPtr;
{
    HListElement * ptr;

    for (ptr=chPtr->parent->childHead; ptr; ptr=ptr->next) {
	if (ptr != chPtr) {
	    DeleteNode(wPtr, ptr);
	}
    }
}

/*
 *----------------------------------------------------------------------
 * DeleteNode --
 *--------------------------------------------------------------
 */
static void DeleteNode(wPtr, chPtr)
    WidgetPtr wPtr;
    HListElement * chPtr;
{
    if (chPtr->parent == NULL) {
	/*
	 * This is root node : can't delete
	 */
	return;
    }

    DeleteOffsprings(wPtr, chPtr);

    /*
     * Check for deleting parent's first child
     */
    if (chPtr == chPtr->parent->childHead) {
	chPtr->parent->childHead = chPtr->next;
    }
    else {
	chPtr->prev->next = chPtr->next;
    }

    /*
     * Check for 'last' child (could be both first AND last)
     */
    if (chPtr == chPtr->parent->childTail) {
	chPtr->parent->childTail = chPtr->prev;
    }
    else {
	chPtr->next->prev = chPtr->prev;
    }

    FreeElement(wPtr, chPtr);
}

/*
 *----------------------------------------------------------------------
 * UpdateOneScrollBar --
 *--------------------------------------------------------------
 */
static void UpdateOneScrollBar(wPtr, command, total, window, first)
    WidgetPtr wPtr;
    LangCallback *command;
    int total;
    int window;
    int first;
{
    double d_first, d_last;

    GetScrollFractions(total, window, first, &d_first, &d_last);

    if (LangDoCallback(wPtr->dispData.interp, command, 0, 2, " %g %g", d_first, d_last)
	!= TCL_OK) {
	Tcl_AddErrorInfo(wPtr->dispData.interp,
		"\n    (scrolling command executed by tixHList)");
	Tcl_BackgroundError(wPtr->dispData.interp);
    }
}

/*----------------------------------------------------------------------
 *  UpdateScrollBars
 *----------------------------------------------------------------------
 */
static void UpdateScrollBars(wPtr, sizeChanged)
    WidgetPtr wPtr;
    int sizeChanged;
{
    int total, window, first;

    CheckScrollBar(wPtr, TIX_X);
    CheckScrollBar(wPtr, TIX_Y);

    if (wPtr->xScrollCmd) {
	total  = wPtr->totalSize[0];
	window = Tk_Width(wPtr->dispData.tkwin)
	  - 2*wPtr->borderWidth - 2*wPtr->highlightWidth;
	first  = wPtr->leftPixel;
	UpdateOneScrollBar(wPtr, wPtr->xScrollCmd, total, window, first);
    }

    if (wPtr->yScrollCmd) {
	total  = wPtr->totalSize[1];
	window = Tk_Height(wPtr->dispData.tkwin)
	  - 2*wPtr->borderWidth - 2*wPtr->highlightWidth;
	first  = wPtr->topPixel;

	if (wPtr->useHeader) {
	    window -= wPtr->headerHeight;
	}
	UpdateOneScrollBar(wPtr, wPtr->yScrollCmd, total, window, first);
    }

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

/*----------------------------------------------------------------------
 * XScrollByUnits
 *----------------------------------------------------------------------
 */
static int XScrollByUnits(wPtr, count)
    WidgetPtr wPtr;
    int count;
{
    return wPtr->leftPixel + count*wPtr->scrollUnit[0];
}

/*----------------------------------------------------------------------
 * XScrollByPages
 *----------------------------------------------------------------------
 */
static int XScrollByPages(wPtr, count)
    WidgetPtr wPtr;
    int count;
{
    return wPtr->leftPixel + count*Tk_Width(wPtr->dispData.tkwin);
}

/*----------------------------------------------------------------------
 * YScrollByUnits
 *----------------------------------------------------------------------
 */
static int YScrollByUnits(wPtr, count)
    WidgetPtr wPtr;
    int count;
{
    HListElement * chPtr;
    int height;

    if ((chPtr = FindElementAtPosition(wPtr, 0))) {
	height = chPtr->height;
    } else if (wPtr->root->childHead) {
	height = wPtr->root->childHead->height;
    } else {
	height = 0;
    }

    return wPtr->topPixel + count*height;
}

/*----------------------------------------------------------------------
 * YScrollByPages
 *----------------------------------------------------------------------
 */
static int YScrollByPages(wPtr, count)
    WidgetPtr wPtr;
    int count;
{
    int window = Tk_Height(wPtr->dispData.tkwin)
      - 2*wPtr->borderWidth - 2*wPtr->highlightWidth;

    if (wPtr->useHeader) {
	window -= wPtr->headerHeight;
    }

    return wPtr->topPixel + count*window;
}

/*----------------------------------------------------------------------
 * CheckScrollBar
 *
 *	Make sures that the seeting of the scrollbars are correct: i.e.
 *	the bottom element will never be scrolled up by too much.
 *----------------------------------------------------------------------
 */
static void CheckScrollBar(wPtr, which)
    WidgetPtr wPtr;
    int which;
{
    int window;
    int total;
    int first;

    if (which == TIX_Y) {
	window = Tk_Height(wPtr->dispData.tkwin)
	  - 2*wPtr->borderWidth - 2*wPtr->highlightWidth;
	if (wPtr->useHeader) {
	    window -= wPtr->headerHeight;
	}
	total  = wPtr->totalSize[1];
	first  = wPtr->topPixel;
    } else {
	window = Tk_Width(wPtr->dispData.tkwin)
	  - 2*wPtr->borderWidth - 2*wPtr->highlightWidth;
	total  = wPtr->totalSize[0];
	first  = wPtr->leftPixel;
    }

    /* Check whether the topPixel is out of bound */
    if (first < 0) {
	first = 0;
    } else {
	if (window > total) {
	    first = 0;
	} else if ((first + window) > total) {
	    first = total - window;
	}
    }

    if (which == TIX_Y) {
	wPtr->topPixel = first;
    } else {
	wPtr->leftPixel = first;
    }
}

/*----------------------------------------------------------------------
 * GetScrollFractions --
 *
 * Compute the fractions of a scroll-able widget.
 *
 */
static void GetScrollFractions(total, window, first, first_ret, last_ret)
    int total;
    int window;
    int first;
    double * first_ret;
    double * last_ret;
{
    if (total == 0 || total < window) {
	*first_ret = 0.0;
	*last_ret  = 1.0;
    } else {
	*first_ret = (double)(first) / (double)(total);
	*last_ret  = (double)(first+window) / (double)(total);
    }
}

/*----------------------------------------------------------------------
 * Find the element that's immediately below this element.
 *
 *----------------------------------------------------------------------
 */
static HListElement *
FindNextEntry(wPtr, chPtr)
    WidgetPtr wPtr;
    HListElement * chPtr;
{
    if (chPtr->childHead != NULL) {
	return chPtr->childHead;
    }
    if (chPtr->next) {
	return chPtr->next;
    }

    /* go to a different branch */
    while (1) {
	if (chPtr == wPtr->root) {
	    return (HListElement *)NULL;
	}
	chPtr = chPtr->parent;
	if (chPtr->next) {
	    return chPtr->next;
	}
    }
}

/*----------------------------------------------------------------------
 * Find the element that's immediately above this element.
 *
 *----------------------------------------------------------------------
 */
static HListElement *
FindPrevEntry(wPtr, chPtr)
    WidgetPtr wPtr;
    HListElement * chPtr;
{
    if (chPtr->prev) {
	/* Find the bottom of this sub-tree
	 */
	for (chPtr=chPtr->prev; chPtr->childTail; chPtr = chPtr->childTail)
	  ;

	return chPtr;
    } else {
	if (chPtr->parent == wPtr->root) {
	    return 0;
	} else {
	    return chPtr->parent;
	}
    }
}


/*----------------------------------------------------------------------
 * Recurse through all items and gather the -text arguments of selected
 * entries.
 *
 *----------------------------------------------------------------------
 */
static void
GetSelectedText(wPtr, chPtr, selection)
     WidgetPtr wPtr;
     HListElement * chPtr;
     Tcl_DString * selection;
{
    register HListElement * ptr;
    int needTab, j;

    for (ptr = chPtr->childHead; ptr; ptr = ptr->next) {
	if (ptr->selected && !ptr->hidden) {
	    needTab = 0;
	    for (j = 0; j < wPtr->numColumns; j++) {
	        Tix_DItem *iPtr = ptr->col[j].iPtr;
	        if (needTab) {
		    Tcl_DStringAppend(selection, "\t", 1);
		}
		if (iPtr) {
		    switch (Tix_DItemType(iPtr)) {
		        case TIX_DITEM_TEXT:
			    Tcl_DStringAppend(selection,
					      Tcl_GetString(iPtr->text.text),
					      iPtr->text.numChars);
			    break;
		        case TIX_DITEM_IMAGETEXT:
			    Tcl_DStringAppend(selection,
					      Tcl_GetString(iPtr->imagetext.text),
					      iPtr->imagetext.numChars);
			    break;
		    }
		}
		needTab = 1;
	    }
	    Tcl_DStringAppend(selection, "\n", 1);
	}
	if (!ptr->hidden) {
	    if (ptr->childHead) {
		GetSelectedText(wPtr, ptr, selection);
	    }
	}
    }
}


/*
 *----------------------------------------------------------------------
 *
 * HListFetchSelection --
 *
 *	This procedure is called back by Tk when the selection is
 *	requested by someone.  It returns part or all of the selection
 *	in a buffer provided by the caller.
 *
 * Results:
 *	The return value is the number of non-NULL bytes stored
 *	at buffer.  Buffer is filled (or partially filled) with a
 *	NULL-terminated string containing part or all of the selection,
 *	as given by offset and maxBytes.  The selection is returned
 *	as a Tcl list with one list element for each element in the
 *	listbox.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
HListFetchSelection(clientData, offset, buffer, maxBytes)
    ClientData clientData;		/* Information about hlist widget. */
    int offset;				/* Offset within selection of first
					 * byte to be returned. */
    char *buffer;			/* Location in which to place
					 * selection. */
    int maxBytes;			/* Maximum number of bytes to place
					 * at buffer, not including terminating
					 * NULL character. */
{
    register WidgetPtr wPtr = (WidgetPtr ) clientData;
    Tcl_DString selection;
    int length, count;

    if (!wPtr->exportSelection) {
	return -1;
    }

    /*
     * Use a dynamic string to accumulate the contents of the selection.
     */

    Tcl_DStringInit(&selection);
    GetSelectedText(wPtr, wPtr->root, &selection);

    length = Tcl_DStringLength(&selection);
    if (length == 0) {
	return -1;
    }

    /*
     * Copy the requested portion of the selection to the buffer.
     */

    count = length - offset;
    if (count <= 0) {
	count = 0;
    } else {
	if (count > maxBytes) {
	    count = maxBytes;
	}
	memcpy((VOID *) buffer,
		(VOID *) (Tcl_DStringValue(&selection) + offset),
		(size_t) count);
    }
    buffer[count] = '\0';
    Tcl_DStringFree(&selection);
    return count;
}

/*
 *----------------------------------------------------------------------
 *
 * HListLostSelection --
 *
 *	This procedure is called back by Tk when the selection is
 *	grabbed away from a HList widget.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The existing selection is unhighlighted, and the window is
 *	marked as not containing a selection.
 *
 *----------------------------------------------------------------------
 */

static void
HListLostSelection(clientData)
    ClientData clientData;		/* Information about listbox widget. */
{
    WidgetPtr wPtr = (WidgetPtr) clientData;
    int changed = 0;
    if ((wPtr->exportSelection) && (wPtr->root != NULL)) {
	HL_SelectionClearAll(wPtr, wPtr->root, &changed);
	if (changed) {
	    RedrawWhenIdle(wPtr);
	}
    }
}