diff options
Diffstat (limited to 'blt/src/bltTreeViewColumn.c')
-rw-r--r-- | blt/src/bltTreeViewColumn.c | 1881 |
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 */ |