diff options
Diffstat (limited to 'tix/generic/tixHList.c')
-rw-r--r-- | tix/generic/tixHList.c | 4417 |
1 files changed, 4417 insertions, 0 deletions
diff --git a/tix/generic/tixHList.c b/tix/generic/tixHList.c new file mode 100644 index 00000000000..1b217901df3 --- /dev/null +++ b/tix/generic/tixHList.c @@ -0,0 +1,4417 @@ +/* + * 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 <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_STRING, "-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_STRING, "-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_STRING, "-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_STRING, "-dropcmd", "dropCmd", "DropCmd", + DEF_HLIST_DROP_COMMAND, Tk_Offset(WidgetRecord, dropCmd), + TK_CONFIG_NULL_OK}, + + {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_STRING, "-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_STRING, "-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_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand", + DEF_HLIST_X_SCROLL_COMMAND, Tk_Offset(WidgetRecord, xScrollCmd), + TK_CONFIG_NULL_OK}, + + {TK_CONFIG_STRING, "-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_STRING, "-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_((ClientData 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)); +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, + char * 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 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 main = (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, main, 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); + + 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 ...?", + }; + + Tk_Preserve(clientData); + code = Tix_HandleSubCmds(&cmdInfo, subCmdInfo, clientData, + interp, argc, argv); + Tk_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]; + + argc --; + argv ++; + + if ((chPtr = NewElement(interp, wPtr, argc, argv, pathName, + NULL, &argc)) == NULL) { + return TCL_ERROR; + } + + if (argc > 0) { + if (ConfigElement(wPtr, chPtr, argc, argv, 0, 1) != TCL_OK) { + DeleteNode(wPtr, chPtr); + return TCL_ERROR; + } + } else { + if (Tix_DItemConfigure(chPtr->col[0].iPtr, 0, 0, 0) != TCL_OK) { + DeleteNode(wPtr, chPtr); + return TCL_ERROR; + } + } + + Tcl_AppendResult(interp, chPtr->pathName, NULL); + return TCL_OK; +} + +/*---------------------------------------------------------------------- + * "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; + + parentName = argv[0]; + if (argv[0] && strcmp(argv[0], "") == 0) { + parentName = NULL; + } + + argc --; + argv ++; + if ((chPtr = NewElement(interp, wPtr, argc, argv, NULL, + parentName, &argc)) == NULL) { + return TCL_ERROR; + } + + if (argc > 0) { + if (ConfigElement(wPtr, chPtr, argc, argv, 0, 1) != TCL_OK) { + DeleteNode(wPtr, chPtr); + return TCL_ERROR; + } + } else { + if (Tix_DItemConfigure(chPtr->col[0].iPtr, 0, 0, 0) != TCL_OK) { + DeleteNode(wPtr, chPtr); + return TCL_ERROR; + } + } + + Tcl_AppendResult(interp, chPtr->pathName, NULL); + return TCL_OK; +} + +/*---------------------------------------------------------------------- + * "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_GetInt(interp, argv[0], &qSize[0]) != TCL_OK) { + return TCL_ERROR; + } + if (Tcl_GetInt(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]); + + sprintf(string, "{%f %f} {%f %f}", first[0], last[0], first[1], last[1]); + Tcl_AppendResult(interp, string, NULL); + + 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_AppendResult(interp, chPtr->data, NULL); + 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_AppendResult(interp, "1", NULL); + } else { + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, "0", NULL); + } + 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_AppendElement(interp, "1"); + } else { + Tcl_AppendElement(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]; + + if (argc != 2) { + return Tix_ArgcError(interp, argc+3, argv-3, 3, "x y"); + } + if (Tcl_GetInt(interp, argv[0], &widX) != TCL_OK) { + return TCL_ERROR; + } + if (Tcl_GetInt(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_GetInt(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) { + sprintf(buff, "%d %d %d %d", x1, y1, x1+wXSize-1, y2); + Tcl_SetResult(interp, buff, TCL_VOLATILE); + } + 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 (oldLeft != left || oldTop != top) { + wPtr->leftPixel = left; + wPtr->topPixel = top; + + if (callRedraw) { + UpdateScrollBars(wPtr, 0); + 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; + + 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_AppendResult(interp, "1", NULL); + } else { + Tcl_AppendResult(interp, "0", NULL); + } + } + 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) { + 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) { + char string[20]; + + sprintf(string, "%d", wPtr->leftPixel); + Tcl_AppendResult(interp, string, NULL); + return TCL_OK; + } + else if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[0])) != NULL) { + leftPixel = Tix_HLElementLeftOffset(wPtr, chPtr); + } + else if (Tcl_GetInt(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) { + char string[20]; + + sprintf(string, "%d", wPtr->topPixel); + Tcl_AppendResult(interp, string, NULL); + return TCL_OK; + } + else if ((chPtr = Tix_HLFindElement(interp, wPtr, argv[0])) != NULL) { + topPixel = Tix_HLElementTopOffset(wPtr, chPtr); + } + else if (Tcl_GetInt(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; + + 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 = (char*)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); + + 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; + Tcl_DeleteCommand(wPtr->dispData.interp, + Tcl_GetCommandName(wPtr->dispData.interp, wPtr->widgetCmd)); + } + Tix_HLCancelResizeWhenIdle(wPtr); + CancelRedrawWhenIdle(wPtr); + Tk_EventuallyFree((ClientData)wPtr, (Tix_FreeProc*)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) + ClientData 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; + 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; + 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->resizing) { + wPtr->resizing = 1; + Tk_DoWhenIdle(Tix_HLComputeGeometry, (ClientData)wPtr); + } + if (wPtr->redrawing) { + CancelRedrawWhenIdle(wPtr); + } +} + +/* + *---------------------------------------------------------------------- + * Tix_HLResizeNow -- + *---------------------------------------------------------------------- + */ +void +Tix_HLResizeNow(wPtr) + WidgetPtr wPtr; +{ + if (wPtr->resizing) { + wPtr->resizing = 0; + Tk_CancelIdleCall(Tix_HLComputeGeometry, (ClientData)wPtr); + Tix_HLComputeGeometry((ClientData)wPtr); + } +} + +/* + *---------------------------------------------------------------------- + * Tix_HLCancelResizeWhenIdle -- + *---------------------------------------------------------------------- + */ +void +Tix_HLCancelResizeWhenIdle(wPtr) + WidgetPtr wPtr; +{ + if (wPtr->resizing) { + wPtr->resizing = 0; + Tk_CancelIdleCall(Tix_HLComputeGeometry, (ClientData)wPtr); + } +} + +/* + *---------------------------------------------------------------------- + * RedrawWhenIdle -- + *---------------------------------------------------------------------- + */ +static void +RedrawWhenIdle(wPtr) + WidgetPtr wPtr; +{ + if (!wPtr->redrawing && Tk_IsMapped(wPtr->dispData.tkwin)) { + wPtr->redrawing = 1; + Tk_DoWhenIdle(WidgetDisplay, (ClientData)wPtr); + } +} + +/* + *---------------------------------------------------------------------- + * CancelRedrawWhenIdle -- + *---------------------------------------------------------------------- + */ +static void +CancelRedrawWhenIdle(wPtr) + WidgetPtr wPtr; +{ + if (wPtr->redrawing) { + wPtr->redrawing = 0; + Tk_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 = (char*)tixStrDup(pathName); + } else { + chPtr->pathName = NULL; + } + + if (name) { + chPtr->name = (char*)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); + } + if (chPtr->data != NULL) { + ckfree(chPtr->data); + } + + 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) + Tcl_Interp *interp; + WidgetPtr wPtr; + int argc; + char ** argv; + 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; +{ +#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_GetInt(interp, argv[i+1], &at) != TCL_OK) { + chPtr = NULL; + goto done; + } + ++ numSwitches; + continue; + } + + copy: + if (n!=i) { + argv[n] = argv[i]; + argv[n+1] = argv[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 = (char*)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 (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; + } + } + } + } +} + +/* + *-------------------------------------------------------------- + * + * 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) { + if (select) { + SelectionAdd(wPtr, from); + } else { + HL_SelectionClear(wPtr, from); + changed = 1; + } + } + + 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); + } + + 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 (Tcl_GlobalEval(wPtr->dispData.interp, wPtr->sizeCmd) + != TCL_OK) { + Tcl_AddErrorInfo(wPtr->dispData.interp, + "\n (size command executed by tixHList)"); + Tk_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 topBorder; + + if (wPtr->useHeader) { + topBorder = wPtr->headerHeight; + } else { + topBorder = 0; + } + + if (chPtr != wPtr->root) { + if (wPtr->bottomPixel > y && (y + chPtr->height) >= topBorder) { + /* 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 (wPtr->bottomPixel > childY && + (childY + ptr->allHeight) >= topBorder) { + + /* Otherwise all descendants of ptr are not seen at all + */ + DrawElements(wPtr, pixmap, gc, ptr, childX, childY, xOffset); + + if (wPtr->drawBranch && chPtr != wPtr->root) { + /* Draw a horizontal branch to the child's image/bitmap */ + XDrawLine(wPtr->dispData.display, pixmap, gc, myIconX, + childIconY, childIconX, childIconY); + } + } + + if (wPtr->drawBranch && chPtr != wPtr->root) { + /* + * NB: no branches for toplevel elements + */ + if (ptr == lastVisible) { + /* Last element. Must draw a vertical branch, even if element + * is not seen + */ + int y0, y1; /* used to clip the vertical lines. Otherwise + * will wrap-around 65536 (max coordinate for + * X + */ + y0 = myIconY; + y1 = childIconY; + + if (y0 < 0) { + y0 = 0; + } + if (y1 > Tk_Height(wPtr->dispData.tkwin)) { + y1 = Tk_Height(wPtr->dispData.tkwin); + } + XDrawLine(wPtr->dispData.display, pixmap, gc, myIconX, y0, + myIconX, y1); + } + } + childY += ptr->allHeight; + } + + if (!wPtr->useIndicator) { + return; + } else { + childY = oldY; + } + + /* Second iteration : draw the indicators */ + for (ptr = chPtr->childHead; ptr!=NULL; ptr=ptr->next) { + int justMapped; + + if (ptr->hidden) { + continue; + } + + childIconY = childY + ptr->iconY; + + if (wPtr->bottomPixel > childY && + (childY + ptr->allHeight) >= topBorder) { + /* Otherwise all descendants of ptr are not seen at all + */ + if (ptr->indicator != NULL) { + int indW = Tix_DItemWidth (ptr->indicator); + int indH = Tix_DItemHeight(ptr->indicator); + int indX; + int indY = childIconY; + + if (chPtr == wPtr->root) { + indX = wPtr->indent / 2 + wPtr->borderWidth + + wPtr->highlightWidth - wPtr->leftPixel; + } else { + indX = myIconX; + } + + indX -= indW/2; + indY -= indH/2; + + 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) { + XLowerWindow(Tk_Display(ptr->indicator->window.tkwin), + Tk_WindowId(ptr->indicator->window.tkwin)); + } + } + } + childY += ptr->allHeight; + } +} + +/* + *---------------------------------------------------------------------- + * + * 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 + */ + XLowerWindow(Tk_Display(iPtr->window.tkwin), + Tk_WindowId(iPtr->window.tkwin)); + } + } + + 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; + char * command; + int total; + int window; + int first; +{ + char string[100]; + double d_first, d_last; + + GetScrollFractions(total, window, first, &d_first, &d_last); + + sprintf(string, " %g %g", d_first, d_last); + if (Tix_GlobalVarEval(wPtr->dispData.interp, command, string, + (char *) NULL) != TCL_OK) { + Tcl_AddErrorInfo(wPtr->dispData.interp, + "\n (scrolling command executed by tixHList)"); + Tk_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 (Tcl_GlobalEval(wPtr->dispData.interp, wPtr->sizeCmd) != TCL_OK) { + Tcl_AddErrorInfo(wPtr->dispData.interp, + "\n (size command executed by tixHList)"); + Tk_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; + } + } +} + |