summaryrefslogtreecommitdiff
path: root/blt/src/bltTreeViewColumn.c
diff options
context:
space:
mode:
Diffstat (limited to 'blt/src/bltTreeViewColumn.c')
-rw-r--r--blt/src/bltTreeViewColumn.c1881
1 files changed, 1881 insertions, 0 deletions
diff --git a/blt/src/bltTreeViewColumn.c b/blt/src/bltTreeViewColumn.c
new file mode 100644
index 00000000000..1f1c68d2c87
--- /dev/null
+++ b/blt/src/bltTreeViewColumn.c
@@ -0,0 +1,1881 @@
+/*
+ * bltTreeViewColumn.c --
+ *
+ * This module implements an hierarchy widget for the BLT toolkit.
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "treeview" widget was created by George A. Howlett.
+ */
+
+/*
+ * TODO:
+ *
+ * BUGS:
+ * 1. "open" operation should change scroll offset so that as many
+ * new entries (up to half a screen) can be seen.
+ * 2. "open" needs to adjust the scrolloffset so that the same entry
+ * is seen at the same place.
+ */
+#include "bltInt.h"
+
+#ifndef NO_TREEVIEW
+
+#include "bltTreeView.h"
+#include <X11/Xutil.h>
+
+static Blt_OptionParseProc ObjToColumn;
+static Blt_OptionPrintProc ColumnToObj;
+static Blt_OptionParseProc ObjToData;
+static Blt_OptionPrintProc DataToObj;
+
+static char *sortTypeStrings[] = {
+ "dictionary", "ascii", "integer", "real", "command", "none", NULL
+};
+
+enum SortTypeValues {
+ SORT_TYPE_DICTIONARY, SORT_TYPE_ASCII, SORT_TYPE_INTEGER,
+ SORT_TYPE_REAL, SORT_TYPE_COMMAND, SORT_TYPE_NONE
+};
+
+extern Blt_OptionParseProc Blt_ObjToEnum;
+extern Blt_OptionPrintProc Blt_EnumToObj;
+
+static Blt_CustomOption typeOption =
+{
+ Blt_ObjToEnum, Blt_EnumToObj, NULL, (ClientData)sortTypeStrings
+};
+
+static Blt_CustomOption columnOption =
+{
+ ObjToColumn, ColumnToObj, NULL, (ClientData)0
+};
+
+Blt_CustomOption bltTreeViewDataOption =
+{
+ ObjToData, DataToObj, NULL, (ClientData)0,
+};
+
+#define DEF_SORT_COLUMN (char *)NULL
+#define DEF_SORT_COMMAND (char *)NULL
+#define DEF_SORT_DECREASING "no"
+#define DEF_SORT_TYPE "dictionary"
+
+#define TOGGLE(x, mask) \
+ (((x) & (mask)) ? ((x) & ~(mask)) : ((x) | (mask)))
+#define CLAMP(val,low,hi) \
+ (((val) < (low)) ? (low) : ((val) > (hi)) ? (hi) : (val))
+
+#ifdef WIN32
+#define DEF_COLUMN_ACTIVE_TITLE_BG RGB_GREY85
+#else
+#define DEF_COLUMN_ACTIVE_TITLE_BG RGB_GREY90
+#endif
+#define DEF_COLUMN_ACTIVE_TITLE_FG STD_COLOR_ACTIVE_FG
+#define DEF_COLUMN_BACKGROUND (char *)NULL
+#define DEF_COLUMN_BIND_TAGS "all"
+#define DEF_COLUMN_BORDER_WIDTH STD_BORDERWIDTH
+#define DEF_COLUMN_COLOR RGB_BLACK
+#define DEF_COLUMN_EDIT "yes"
+#define DEF_COLUMN_FONT STD_FONT
+#define DEF_COLUMN_COMMAND (char *)NULL
+#define DEF_COLUMN_FORMAT_COMMAND (char *)NULL
+#define DEF_COLUMN_HIDE "no"
+#define DEF_COLUMN_JUSTIFY "center"
+#define DEF_COLUMN_MAX "0"
+#define DEF_COLUMN_MIN "0"
+#define DEF_COLUMN_PAD "2"
+#define DEF_COLUMN_RELIEF "flat"
+#define DEF_COLUMN_STATE "normal"
+#define DEF_COLUMN_TEXT (char *)NULL
+#define DEF_COLUMN_TITLE (char *)NULL
+#define DEF_COLUMN_TITLE_BACKGROUND STD_COLOR_NORMAL_BG
+#define DEF_COLUMN_TITLE_FONT STD_FONT
+#define DEF_COLUMN_TITLE_FOREGROUND STD_COLOR_NORMAL_FG
+#define DEF_COLUMN_TITLE_LABEL (char *)NULL
+#define DEF_COLUMN_TITLE_SHADOW (char *)NULL
+#define DEF_COLUMN_WEIGHT "1.0"
+#define DEF_COLUMN_WIDTH "0"
+#define DEF_COLUMN_RULE_DASHES "dot"
+
+#ifdef __STDC__
+static Blt_TreeCompareNodesProc CompareNodes;
+static Blt_TreeApplyProc SortApplyProc;
+#endif /* __STDC__ */
+
+extern Blt_CustomOption bltTreeViewUidOption;
+static Blt_TreeApplyProc SortApplyProc;
+
+static Blt_ConfigSpec columnSpecs[] =
+{
+ {BLT_CONFIG_BORDER, "-activetitlebackground", "activeTitleBackground",
+ "Background", DEF_COLUMN_ACTIVE_TITLE_BG,
+ Blt_Offset(TreeViewColumn, activeTitleBorder), 0},
+ {BLT_CONFIG_COLOR, "-activetitleforeground", "activeTitleForeground",
+ "Foreground", DEF_COLUMN_ACTIVE_TITLE_FG,
+ Blt_Offset(TreeViewColumn, activeTitleFgColor), 0},
+ {BLT_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_COLUMN_BACKGROUND, Blt_Offset(TreeViewColumn, border),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL,
+ 0, 0},
+ {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL,
+ 0, 0},
+ {BLT_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_COLUMN_BIND_TAGS, Blt_Offset(TreeViewColumn, tagsUid),
+ BLT_CONFIG_NULL_OK, &bltTreeViewUidOption},
+ {BLT_CONFIG_DISTANCE, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_COLUMN_BORDER_WIDTH, Blt_Offset(TreeViewColumn, borderWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-command", "command", "Command",
+ DEF_COLUMN_COMMAND, Blt_Offset(TreeViewColumn, command),
+ BLT_CONFIG_DONT_SET_DEFAULT | BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_BOOLEAN, "-edit", "edit", "Edit",
+ DEF_COLUMN_STATE, Blt_Offset(TreeViewColumn, editable),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+ {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_COLUMN_COLOR, Blt_Offset(TreeViewColumn, fgColor),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_FONT, "-font", "font", "Font",
+ DEF_COLUMN_FONT, Blt_Offset(TreeViewColumn, font), 0},
+ {BLT_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_COLUMN_HIDE, Blt_Offset(TreeViewColumn, hidden),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
+ DEF_COLUMN_JUSTIFY, Blt_Offset(TreeViewColumn, justify),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_DISTANCE, "-max", "max", "Max",
+ DEF_COLUMN_MAX, Blt_Offset(TreeViewColumn, reqMax),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_DISTANCE, "-min", "min", "Min",
+ DEF_COLUMN_MIN, Blt_Offset(TreeViewColumn, reqMin),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PAD, "-pad", "pad", "Pad",
+ DEF_COLUMN_PAD, Blt_Offset(TreeViewColumn, pad),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_COLUMN_RELIEF, Blt_Offset(TreeViewColumn, relief),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_DASHES, "-ruledashes", "ruleDashes", "RuleDashes",
+ DEF_COLUMN_RULE_DASHES, Blt_Offset(TreeViewColumn, rule.dashes),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_STRING, "-sortcommand", "sortCommand", "SortCommand",
+ DEF_SORT_COMMAND, Blt_Offset(TreeViewColumn, sortCmd),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_STATE, "-state", "state", "State",
+ DEF_COLUMN_STATE, Blt_Offset(TreeViewColumn, state),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-text", "text", "Text",
+ DEF_COLUMN_TITLE_LABEL, Blt_Offset(TreeViewColumn, text), 0},
+ {BLT_CONFIG_BORDER, "-titlebackground", "titleBackground",
+ "TitleBackground", DEF_COLUMN_TITLE_BACKGROUND,
+ Blt_Offset(TreeViewColumn, titleBorder),0},
+ {BLT_CONFIG_FONT, "-titlefont", "titleFont", "Font",
+ DEF_COLUMN_TITLE_FONT, Blt_Offset(TreeViewColumn, titleFont), 0},
+ {BLT_CONFIG_COLOR, "-titleforeground", "titleForeground", "TitleForeground",
+ DEF_COLUMN_TITLE_FOREGROUND,
+ Blt_Offset(TreeViewColumn, titleFgColor), 0},
+ {BLT_CONFIG_SHADOW, "-titleshadow", "titleShadow", "TitleShadow",
+ DEF_COLUMN_TITLE_SHADOW, Blt_Offset(TreeViewColumn, titleShadow), 0},
+ {BLT_CONFIG_DOUBLE, "-weight", (char *)NULL, (char *)NULL,
+ DEF_COLUMN_WEIGHT, Blt_Offset(TreeViewColumn, weight),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_DISTANCE, "-width", "width", "Width",
+ DEF_COLUMN_WIDTH, Blt_Offset(TreeViewColumn, reqWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+static Blt_ConfigSpec sortSpecs[] =
+{
+ {BLT_CONFIG_STRING, "-command", "command", "Command",
+ DEF_SORT_COMMAND, Blt_Offset(TreeView, sortCmd),
+ BLT_CONFIG_DONT_SET_DEFAULT | BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_CUSTOM, "-column", "column", "Column",
+ DEF_SORT_COLUMN, Blt_Offset(TreeView, sortColumnPtr),
+ BLT_CONFIG_DONT_SET_DEFAULT, &columnOption},
+ {BLT_CONFIG_BITFLAG, "-decreasing", "decreasing", "Decreasing",
+ DEF_SORT_DECREASING, Blt_Offset(TreeView, flags),
+ BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_DECREASING},
+ {BLT_CONFIG_CUSTOM, "-mode", "mode", "Mode",
+ DEF_SORT_TYPE, Blt_Offset(TreeView, sortType), 0, &typeOption},
+ {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+static int GetColumnFromObj _ANSI_ARGS_((Tcl_Interp *interp, TreeView *tvPtr,
+ Tcl_Obj *objPtr, TreeViewColumn **columnPtrPtr));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToColumn --
+ *
+ * Convert the string reprsenting a scroll mode, to its numeric
+ * form.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToColumn(clientData, interp, tkwin, objPtr, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ Tcl_Obj *objPtr; /* New legend position string */
+ char *widgRec;
+ int offset;
+{
+ TreeViewColumn **columnPtrPtr = (TreeViewColumn **)(widgRec + offset);
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ if (*string == '\0') {
+ *columnPtrPtr = NULL;
+ } else {
+ TreeView *tvPtr = (TreeView *)widgRec;
+
+ if (GetColumnFromObj(interp, tvPtr, objPtr, columnPtrPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnToString --
+ *
+ * Results:
+ * The string representation of the button boolean is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+ColumnToObj(clientData, interp, tkwin, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec;
+ int offset;
+{
+ TreeViewColumn *columnPtr = *(TreeViewColumn **)(widgRec + offset);
+
+ if (columnPtr == NULL) {
+ return Tcl_NewStringObj("", -1);
+ }
+ return Tcl_NewStringObj(columnPtr->key, -1);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToData --
+ *
+ * Convert the string reprsenting a scroll mode, to its numeric
+ * form.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToData(clientData, interp, tkwin, objPtr, widgRec, offset)
+ ClientData clientData; /* Node of entry. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ Tcl_Obj *objPtr; /* Tcl_Obj representing new data. */
+ char *widgRec;
+ int offset;
+{
+ Tcl_Obj **objv;
+ TreeViewColumn *columnPtr;
+ TreeViewEntry *entryPtr = (TreeViewEntry *)widgRec;
+ char *string;
+ int objc;
+ register int i;
+
+ string = Tcl_GetString(objPtr);
+ if (*string == '\0') {
+ return TCL_OK;
+ }
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc == 0) {
+ return TCL_OK;
+ }
+ if (objc & 0x1) {
+ Tcl_AppendResult(interp, "data \"", string,
+ "\" must be in even name-value pairs", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Load the keys in reverse */
+ for (i = objc - 2; i >= 0; i -= 2) {
+ TreeView *tvPtr = entryPtr->tvPtr;
+
+ if (GetColumnFromObj(interp, tvPtr, objv[i], &columnPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Blt_TreeSetValueByKey(tvPtr->interp, tvPtr->tree, entryPtr->node,
+ columnPtr->key, objv[i + 1]) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_TreeViewAddValue(entryPtr, columnPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DataToObj --
+ *
+ * Results:
+ * The string representation of the data is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+DataToObj(clientData, interp, tkwin, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec;
+ int offset;
+{
+ Tcl_Obj *listObjPtr, *objPtr;
+ TreeViewEntry *entryPtr = (TreeViewEntry *)widgRec;
+ TreeViewValue **values;
+ TreeViewValue *valuePtr;
+ int i, count;
+
+ /* Values are stored in a list last-to-first. Create an array to
+ * hold the pointer so that we can return them in reverse
+ * order. */
+
+ /* Find out how may values there are. */
+ count = 0;
+ for (valuePtr = entryPtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->nextPtr) {
+ count++;
+ }
+ /* Create a temporary array to store the values in reverse
+ * order. */
+ values = Blt_Malloc(sizeof(TreeViewValue *) * count);
+ for (i = count - 1, valuePtr = entryPtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->nextPtr, i--) {
+ values[i] = valuePtr;
+ }
+
+ /* Add the key-value pairs to a new Tcl_Obj */
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (i = 0; i < count; i++) {
+ valuePtr = values[i];
+ objPtr = Tcl_NewStringObj(valuePtr->columnPtr->key, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ objPtr = Blt_TreeViewGetData(entryPtr, valuePtr->columnPtr->key);
+ if (objPtr == NULL) {
+ objPtr = Tcl_NewStringObj("", -1);
+ }
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Blt_Free(values);
+ return listObjPtr;
+}
+
+static int
+GetColumnFromObj(interp, tvPtr, objPtr, columnPtrPtr)
+ Tcl_Interp *interp;
+ TreeView *tvPtr;
+ Tcl_Obj *objPtr;
+ TreeViewColumn **columnPtrPtr;
+{
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ if (strcmp(string, "treeView") == 0) {
+ *columnPtrPtr = &tvPtr->treeColumn;
+ } else {
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&tvPtr->columnTable, Blt_TreeGetKey(string));
+ if (hPtr == NULL) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't find column \"", string,
+ "\" in \"", Tk_PathName(tvPtr->tkwin), "\"",
+ (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ *columnPtrPtr = Blt_GetHashValue(hPtr);
+ }
+ return TCL_OK;
+}
+
+void
+Blt_TreeViewConfigureColumn(tvPtr, columnPtr)
+ TreeView *tvPtr;
+ TreeViewColumn *columnPtr;
+{
+ Drawable drawable;
+ GC newGC;
+ TextLayout *textPtr;
+ TextStyle ts;
+ Tk_3DBorder border;
+ XGCValues gcValues;
+ int ruleDrawn;
+ unsigned long gcMask;
+
+ gcMask = GCForeground | GCFont;
+ gcValues.foreground = columnPtr->fgColor->pixel;
+
+ gcValues.font = Tk_FontId(columnPtr->font);
+ newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+ if (columnPtr->gc != NULL) {
+ Tk_FreeGC(tvPtr->display, columnPtr->gc);
+ }
+ columnPtr->gc = newGC;
+
+ gcValues.foreground = columnPtr->titleFgColor->pixel;
+ gcValues.font = Tk_FontId(columnPtr->titleFont);
+ newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+ if (columnPtr->titleGC != NULL) {
+ Tk_FreeGC(tvPtr->display, columnPtr->titleGC);
+ }
+ columnPtr->titleGC = newGC;
+
+ gcValues.foreground = columnPtr->activeTitleFgColor->pixel;
+ gcValues.font = Tk_FontId(columnPtr->titleFont);
+ newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+ if (columnPtr->activeTitleGC != NULL) {
+ Tk_FreeGC(tvPtr->display, columnPtr->activeTitleGC);
+ }
+ columnPtr->activeTitleGC = newGC;
+
+ memset(&ts, 0, sizeof(TextStyle));
+ ts.font = columnPtr->titleFont;
+ ts.justify = TK_JUSTIFY_LEFT;
+ ts.shadow.offset = columnPtr->titleShadow.offset;
+ textPtr = Blt_GetTextLayout(columnPtr->text, &ts);
+ if (columnPtr->textPtr != NULL) {
+ Blt_Free(columnPtr->textPtr);
+ }
+ columnPtr->textPtr = textPtr;
+ columnPtr->titleWidth = columnPtr->textPtr->width + SORT_MARKER_WIDTH + 1;
+ gcMask = (GCFunction | GCLineWidth | GCLineStyle | GCForeground | GCFont);
+
+ /*
+ * If the rule is active, turn it off (i.e. draw again to erase
+ * it) before changing the GC. If the color changes, we won't be
+ * able to erase the old line, since it will no longer be
+ * correctly XOR-ed with the background.
+ */
+ drawable = Tk_WindowId(tvPtr->tkwin);
+ ruleDrawn = ((tvPtr->flags & TV_RULE_ACTIVE) &&
+ (tvPtr->activeColumnPtr == columnPtr) &&
+ (drawable != None));
+ if (ruleDrawn) {
+ Blt_TreeViewDrawRule(tvPtr, columnPtr, drawable);
+ }
+ gcValues.line_width = LineWidth(columnPtr->rule.lineWidth);
+ gcValues.foreground = columnPtr->fgColor->pixel;
+ if (LineIsDashed(columnPtr->rule.dashes)) {
+ gcValues.line_style = LineOnOffDash;
+ } else {
+ gcValues.line_style = LineSolid;
+ }
+ gcValues.function = GXxor;
+
+ border = CHOOSE(tvPtr->border, columnPtr->border);
+ gcValues.foreground ^= Tk_3DBorderColor(border)->pixel;
+ newGC = Blt_GetPrivateGC(tvPtr->tkwin, gcMask, &gcValues);
+ if (columnPtr->rule.gc != NULL) {
+ Blt_FreePrivateGC(tvPtr->display, columnPtr->rule.gc);
+ }
+ if (LineIsDashed(columnPtr->rule.dashes)) {
+ Blt_SetDashes(tvPtr->display, newGC, &columnPtr->rule.dashes);
+ }
+ columnPtr->rule.gc = newGC;
+ if (ruleDrawn) {
+ Blt_TreeViewDrawRule(tvPtr, columnPtr, drawable);
+ }
+ columnPtr->flags |= COLUMN_DIRTY;
+ tvPtr->flags |= TV_UPDATE;
+}
+
+static void
+DestroyColumn(tvPtr, columnPtr)
+ TreeView *tvPtr;
+ TreeViewColumn *columnPtr;
+{
+ Blt_HashEntry *hPtr;
+
+ bltTreeViewUidOption.clientData = tvPtr;
+ Blt_FreeObjOptions(columnSpecs, (char *)columnPtr, tvPtr->display, 0);
+
+ if (columnPtr->gc != NULL) {
+ Tk_FreeGC(tvPtr->display, columnPtr->gc);
+ }
+ if (columnPtr->titleGC != NULL) {
+ Tk_FreeGC(tvPtr->display, columnPtr->titleGC);
+ }
+ if (columnPtr->rule.gc != NULL) {
+ Blt_FreePrivateGC(tvPtr->display, columnPtr->rule.gc);
+ }
+ hPtr = Blt_FindHashEntry(&tvPtr->columnTable, columnPtr->key);
+ if (hPtr != NULL) {
+ Blt_DeleteHashEntry(&tvPtr->columnTable, hPtr);
+ }
+ if (columnPtr->linkPtr != NULL) {
+ Blt_ChainDeleteLink(tvPtr->colChainPtr, columnPtr->linkPtr);
+ }
+ if (columnPtr->text != NULL) {
+ Blt_Free(columnPtr->text);
+ }
+ if (columnPtr->textPtr != NULL) {
+ Blt_Free(columnPtr->textPtr);
+ }
+ if (columnPtr != &tvPtr->treeColumn) {
+ Blt_Free(columnPtr);
+ }
+}
+
+void
+Blt_TreeViewDestroyColumns(tvPtr)
+ TreeView *tvPtr;
+{
+ if (tvPtr->colChainPtr != NULL) {
+ Blt_ChainLink *linkPtr;
+ TreeViewColumn *columnPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ columnPtr->linkPtr = NULL;
+ DestroyColumn(tvPtr, columnPtr);
+ }
+ Blt_ChainDestroy(tvPtr->colChainPtr);
+ tvPtr->colChainPtr = NULL;
+ }
+ Blt_DeleteHashTable(&tvPtr->columnTable);
+}
+
+int
+Blt_TreeViewInitColumn(tvPtr, columnPtr, name, defTitle, objc, objv)
+ TreeView *tvPtr;
+ TreeViewColumn *columnPtr;
+ char *name, *defTitle;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ columnPtr->key = Blt_TreeGetKey(name);
+ columnPtr->text = Blt_Strdup(defTitle);
+ columnPtr->justify = TK_JUSTIFY_CENTER;
+ columnPtr->relief = TK_RELIEF_FLAT;
+ columnPtr->borderWidth = 1;
+ columnPtr->pad.side1 = columnPtr->pad.side2 = 2;
+ columnPtr->state = STATE_NORMAL;
+ columnPtr->weight = 1.0;
+ columnPtr->editable = FALSE;
+ columnPtr->type = TV_ITEM_COLUMN;
+ columnPtr->rule.type = TV_ITEM_RULE;
+ columnPtr->rule.lineWidth = 1;
+ columnPtr->rule.columnPtr = columnPtr;
+ hPtr = Blt_CreateHashEntry(&tvPtr->columnTable, columnPtr->key, &isNew);
+ Blt_SetHashValue(hPtr, columnPtr);
+
+ bltTreeViewUidOption.clientData = tvPtr;
+ if (Blt_ConfigureComponentFromObj(tvPtr->interp, tvPtr->tkwin, name,
+ Tk_GetUid("Column"), columnSpecs, objc, objv, (char *)columnPtr, 0)
+ != TCL_OK) {
+ DestroyColumn(tvPtr, columnPtr);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+static TreeViewColumn *
+CreateColumn(tvPtr, nameObjPtr, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Obj *nameObjPtr;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewColumn *columnPtr;
+
+ columnPtr = Blt_Calloc(1, sizeof(TreeViewColumn));
+ assert(columnPtr);
+ if (Blt_TreeViewInitColumn(tvPtr, columnPtr, Tcl_GetString(nameObjPtr),
+ Tcl_GetString(nameObjPtr), objc, objv) != TCL_OK) {
+ return NULL;
+ }
+ Blt_TreeViewConfigureColumn(tvPtr, columnPtr);
+ return columnPtr;
+}
+
+TreeViewColumn *
+Blt_TreeViewNearestColumn(tvPtr, x, y, flags)
+ TreeView *tvPtr;
+ int x, y;
+ int flags;
+{
+ if (flags & SEARCH_Y) {
+ if ((y < tvPtr->inset) || (y >= (tvPtr->titleHeight + tvPtr->inset))) {
+ return NULL;
+ }
+ }
+ if (tvPtr->nVisible > 0) {
+ Blt_ChainLink *linkPtr;
+ TreeViewColumn *columnPtr;
+ int right;
+
+ /*
+ * Determine if the pointer is over the rightmost portion of the
+ * column. This activates the rule.
+ */
+ x = WORLDX(tvPtr, x);
+ for(linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ right = columnPtr->worldX + columnPtr->width;
+ columnPtr->flags &= ~COLUMN_RULE_PICKED;
+ if ((x >= columnPtr->worldX) && (x <= right)) {
+#define RULE_AREA (8)
+ if (x >= (right - RULE_AREA)) {
+ columnPtr->flags |= COLUMN_RULE_PICKED;
+ }
+ return columnPtr;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnActivateOp --
+ *
+ * Selects the button to appear active.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnActivateOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ if (objc == 4) {
+ Drawable drawable;
+ TreeViewColumn *columnPtr;
+ char *string;
+
+ string = Tcl_GetString(objv[3]);
+ if (string[0] == '\0') {
+ columnPtr = NULL;
+ } else {
+ if (GetColumnFromObj(interp, tvPtr, objv[3], &columnPtr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (((tvPtr->flags & TV_SHOW_COLUMN_TITLES) == 0) ||
+ (columnPtr->hidden) || (columnPtr->state == STATE_DISABLED)) {
+ columnPtr = NULL;
+ }
+ }
+ tvPtr->activeColumnPtr = columnPtr;
+ drawable = Tk_WindowId(tvPtr->tkwin);
+ if (drawable != None) {
+ Blt_TreeViewDrawHeadings(tvPtr, drawable);
+ Blt_TreeViewDrawOuterBorders(tvPtr, drawable);
+ }
+ }
+ if (tvPtr->activeColumnPtr != NULL) {
+ Tcl_Obj *objPtr;
+
+ objPtr = Tcl_NewStringObj(tvPtr->activeColumnPtr->key, -1);
+ Tcl_SetObjResult(interp, objPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnBindOp --
+ *
+ * .t bind tag sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnBindOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ ClientData object;
+ TreeViewColumn *columnPtr;
+
+ if (GetColumnFromObj(NULL, tvPtr, objv[3], &columnPtr) == TCL_OK) {
+ object = columnPtr->key;
+ } else {
+ object = Blt_TreeViewGetUid(tvPtr, Tcl_GetString(objv[3]));
+ }
+ return Blt_ConfigureBindingsFromObj(interp, tvPtr->columnBindTable, object,
+ objc - 4, objv + 4);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnCgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnCgetOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewColumn *columnPtr;
+
+ if (GetColumnFromObj(interp, tvPtr, objv[3], &columnPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin, columnSpecs,
+ (char *)columnPtr, objv[4], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnConfigureOp --
+ *
+ * This procedure is called to process a list of configuration
+ * options database, in order to reconfigure the one of more
+ * entries in the widget.
+ *
+ * .h entryconfigure node node node node option value
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for tvPtr; old resources get freed, if there
+ * were any. The hypertext is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ColumnConfigureOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewColumn *columnPtr;
+ int nOptions, start;
+ register int i;
+
+ /* Figure out where the option value pairs begin */
+ for(i = 3; i < objc; i++) {
+ if (Blt_ObjIsOption(columnSpecs, objv[i], 0)) {
+ break;
+ }
+ if (GetColumnFromObj(interp, tvPtr, objv[i], &columnPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ start = i;
+ nOptions = objc - start;
+
+ bltTreeViewUidOption.clientData = tvPtr;
+ for (i = 3; i < start; i++) {
+ if (GetColumnFromObj(interp, tvPtr, objv[i], &columnPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (nOptions == 0) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, columnSpecs,
+ (char *)columnPtr, (Tcl_Obj *)NULL, 0);
+ } else if (nOptions == 1) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, columnSpecs,
+ (char *)columnPtr, objv[start], 0);
+ }
+ if (Blt_ConfigureWidgetFromObj(tvPtr->interp, tvPtr->tkwin,
+ columnSpecs, nOptions, objv + start, (char *)columnPtr,
+ BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_TreeViewConfigureColumn(tvPtr, columnPtr);
+ }
+ /*FIXME: Makes every change redo everything. */
+ tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnDeleteOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnDeleteOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewColumn *columnPtr;
+ TreeViewEntry *entryPtr;
+ register int i;
+
+ for(i = 3; i < objc; i++) {
+ if (GetColumnFromObj(interp, tvPtr, objv[i], &columnPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Traverse the tree deleting values associated with the column. */
+ for(entryPtr = tvPtr->rootPtr; entryPtr != NULL;
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr, 0)) {
+ if (entryPtr != NULL) {
+ TreeViewValue *valuePtr, *lastPtr, *nextPtr;
+
+ lastPtr = NULL;
+ for (valuePtr = entryPtr->values; valuePtr != NULL;
+ valuePtr = nextPtr) {
+ nextPtr = valuePtr->nextPtr;
+ if (valuePtr->columnPtr == columnPtr) {
+ Blt_TreeViewDestroyValue(tvPtr, valuePtr);
+ if (lastPtr == NULL) {
+ entryPtr->values = nextPtr;
+ } else {
+ lastPtr->nextPtr = nextPtr;
+ }
+ break;
+ }
+ lastPtr = valuePtr;
+ }
+ }
+ }
+ DestroyColumn(tvPtr, columnPtr);
+ }
+ /* Deleting a column may affect the height of an entry. */
+ tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnInsertOp --
+ *
+ * Add new columns to the tree.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnInsertOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_ChainLink *beforePtr;
+ Tcl_Obj *CONST *options;
+ TreeViewColumn *columnPtr;
+ TreeViewEntry *entryPtr;
+ int insertPos;
+ int nOptions;
+ int start;
+ register int i;
+
+ if (Blt_GetPositionFromObj(tvPtr->interp, objv[3], &insertPos) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((insertPos == -1) ||
+ (insertPos >= Blt_ChainGetLength(tvPtr->colChainPtr))) {
+ beforePtr = NULL;
+ } else {
+ beforePtr = Blt_ChainGetNthLink(tvPtr->colChainPtr, insertPos);
+ }
+ /*
+ * Count the column names that follow. Count the arguments until we
+ * spot one that looks like a configuration option (i.e. starts
+ * with a minus ("-")).
+ */
+ for (i = 4; i < objc; i++) {
+ if (Blt_ObjIsOption(columnSpecs, objv[i], 0)) {
+ break;
+ }
+ }
+ start = i;
+ nOptions = objc - i;
+ options = objv + start;
+
+ for (i = 4; i < start; i++) {
+ if (GetColumnFromObj(NULL, tvPtr, objv[i], &columnPtr) == TCL_OK) {
+ Tcl_AppendResult(interp, "column \"", Tcl_GetString(objv[i]),
+ "\" already exists", (char *)NULL);
+ return TCL_ERROR;
+ }
+ columnPtr = CreateColumn(tvPtr, objv[i], nOptions, options);
+ if (columnPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (beforePtr == NULL) {
+ columnPtr->linkPtr = Blt_ChainAppend(tvPtr->colChainPtr, columnPtr);
+ } else {
+ columnPtr->linkPtr = Blt_ChainNewLink();
+ Blt_ChainSetValue(columnPtr->linkPtr, columnPtr);
+ Blt_ChainLinkBefore(tvPtr->colChainPtr, columnPtr->linkPtr,
+ beforePtr);
+ }
+ /*
+ * Traverse the tree adding column entries where needed.
+ */
+ for(entryPtr = tvPtr->rootPtr; entryPtr != NULL;
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr, 0)) {
+ Blt_TreeViewAddValue(entryPtr, columnPtr);
+ }
+ Blt_TreeViewTraceColumn(tvPtr, columnPtr);
+ }
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnCurrentOp --
+ *
+ * Make the rule to appear active.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnCurrentOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv; /* Not used. */
+{
+ TreeViewColumn *columnPtr;
+
+ columnPtr = Blt_GetCurrentItem(tvPtr->columnBindTable);
+ if (columnPtr != NULL) {
+ if (columnPtr->type == TV_ITEM_RULE) {
+ Rule *rulePtr = (Rule *)columnPtr;
+ columnPtr = rulePtr->columnPtr;
+ }
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(columnPtr->key, -1));
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnInvokeOp --
+ *
+ * This procedure is called to invoke a column command.
+ *
+ * .h column invoke columnName
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnInvokeOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewColumn *columnPtr;
+ char *string;
+
+ string = Tcl_GetString(objv[3]);
+ if (string[0] == '\0') {
+ return TCL_OK;
+ }
+ if (GetColumnFromObj(interp, tvPtr, objv[3], &columnPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((columnPtr->state == STATE_NORMAL) && (columnPtr->command != NULL)) {
+ int result;
+
+ Tcl_Preserve(tvPtr);
+ Tcl_Preserve(columnPtr);
+ result = Tcl_GlobalEval(interp, columnPtr->command);
+ Tcl_Release(columnPtr);
+ Tcl_Release(tvPtr);
+ return result;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnMoveOp --
+ *
+ * Move a column.
+ *
+ * .h column move field1 position
+ *----------------------------------------------------------------------
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnNamesOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnNamesOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv; /* Not used. */
+{
+ Blt_ChainLink *linkPtr;
+ Tcl_Obj *listObjPtr, *objPtr;
+ TreeViewColumn *columnPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for(linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ objPtr = Tcl_NewStringObj(columnPtr->key, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+ColumnNearestOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ int x, y; /* Screen coordinates of the test point. */
+ TreeViewColumn *columnPtr;
+ int flags;
+
+ flags = 0;
+ if (Tk_GetPixelsFromObj(interp, tvPtr->tkwin, objv[3], &x) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc == 5) {
+ if (Tk_GetPixelsFromObj(interp, tvPtr->tkwin, objv[4], &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ flags |= SEARCH_Y;
+ }
+ columnPtr = Blt_TreeViewNearestColumn(tvPtr, x, y, flags);
+ if (columnPtr != NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(columnPtr->key, -1));
+ }
+ return TCL_OK;
+}
+
+static void
+UpdateMark(tvPtr, newMark)
+ TreeView *tvPtr;
+ int newMark;
+{
+ Drawable drawable;
+ TreeViewColumn *columnPtr;
+ int dx;
+ int width;
+
+ columnPtr = tvPtr->resizeColumnPtr;
+ if (columnPtr == NULL) {
+ return;
+ }
+ drawable = Tk_WindowId(tvPtr->tkwin);
+ if (drawable == None) {
+ return;
+ }
+
+ /* Erase any existing rule. */
+ if (tvPtr->flags & TV_RULE_ACTIVE) {
+ Blt_TreeViewDrawRule(tvPtr, columnPtr, drawable);
+ }
+
+ dx = newMark - tvPtr->ruleAnchor;
+ width = columnPtr->width -
+ (PADDING(columnPtr->pad) + 2 * columnPtr->borderWidth);
+ if ((columnPtr->reqMin > 0) && ((width + dx) < columnPtr->reqMin)) {
+ dx = columnPtr->reqMin - width;
+ }
+ if ((columnPtr->reqMax > 0) && ((width + dx) > columnPtr->reqMax)) {
+ dx = columnPtr->reqMax - width;
+ }
+ if ((width + dx) < 4) {
+ dx = 4 - width;
+ }
+ tvPtr->ruleMark = tvPtr->ruleAnchor + dx;
+
+ /* Redraw the rule if required. */
+ if (tvPtr->flags & TV_RULE_NEEDED) {
+ Blt_TreeViewDrawRule(tvPtr, columnPtr, drawable);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResizeActivateOp --
+ *
+ * Turns on/off the resize cursor.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ResizeActivateOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewColumn *columnPtr;
+ char *string;
+
+ string = Tcl_GetString(objv[4]);
+ if (string[0] == '\0') {
+ if (tvPtr->cursor != None) {
+ Tk_DefineCursor(tvPtr->tkwin, tvPtr->cursor);
+ } else {
+ Tk_UndefineCursor(tvPtr->tkwin);
+ }
+ tvPtr->resizeColumnPtr = NULL;
+ } else if (GetColumnFromObj(interp, tvPtr, objv[4], &columnPtr)
+ == TCL_OK) {
+ if (tvPtr->resizeCursor != None) {
+ Tk_DefineCursor(tvPtr->tkwin, tvPtr->resizeCursor);
+ }
+ tvPtr->resizeColumnPtr = columnPtr;
+ } else {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResizeAnchorOp --
+ *
+ * Set the anchor for the resize.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ResizeAnchorOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ int x;
+
+ if (Tcl_GetIntFromObj(NULL, objv[4], &x) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tvPtr->ruleAnchor = x;
+ tvPtr->flags |= TV_RULE_NEEDED;
+ UpdateMark(tvPtr, x);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResizeMarkOp --
+ *
+ * Sets the resize mark. The distance between the mark and the anchor
+ * is the delta to change the width of the active column.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ResizeMarkOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ int x;
+
+ if (Tcl_GetIntFromObj(NULL, objv[4], &x) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tvPtr->flags |= TV_RULE_NEEDED;
+ UpdateMark(tvPtr, x);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResizeSetOp --
+ *
+ * Returns the new width of the column including the resize delta.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ResizeSetOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv; /* Not used. */
+{
+ tvPtr->flags &= ~TV_RULE_NEEDED;
+ UpdateMark(tvPtr, tvPtr->ruleMark);
+ if (tvPtr->resizeColumnPtr != NULL) {
+ int width, delta;
+ TreeViewColumn *columnPtr;
+
+ columnPtr = tvPtr->resizeColumnPtr;
+ delta = (tvPtr->ruleMark - tvPtr->ruleAnchor);
+ width = tvPtr->resizeColumnPtr->width + delta -
+ (PADDING(columnPtr->pad) + 2 * columnPtr->borderWidth) - 1;
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(width));
+ }
+ return TCL_OK;
+}
+
+static Blt_OpSpec resizeOps[] =
+{
+ {"activate", 2, (Blt_Op)ResizeActivateOp, 5, 5, "column"},
+ {"anchor", 2, (Blt_Op)ResizeAnchorOp, 5, 5, "x"},
+ {"mark", 1, (Blt_Op)ResizeMarkOp, 5, 5, "x"},
+ {"set", 1, (Blt_Op)ResizeSetOp, 4, 4, "",},
+};
+
+static int nResizeOps = sizeof(resizeOps) / sizeof(Blt_OpSpec);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnResizeOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ColumnResizeOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nResizeOps, resizeOps, BLT_OP_ARG3,
+ objc, objv,0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (tvPtr, interp, objc, objv);
+ return result;
+}
+
+
+static Blt_OpSpec columnOps[] =
+{
+ {"activate", 1, (Blt_Op)ColumnActivateOp, 3, 4, "?field?",},
+ {"bind", 1, (Blt_Op)ColumnBindOp, 4, 6, "tagName ?sequence command?",},
+ {"cget", 2, (Blt_Op)ColumnCgetOp, 5, 5, "field option",},
+ {"configure", 2, (Blt_Op)ColumnConfigureOp, 4, 0,
+ "field ?option value?...",},
+ {"current", 2, (Blt_Op)ColumnCurrentOp, 3, 3, "",},
+ {"delete", 1, (Blt_Op)ColumnDeleteOp, 3, 0, "?field...?",},
+ {"highlight", 1, (Blt_Op)ColumnActivateOp, 3, 4, "?field?",},
+ {"insert", 3, (Blt_Op)ColumnInsertOp, 5, 0,
+ "position field ?field...? ?option value?...",},
+ {"invoke", 3, (Blt_Op)ColumnInvokeOp, 4, 4, "field",},
+ {"names", 2, (Blt_Op)ColumnNamesOp, 3, 3, "",},
+ {"nearest", 2, (Blt_Op)ColumnNearestOp, 4, 5, "x ?y?",},
+ {"resize", 1, (Blt_Op)ColumnResizeOp, 3, 0, "arg",},
+};
+static int nColumnOps = sizeof(columnOps) / sizeof(Blt_OpSpec);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewColumnOp --
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_TreeViewColumnOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nColumnOps, columnOps, BLT_OP_ARG2,
+ objc, objv,0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (tvPtr, interp, objc, objv);
+ return result;
+}
+
+
+static int
+InvokeCompare(tvPtr, e1Ptr, e2Ptr, command)
+ TreeView *tvPtr;
+ TreeViewEntry *e1Ptr, *e2Ptr;
+ char *command;
+{
+ int result;
+ Tcl_Obj *objv[8];
+ int i;
+
+ objv[0] = Tcl_NewStringObj(command, -1);
+ objv[1] = Tcl_NewStringObj(Tk_PathName(tvPtr->tkwin), -1);
+ objv[2] = Tcl_NewIntObj(Blt_TreeNodeId(e1Ptr->node));
+ objv[3] = Tcl_NewIntObj(Blt_TreeNodeId(e2Ptr->node));
+ objv[4] = Tcl_NewStringObj(tvPtr->sortColumnPtr->key, -1);
+
+ if (tvPtr->flatView) {
+ objv[5] = Tcl_NewStringObj(e1Ptr->fullName, -1);
+ objv[6] = Tcl_NewStringObj(e2Ptr->fullName, -1);
+ } else {
+ objv[5] = Tcl_NewStringObj(GETLABEL(e1Ptr), -1);
+ objv[6] = Tcl_NewStringObj(GETLABEL(e2Ptr), -1);
+ }
+ objv[7] = NULL;
+ result = Tcl_EvalObjv(tvPtr->interp, 7, objv, TCL_EVAL_GLOBAL);
+ if ((result != TCL_OK) ||
+ (Tcl_GetIntFromObj(tvPtr->interp, Tcl_GetObjResult(tvPtr->interp),
+ &result) != TCL_OK)) {
+ Tcl_BackgroundError(tvPtr->interp);
+ }
+ for(i = 0; i < 7; i++) {
+ Tcl_DecrRefCount(objv[i]);
+ }
+ Tcl_ResetResult(tvPtr->interp);
+ return result;
+}
+
+static TreeView *treeViewInstance;
+
+static int
+CompareEntries(a, b)
+ CONST void *a, *b;
+{
+ TreeView *tvPtr;
+ TreeViewEntry **e1PtrPtr = (TreeViewEntry **)a;
+ TreeViewEntry **e2PtrPtr = (TreeViewEntry **)b;
+ char *s1, *s2;
+ int result;
+
+ tvPtr = (*e1PtrPtr)->tvPtr;
+ s1 = (char *)(*e1PtrPtr)->data;
+ s2 = (char *)(*e2PtrPtr)->data;
+ result = 0;
+ switch (tvPtr->sortType) {
+ case SORT_TYPE_ASCII:
+ result = strcmp(s1, s2);
+ break;
+
+ case SORT_TYPE_COMMAND:
+ {
+ char *cmd;
+
+ cmd = tvPtr->sortColumnPtr->sortCmd;
+ if (cmd == NULL) {
+ cmd = tvPtr->sortCmd;
+ }
+ if (cmd == NULL) {
+ result = Blt_DictionaryCompare(s1, s2);
+ } else {
+ result = InvokeCompare(tvPtr, *e1PtrPtr, *e2PtrPtr, cmd);
+ }
+ }
+ break;
+
+ case SORT_TYPE_DICTIONARY:
+ result = Blt_DictionaryCompare(s1, s2);
+ break;
+
+ case SORT_TYPE_INTEGER:
+ {
+ int i1, i2;
+
+ if (Tcl_GetInt(NULL, s1, &i1) == TCL_OK) {
+ if (Tcl_GetInt(NULL, s2, &i2) == TCL_OK) {
+ result = i1 - i2;
+ } else {
+ result = -1;
+ }
+ } else if (Tcl_GetInt(NULL, s2, &i2) == TCL_OK) {
+ result = 1;
+ } else {
+ result = Blt_DictionaryCompare(s1, s2);
+ }
+ }
+ break;
+
+ case SORT_TYPE_REAL:
+ {
+ double r1, r2;
+
+ if (Tcl_GetDouble(NULL, s1, &r1) == TCL_OK) {
+ if (Tcl_GetDouble(NULL, s2, &r2) == TCL_OK) {
+ result = (r1 < r2) ? -1 : (r1 > r2) ? 1 : 0;
+ } else {
+ result = -1;
+ }
+ } else if (Tcl_GetDouble(NULL, s2, &r2) == TCL_OK) {
+ result = 1;
+ } else {
+ result = Blt_DictionaryCompare(s1, s2);
+ }
+ }
+ break;
+ }
+ if (tvPtr->flags & TV_DECREASING) {
+ return -result;
+ }
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CompareNodes --
+ *
+ * Comparison routine (used by qsort) to sort a chain of subnodes.
+ *
+ * Results:
+ * 1 is the first is greater, -1 is the second is greater, 0
+ * if equal.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+CompareNodes(n1Ptr, n2Ptr)
+ Blt_TreeNode *n1Ptr, *n2Ptr;
+{
+ TreeView *tvPtr = treeViewInstance;
+ TreeViewEntry *e1Ptr, *e2Ptr;
+
+ e1Ptr = NodeToEntry(tvPtr, *n1Ptr);
+ e2Ptr = NodeToEntry(tvPtr, *n2Ptr);
+
+ /* Fetch the data for sorting. */
+ if (tvPtr->sortType == SORT_TYPE_COMMAND) {
+ e1Ptr->data = (ClientData)Blt_TreeNodeId(*n1Ptr);
+ e2Ptr->data = (ClientData)Blt_TreeNodeId(*n2Ptr);
+ } else if (tvPtr->sortColumnPtr == &tvPtr->treeColumn) {
+ Tcl_DString dString;
+
+ Tcl_DStringInit(&dString);
+ if (e1Ptr->fullName == NULL) {
+ Blt_TreeViewGetFullName(tvPtr, e1Ptr, TRUE, &dString);
+ e1Ptr->fullName = Blt_Strdup(Tcl_DStringValue(&dString));
+ }
+ e1Ptr->data = (ClientData)e1Ptr->fullName;
+ if (e2Ptr->fullName == NULL) {
+ Blt_TreeViewGetFullName(tvPtr, e2Ptr, TRUE, &dString);
+ e2Ptr->fullName = Blt_Strdup(Tcl_DStringValue(&dString));
+ }
+ e2Ptr->data = (ClientData)e2Ptr->fullName;
+ Tcl_DStringFree(&dString);
+ } else {
+ Blt_TreeKey key;
+ Tcl_Obj *objPtr;
+
+ key = tvPtr->sortColumnPtr->key;
+ objPtr = Blt_TreeViewGetData(e1Ptr, key);
+ e1Ptr->data = (objPtr == NULL) ? "" : Tcl_GetString(objPtr);
+ objPtr = Blt_TreeViewGetData(e2Ptr, key);
+ e2Ptr->data = (objPtr == NULL) ? "" : Tcl_GetString(objPtr);
+ }
+ return CompareEntries(&e1Ptr, &e2Ptr);
+}
+
+static int
+SortAutoOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+
+ if (objc == 4) {
+ int bool;
+ int isAuto;
+
+ isAuto = ((tvPtr->flags & TV_AUTO_SORT) != 0);
+ if (Tcl_GetBooleanFromObj(interp, objv[3], &bool) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (isAuto != bool) {
+ tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ }
+ if (bool) {
+ tvPtr->flags |= TV_AUTO_SORT;
+ } else {
+ tvPtr->flags &= ~TV_AUTO_SORT;
+ }
+ }
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(tvPtr->flags & TV_AUTO_SORT));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SortCgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SortCgetOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin, sortSpecs,
+ (char *)tvPtr, objv[3], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SortConfigureOp --
+ *
+ * This procedure is called to process a list of configuration
+ * options database, in order to reconfigure the one of more
+ * entries in the widget.
+ *
+ * .h sort configure option value
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for tvPtr; old resources get freed, if there
+ * were any. The hypertext is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SortConfigureOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ unsigned int oldDirection;
+
+ if (objc == 3) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, sortSpecs,
+ (char *)tvPtr, (Tcl_Obj *)NULL, 0);
+ } else if (objc == 4) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, sortSpecs,
+ (char *)tvPtr, objv[3], 0);
+ }
+ oldDirection = tvPtr->flags & TV_DECREASING;
+ if (Blt_ConfigureWidgetFromObj(interp, tvPtr->tkwin, sortSpecs,
+ objc - 3, objv + 3, (char *)tvPtr, BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((Blt_ObjConfigModified(sortSpecs, "-column", "-mode", "-command",
+ (char *)NULL)) || ((tvPtr->flags & TV_DECREASING) == oldDirection)) {
+ tvPtr->flags &= ~TV_SORTED;
+ tvPtr->flags |= TV_DIRTY;
+ }
+ tvPtr->flags |= TV_LAYOUT | TV_SORT_PENDING;
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SortOnceOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr;
+ int recurse, result;
+ register int i;
+
+ recurse = FALSE;
+ if (objc > 3) {
+ char *string;
+ int length;
+
+ string = Tcl_GetString(objv[3]);
+ length = strlen(string);
+ if ((string[0] == '-') && (length > 1) &&
+ (strncmp(string, "-recurse", length) == 0)) {
+ objv++, objc--;
+ recurse = TRUE;
+ }
+ }
+ for (i = 3; i < objc; i++) {
+ if (Blt_TreeViewGetEntry(tvPtr, objv[i], &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (recurse) {
+ result = Blt_TreeApply(entryPtr->node, SortApplyProc, tvPtr);
+ } else {
+ result = SortApplyProc(entryPtr->node, tvPtr, TREE_PREORDER);
+ }
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ tvPtr->flags |= TV_LAYOUT;
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewSortOp --
+ *
+ * Comparison routine (used by qsort) to sort a chain of subnodes.
+ * A simple string comparison is performed on each node name.
+ *
+ * .h sort auto
+ * .h sort once -recurse root
+ *
+ * Results:
+ * 1 is the first is greater, -1 is the second is greater, 0
+ * if equal.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec sortOps[] =
+{
+ {"auto", 1, (Blt_Op)SortAutoOp, 3, 4, "?boolean?",},
+ {"cget", 2, (Blt_Op)SortCgetOp, 4, 4, "option",},
+ {"configure", 2, (Blt_Op)SortConfigureOp, 3, 0, "?option value?...",},
+ {"once", 1, (Blt_Op)SortOnceOp, 3, 0, "?-recurse? node...",},
+};
+static int nSortOps = sizeof(sortOps) / sizeof(Blt_OpSpec);
+
+/*ARGSUSED*/
+int
+Blt_TreeViewSortOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nSortOps, sortOps, BLT_OP_ARG2, objc,
+ objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (tvPtr, interp, objc, objv);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SortApplyProc --
+ *
+ * Sorts the subnodes at a given node.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SortApplyProc(node, clientData, order)
+ Blt_TreeNode node;
+ ClientData clientData;
+ int order; /* Not used. */
+{
+ TreeView *tvPtr = clientData;
+
+ if (!Blt_TreeIsLeaf(node)) {
+ Blt_TreeSortNode(tvPtr->tree, node, CompareNodes);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewSortFlatView --
+ *
+ * Sorts the flatten array of entries.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TreeViewSortFlatView(tvPtr)
+ TreeView *tvPtr;
+{
+ TreeViewEntry *entryPtr, **p;
+
+ if (((tvPtr->flags & TV_AUTO_SORT) == 0) ||
+ (tvPtr->sortType == SORT_TYPE_NONE) ||
+ (tvPtr->sortColumnPtr == NULL) ||
+ (tvPtr->nEntries == 1)) {
+ return;
+ }
+ /* Prefetch the data for sorting. */
+ if (tvPtr->sortColumnPtr == &tvPtr->treeColumn) {
+ for(p = tvPtr->flatArr; *p != NULL; p++) {
+ entryPtr = *p;
+ if (entryPtr->fullName == NULL) {
+ Tcl_DString dString;
+
+ Blt_TreeViewGetFullName(tvPtr, entryPtr, TRUE, &dString);
+ entryPtr->fullName = Blt_Strdup(Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ }
+ entryPtr->data = entryPtr->fullName;
+ }
+ } else {
+ Blt_TreeKey key;
+ Tcl_Obj *objPtr;
+
+ key = tvPtr->sortColumnPtr->key;
+ for(p = tvPtr->flatArr; *p != NULL; p++) {
+ entryPtr = *p;
+ objPtr = Blt_TreeViewGetData(entryPtr, key);
+ entryPtr->data = (objPtr == NULL) ? "" : Tcl_GetString(objPtr);
+ }
+ }
+ if (tvPtr->flags & TV_SORTED) {
+ int first, last;
+ TreeViewEntry *hold;
+
+ for (first = 0, last = tvPtr->nEntries - 1; last > first;
+ first++, last--) {
+ hold = tvPtr->flatArr[first];
+ tvPtr->flatArr[first] = tvPtr->flatArr[last];
+ tvPtr->flatArr[last] = hold;
+ }
+ } else {
+ qsort((char *)tvPtr->flatArr, tvPtr->nEntries, sizeof(TreeViewEntry *),
+ (QSortCompareProc *)CompareEntries);
+ tvPtr->flags |= TV_SORTED;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewSortTreeView --
+ *
+ * Sorts the tree array of entries.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TreeViewSortTreeView(tvPtr)
+ TreeView *tvPtr;
+{
+
+ if ((tvPtr->flags & TV_AUTO_SORT) &&
+ (tvPtr->sortType != SORT_TYPE_NONE) &&
+ (tvPtr->sortColumnPtr != NULL)) {
+ treeViewInstance = tvPtr;
+ Blt_TreeApply(tvPtr->rootPtr->node, SortApplyProc, tvPtr);
+ }
+}
+
+
+#endif /* NO_TREEVIEW */