From b760cda871c7bb705053178c72a681dbd70aadc6 Mon Sep 17 00:00:00 2001 From: treectrl Date: Wed, 4 Oct 2006 03:52:23 +0000 Subject: New file. --- generic/tkTreeRowLabel.c | 3107 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3107 insertions(+) create mode 100644 generic/tkTreeRowLabel.c diff --git a/generic/tkTreeRowLabel.c b/generic/tkTreeRowLabel.c new file mode 100644 index 0000000..74df736 --- /dev/null +++ b/generic/tkTreeRowLabel.c @@ -0,0 +1,3107 @@ +/* + * tkTreeRow.c -- + * + * This module implements a treectrl's row labels. + * + * Copyright (c) 2002-2006 Tim Baker + * + * RCS: @(#) $Id: tkTreeRowLabel.c,v 1.1 2006/10/04 03:52:23 treectrl Exp $ + */ + +#include "tkTreeCtrl.h" + +typedef struct RowLabel RowLabel; + +/* + * The following structure holds information about a single + * row label in a TreeCtrl. + */ +struct RowLabel +{ + int height; /* -height */ + Tcl_Obj *heightObj; /* -height */ + int minHeight; /* -minheight */ + Tcl_Obj *minHeightObj; /* -minheight */ + int maxHeight; /* -maxheight */ + Tcl_Obj *maxHeightObj; /* -maxheight */ + int visible; /* -visible */ + int resize; /* -resize */ + Tcl_Obj *styleObj; /* -style */ + int state; /* state flags. FIXME: item states have no meaning here */ + + TreeCtrl *tree; + Tk_OptionTable optionTable; + int id; /* unique identifier */ + int index; /* order in list of rows */ + TreeStyle style; /* style */ + int onScreen; /* TRUE if onscreen. */ + TagInfo *tagInfo; /* Tags. May be NULL. */ + RowLabel *prev; + RowLabel *next; +}; + +#define ROW_CONF_WIDTH 0x0002 +#define ROW_CONF_HEIGHT 0x0004 +#define ROW_CONF_DISPLAY 0x0040 +#define ROW_CONF_STYLE 0x0100 +#define ROW_CONF_TAGS 0x0200 +#define ROW_CONF_RANGES 0x0800 + +static Tk_OptionSpec rowSpecs[] = { + {TK_OPTION_PIXELS, "-height", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(RowLabel, heightObj), Tk_Offset(RowLabel, height), + TK_OPTION_NULL_OK, (ClientData) NULL, ROW_CONF_HEIGHT}, + {TK_OPTION_PIXELS, "-maxheight", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(RowLabel, maxHeightObj), + Tk_Offset(RowLabel, maxHeight), + TK_OPTION_NULL_OK, (ClientData) NULL, ROW_CONF_HEIGHT}, + {TK_OPTION_PIXELS, "-minheight", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(RowLabel, minHeightObj), + Tk_Offset(RowLabel, minHeight), + TK_OPTION_NULL_OK, (ClientData) NULL, ROW_CONF_HEIGHT}, + {TK_OPTION_BOOLEAN, "-resize", (char *) NULL, (char *) NULL, + "1", -1, Tk_Offset(RowLabel, resize), 0, (ClientData) NULL, 0}, + {TK_OPTION_STRING, "-style", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(RowLabel, styleObj), -1, + TK_OPTION_NULL_OK, (ClientData) NULL, ROW_CONF_STYLE}, + {TK_OPTION_CUSTOM, "-tags", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(RowLabel, tagInfo), + TK_OPTION_NULL_OK, (ClientData) &TagInfoCO, ROW_CONF_TAGS}, + {TK_OPTION_BOOLEAN, "-visible", (char *) NULL, (char *) NULL, + "1", -1, Tk_Offset(RowLabel, visible), + 0, (ClientData) NULL, 0}, + {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, + (char *) NULL, 0, -1, 0, 0, 0} +}; + +/* + *---------------------------------------------------------------------- + * + * RowOptionSet -- + * + * Tk_ObjCustomOption.setProc(). Converts a Tcl_Obj holding a + * row description into a pointer to a Row. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * May store a TreeRowLabel pointer into the internal representation + * pointer. May change the pointer to the Tcl_Obj to NULL to indicate + * that the specified string was empty and that is acceptable. + * + *---------------------------------------------------------------------- + */ + +static int +RowOptionSet( + ClientData clientData, /* RFO_xxx flags to control the conversion. */ + Tcl_Interp *interp, /* Current interpreter. */ + Tk_Window tkwin, /* Window for which option is being set. */ + Tcl_Obj **value, /* Pointer to the pointer to the value object. + * We use a pointer to the pointer because + * we may need to return a value (NULL). */ + char *recordPtr, /* Pointer to storage for the widget record. */ + int internalOffset, /* Offset within *recordPtr at which the + * internal value is to be stored. */ + char *saveInternalPtr, /* Pointer to storage for the old value. */ + int flags /* Flags for the option, set Tk_SetOptions. */ + ) +{ + int rfoFlags = (int) clientData; + TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData; + int objEmpty; + TreeRowLabel new, *internalPtr; + + if (internalOffset >= 0) + internalPtr = (TreeRowLabel *) (recordPtr + internalOffset); + else + internalPtr = NULL; + + objEmpty = ObjectIsEmpty((*value)); + + if ((flags & TK_OPTION_NULL_OK) && objEmpty) + (*value) = NULL; + else { + if (TreeRowLabel_FromObj(tree, (*value), &new, rfoFlags) != TCL_OK) + return TCL_ERROR; + } + if (internalPtr != NULL) { + if ((*value) == NULL) + new = NULL; + *((TreeRowLabel *) saveInternalPtr) = *internalPtr; + *internalPtr = new; + } + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * RowOptionGet -- + * + * Tk_ObjCustomOption.getProc(). Converts a TreeRowLabel into a + * Tcl_Obj string representation. + * + * Results: + * Tcl_Obj containing the string representation of the row. + * Returns NULL if the TreeRowLabel is NULL. + * + * Side effects: + * May create a new Tcl_Obj. + * + *---------------------------------------------------------------------- + */ + +static Tcl_Obj * +RowOptionGet( + ClientData clientData, /* Not used. */ + Tk_Window tkwin, /* Window for which option is being set. */ + char *recordPtr, /* Pointer to widget record. */ + int internalOffset /* Offset within *recordPtr containing the + * sticky value. */ + ) +{ + TreeRowLabel value = *(TreeRowLabel *) (recordPtr + internalOffset); + TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData; + if (value == NULL) + return NULL; + if (value == ROW_ALL) + return Tcl_NewStringObj("all", -1); + return TreeRowLabel_ToObj(tree, value); +} + +/* + *---------------------------------------------------------------------- + * + * RowOptionRestore -- + * + * Tk_ObjCustomOption.restoreProc(). Restores a TreeRowLabel value + * from a saved value. + * + * Results: + * None. + * + * Side effects: + * Restores the old value. + * + *---------------------------------------------------------------------- + */ + +static void +RowOptionRestore( + ClientData clientData, /* Not used. */ + Tk_Window tkwin, /* Not used. */ + char *internalPtr, /* Where to store old value. */ + char *saveInternalPtr) /* Pointer to old value. */ +{ + *(TreeRowLabel *) internalPtr = *(TreeRowLabel *) saveInternalPtr; +} + +/* + * The following structure contains pointers to functions used for processing + * a custom config option that handles Tcl_Obj<->TreeRowLabel conversion. + * A row description must refer to a valid row. "all" is not allowed. + */ +Tk_ObjCustomOption rowCustomOption = +{ + "row", + RowOptionSet, + RowOptionGet, + RowOptionRestore, + NULL, + (ClientData) (RFO_NOT_MANY | RFO_NOT_NULL) +}; + +static Tk_OptionSpec dragSpecs[] = { + {TK_OPTION_BOOLEAN, "-enable", (char *) NULL, (char *) NULL, + "0", -1, Tk_Offset(TreeCtrl, rowDrag.enable), + 0, (ClientData) NULL, 0}, + {TK_OPTION_INT, "-imagealpha", (char *) NULL, (char *) NULL, + "128", -1, Tk_Offset(TreeCtrl, rowDrag.alpha), + 0, (ClientData) NULL, 0}, + {TK_OPTION_COLOR, "-imagecolor", (char *) NULL, (char *) NULL, + "gray75", -1, Tk_Offset(TreeCtrl, rowDrag.color), + 0, (ClientData) NULL, 0}, + {TK_OPTION_CUSTOM, "-imagerow", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(TreeCtrl, rowDrag.row), + TK_OPTION_NULL_OK, (ClientData) &rowCustomOption, 0}, + {TK_OPTION_PIXELS, "-imageoffset", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(TreeCtrl, rowDrag.offsetObj), + Tk_Offset(TreeCtrl, rowDrag.offset), 0, (ClientData) NULL, 0}, + {TK_OPTION_COLOR, "-indicatorcolor", (char *) NULL, (char *) NULL, + "Black", -1, Tk_Offset(TreeCtrl, rowDrag.indColor), + 0, (ClientData) NULL, 0}, + {TK_OPTION_CUSTOM, "-indicatorrow", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(TreeCtrl, rowDrag.indRow), + TK_OPTION_NULL_OK, (ClientData) &rowCustomOption, 0}, + {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, + (char *) NULL, 0, -1, 0, 0, 0} +}; + +typedef struct Qualifiers { + TreeCtrl *tree; + int visible; /* 1 for -visible TRUE, + 0 for -visible FALSE, + -1 for unspecified. */ + int states[3]; /* States that must be on or off. */ + TagExpr expr; /* Tag expression. */ + int exprOK; /* TRUE if expr is valid. */ +} Qualifiers; + +/* + *---------------------------------------------------------------------- + * + * Qualifiers_Init -- + * + * Helper routine for TreeItem_FromObj. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +Qualifiers_Init( + TreeCtrl *tree, /* Widget info. */ + Qualifiers *q /* Out: Initialized qualifiers. */ + ) +{ + q->tree = tree; + q->visible = -1; + q->states[0] = q->states[1] = q->states[2] = 0; + q->exprOK = FALSE; +} + +/* + *---------------------------------------------------------------------- + * + * Qualifiers_Scan -- + * + * Helper routine for TreeItem_FromObj. + * + * Results: + * TCL_OK or TCL_ERROR. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +Qualifiers_Scan( + Qualifiers *q, /* Must call Qualifiers_Init first, + * and Qualifiers_Free if result is TCL_OK. */ + int objc, /* Number of arguments. */ + Tcl_Obj **objv, /* Argument values. */ + int startIndex, /* First objv[] index to look at. */ + int *argsUsed /* Out: number of objv[] used. */ + ) +{ + TreeCtrl *tree = q->tree; + Tcl_Interp *interp = tree->interp; + int qual, j = startIndex; + + static CONST char *qualifiers[] = { + "state", "tag", "visible", "!visible", NULL + }; + enum qualEnum { + QUAL_STATE, QUAL_TAG, QUAL_VISIBLE, QUAL_NOT_VISIBLE + }; + /* Number of arguments used by qualifiers[]. */ + static int qualArgs[] = { + 2, 2, 1, 1 + }; + + *argsUsed = 0; + + for (; j < objc; ) { + if (Tcl_GetIndexFromObj(NULL, objv[j], qualifiers, NULL, 0, + &qual) != TCL_OK) + break; + if (objc - j < qualArgs[qual]) { + Tcl_AppendResult(interp, "missing arguments to \"", + Tcl_GetString(objv[j]), "\" qualifier", NULL); + goto errorExit; + } + switch ((enum qualEnum) qual) { + case QUAL_STATE: + { + if (Tree_StateFromListObj(tree, objv[j + 1], q->states, + SFO_NOT_TOGGLE) != TCL_OK) + goto errorExit; + break; + } + case QUAL_TAG: + { + if (q->exprOK) + TagExpr_Free(&q->expr); + if (TagExpr_Init(tree, objv[j + 1], &q->expr) != TCL_OK) + return TCL_ERROR; + q->exprOK = TRUE; + break; + } + case QUAL_VISIBLE: + { + q->visible = 1; + break; + } + case QUAL_NOT_VISIBLE: + { + q->visible = 0; + break; + } + } + *argsUsed += qualArgs[qual]; + j += qualArgs[qual]; + } + return TCL_OK; +errorExit: + if (q->exprOK) + TagExpr_Free(&q->expr); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * Qualifies -- + * + * Helper routine for TreeItem_FromObj. + * + * Results: + * Returns TRUE if the item meets the given criteria. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +Qualifies( + Qualifiers *q, /* Qualifiers to check. */ + RowLabel *row /* The row to test. May be NULL. */ + ) +{ + /* Note: if the row is NULL it is a "match" because we have run + * out of row to check. */ + if (row == NULL) + return 1; + if ((q->visible == 1) && !row->visible) + return 0; + else if ((q->visible == 0) && row->visible) + return 0; + if (q->states[STATE_OP_OFF] & row->state) + return 0; + if ((q->states[STATE_OP_ON] & row->state) != q->states[STATE_OP_ON]) + return 0; + if (q->exprOK && !TagExpr_Eval(&q->expr, row->tagInfo)) + return 0; + return 1; +} + +/* + *---------------------------------------------------------------------- + * + * Qualifiers_Free -- + * + * Helper routine for TreeItem_FromObj. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +Qualifiers_Free( + Qualifiers *q /* Out: Initialized qualifiers. */ + ) +{ + if (q->exprOK) + TagExpr_Free(&q->expr); +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowLabelList_FromObj -- + * + * Parse a Tcl_Obj rowlabel description to get a list of rowlabels. + * + * ID MODIFIERS + * TAG QUALIFIERS + * all QUALIFIERS + * first QUALIFIERS MODIFIERS + * end|last QUALIFIERS MODIFIERS + * list listOfDescs + * order N QUALIFIERS MODIFIERS + * range first last QUALIFIERS + * tag tagExpr QUALFIERS + * + * MODIFIERS: + * next QUALIFIERS + * prev QUALIFIERS + * + * QUALIFIERS: + * state stateList + * tag tagExpr + * visible + * !visible + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TreeRowLabelList_FromObj( + TreeCtrl *tree, /* Widget info. */ + Tcl_Obj *objPtr, /* RowLabel description. */ + TreeRowLabelList *rows, /* Uninitialized list. Caller must free + * it with TreeRowLabelList_Free unless the + * result of this function is TCL_ERROR. */ + int flags /* RFO_xxx flags. */ + ) +{ + Tcl_Interp *interp = tree->interp; + int objc; + int index, listIndex; + Tcl_Obj **objv, *elemPtr; + RowLabel *row = NULL; + Qualifiers q; + Tcl_HashEntry *hPtr; + + static CONST char *indexName[] = { + "all", "end", "first", "last", "list", "order", "range", "tag", + (char *) NULL + }; + enum indexEnum { + INDEX_ALL, INDEX_END, INDEX_FIRST, INDEX_LAST, INDEX_LIST, INDEX_ORDER, + INDEX_RANGE, INDEX_TAG + } ; + /* Number of arguments used by indexName[]. */ + static int indexArgs[] = { + 1, 1, 1, 1, 2, 2, 3, 2 + }; + /* Boolean: can indexName[] be followed by 1 or more qualifiers. */ + static int indexQual[] = { + 1, 1, 1, 1, 1, 1, 1, 1 + }; + + static CONST char *modifiers[] = { + "next", "prev", (char *) NULL + }; + enum modEnum { + TMOD_NEXT, TMOD_PREV + }; + /* Number of arguments used by modifiers[]. */ + static int modArgs[] = { + 1, 1 + }; + /* Boolean: can modifiers[] be followed by 1 or more qualifiers. */ + static int modQual[] = { + 1, 1 + }; + + TreeRowLabelList_Init(tree, rows, 0); + Qualifiers_Init(tree, &q); + + if (Tcl_ListObjGetElements(NULL, objPtr, &objc, &objv) != TCL_OK) + goto badDesc; + if (objc == 0) + goto badDesc; + + listIndex = 0; + elemPtr = objv[listIndex]; + if (Tcl_GetIndexFromObj(NULL, elemPtr, indexName, NULL, 0, &index) + == TCL_OK) { + int qualArgsTotal = 0; + + if (objc - listIndex < indexArgs[index]) { + Tcl_AppendResult(interp, "missing arguments to \"", + Tcl_GetString(elemPtr), "\" keyword", NULL); + goto errorExit; + } + if (indexQual[index]) { + if (Qualifiers_Scan(&q, objc, objv, listIndex + indexArgs[index], + &qualArgsTotal) != TCL_OK) { + goto errorExit; + } + } + + switch ((enum indexEnum) index) { + case INDEX_ALL: + { + if (qualArgsTotal) { + row = (RowLabel *) tree->rows; + while (row != NULL) { + if (Qualifies(&q, row)) { + TreeRowLabelList_Append(rows, (TreeRowLabel) row); + } + row = row->next; + } + row = NULL; + } else { + row = (RowLabel *) ROW_ALL; + } + break; + } + case INDEX_FIRST: + { + row = (RowLabel *) tree->rows; + while (!Qualifies(&q, row)) + row = row->next; + break; + } + case INDEX_END: + case INDEX_LAST: + { + row = (RowLabel *) tree->rowLabelLast; + while (!Qualifies(&q, row)) + row = row->prev; + break; + } + case INDEX_LIST: + { + int listObjc; + Tcl_Obj **listObjv; + int i, count; + + if (Tcl_ListObjGetElements(interp, objv[listIndex + 1], + &listObjc, &listObjv) != TCL_OK) + goto errorExit; + for (i = 0; i < listObjc; i++) { + TreeRowLabelList row2s; + if (TreeRowLabelList_FromObj(tree, listObjv[i], &row2s, flags) + != TCL_OK) + goto errorExit; + TreeRowLabelList_Concat(rows, &row2s); + TreeRowLabelList_Free(&row2s); + } + /* If any of the rowlabel descriptions in the list is "all", then + * clear the list of rowlabels and use "all". */ + count = TreeRowLabelList_Count(rows); + for (i = 0; i < count; i++) { + TreeRowLabel row = TreeRowLabelList_Nth(rows, i); + if (row == ROW_ALL) + break; + } + if (i < count) { + TreeRowLabelList_Free(rows); + row = (RowLabel *) ROW_ALL; + } else + row = NULL; + break; + } + case INDEX_ORDER: + { + int order; + + if (Tcl_GetIntFromObj(NULL, objv[listIndex + 1], &order) != TCL_OK) + goto errorExit; + row = (RowLabel *) tree->rows; + while (row != NULL) { + if (Qualifies(&q, row)) + if (order-- <= 0) + break; + row = row->next; + } + break; + } + case INDEX_RANGE: + { + TreeRowLabel _first, _last; + RowLabel *first, *last; + + if (TreeRowLabel_FromObj(tree, objv[listIndex + 1], + &_first, RFO_NOT_MANY | RFO_NOT_NULL) != TCL_OK) + goto errorExit; + first = (RowLabel *) _first; + if (TreeRowLabel_FromObj(tree, objv[listIndex + 2], + &_last, RFO_NOT_MANY | RFO_NOT_NULL) != TCL_OK) + goto errorExit; + last = (RowLabel *) _last; + if (first->index > last->index) { + row = first; + first = last; + last = row; + } + row = first; + while (1) { + if (Qualifies(&q, row)) { + TreeRowLabelList_Append(rows, row); + } + if (row == last) + break; + row = row->next; + } + row = NULL; + break; + } + case INDEX_TAG: + { + TagExpr expr; + + if (TagExpr_Init(tree, objv[listIndex + 1], &expr) != TCL_OK) + goto errorExit; + row = (RowLabel *) tree->rows; + while (row != NULL) { + if (TagExpr_Eval(&expr, row->tagInfo) && + Qualifies(&q, row)) { + TreeRowLabelList_Append(rows, (TreeRowLabel) row); + } + row = row->next; + } + TagExpr_Free(&expr); + row = NULL; + break; + } + } + /* If 1 rowlabel, use it and clear the list. */ + if (TreeRowLabelList_Count(rows) == 1) { + row = (RowLabel *) TreeRowLabelList_Nth(rows, 0); + rows->count = 0; + + } + + /* If > 1 rowlabel, no modifiers may follow. */ + if ((TreeRowLabelList_Count(rows) > 1) || (row == (RowLabel *) ROW_ALL)) { + if (listIndex + indexArgs[index] + qualArgsTotal < objc) { + FormatResult(interp, + "unexpected arguments after \"%s\" keyword", + indexName[index]); + goto errorExit; + } + } + listIndex += indexArgs[index] + qualArgsTotal; + } else { + int gotId = FALSE, id = -1; + + if (tree->rowPrefixLen) { + char *end, *t = Tcl_GetString(elemPtr); + if (strncmp(t, tree->rowPrefix, tree->rowPrefixLen) == 0) + { + t += tree->rowPrefixLen; + id = strtoul(t, &end, 10); + if ((end != t) && (*end == '\0')) + gotId = TRUE; + } + + } else if (Tcl_GetIntFromObj(NULL, elemPtr, &id) == TCL_OK) { + gotId = TRUE; + } + if (gotId) { + hPtr = Tcl_FindHashEntry(&tree->rowIDHash, (char *) id); + if (hPtr != NULL) { + row = (RowLabel *) Tcl_GetHashValue(hPtr); + } + listIndex++; + } else { + TagExpr expr; + int qualArgsTotal = 0; + + if (objc > 1) { + if (Qualifiers_Scan(&q, objc, objv, listIndex + 1, + &qualArgsTotal) != TCL_OK) { + goto errorExit; + } + } + if (TagExpr_Init(tree, elemPtr, &expr) != TCL_OK) + goto errorExit; + row = (RowLabel *) tree->rows; + while (row != NULL) { + if (TagExpr_Eval(&expr, row->tagInfo) && Qualifies(&q, row)) { + TreeRowLabelList_Append(rows, (TreeRowLabel) row); + } + row = row->next; + } + TagExpr_Free(&expr); + row = NULL; + + /* If 1 rowlabel, use it and clear the list. */ + if (TreeRowLabelList_Count(rows) == 1) { + row = (RowLabel *) TreeRowLabelList_Nth(rows, 0); + rows->count = 0; + + } + + /* If > 1 rowlabel, no modifiers may follow. */ + if (TreeRowLabelList_Count(rows) > 1) { + if (listIndex + 1 + qualArgsTotal < objc) { + FormatResult(interp, + "unexpected arguments after \"%s\"", + Tcl_GetString(elemPtr)); + goto errorExit; + } + } + + listIndex += 1 + qualArgsTotal; + } + } + + /* This means a valid specification was given, but there is no such row */ + if ((TreeRowLabelList_Count(rows) == 0) && (row == NULL)) { + if (flags & RFO_NOT_NULL) + goto notNull; + /* Empty list returned */ + goto goodExit; + } + + for (; listIndex < objc; /* nothing */) { + int qualArgsTotal = 0; + + elemPtr = objv[listIndex]; + if (Tcl_GetIndexFromObj(interp, elemPtr, modifiers, "modifier", 0, + &index) != TCL_OK) + return TCL_ERROR; + if (objc - listIndex < modArgs[index]) { + Tcl_AppendResult(interp, "missing arguments to \"", + Tcl_GetString(elemPtr), "\" modifier", NULL); + goto errorExit; + } + if (modQual[index]) { + Qualifiers_Free(&q); + Qualifiers_Init(tree, &q); + if (Qualifiers_Scan(&q, objc, objv, listIndex + modArgs[index], + &qualArgsTotal) != TCL_OK) { + goto errorExit; + } + } + switch ((enum modEnum) index) { + case TMOD_NEXT: + { + row = row->next; + while (!Qualifies(&q, row)) { + row = row->next; + } + break; + } + case TMOD_PREV: + { + row = row->prev; + while (!Qualifies(&q, row)) + row = row->prev; + break; + } + } + if ((TreeRowLabelList_Count(rows) == 0) && (row == NULL)) { + if (flags & RFO_NOT_NULL) + goto notNull; + /* Empty list returned. */ + goto goodExit; + } + listIndex += modArgs[index] + qualArgsTotal; + } + if ((flags & RFO_NOT_MANY) && ((row == (RowLabel *) ROW_ALL) || + (TreeRowLabelList_Count(rows) > 1))) { + FormatResult(interp, "can't specify > 1 rowlabel for this command"); + goto errorExit; + } + if ((flags & RFO_NOT_NULL) && (TreeRowLabelList_Count(rows) == 0) && + (row == NULL)) { +notNull: + FormatResult(interp, "rowlabel \"%s\" doesn't exist", Tcl_GetString(objPtr)); + goto errorExit; + } + if (TreeRowLabelList_Count(rows)) { + } else if (row == (RowLabel *) ROW_ALL) { + if ((flags & RFO_NOT_NULL) && (tree->rowCount == 0)) + goto notNull; + TreeRowLabelList_Append(rows, ROW_ALL); + } else { + TreeRowLabelList_Append(rows, (TreeRowLabel) row); + } +goodExit: + Qualifiers_Free(&q); + return TCL_OK; + +badDesc: + FormatResult(interp, "bad rowlabel description \"%s\"", Tcl_GetString(objPtr)); + goto errorExit; + +errorExit: + Qualifiers_Free(&q); + TreeRowLabelList_Free(rows); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowLabel_FromObj -- + * + * Parse a Tcl_Obj column description to get a single column. + * + * Results: + * TCL_OK or TCL_ERROR. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TreeRowLabel_FromObj( + TreeCtrl *tree, /* Widget info. */ + Tcl_Obj *objPtr, /* Object to parse to an item. */ + TreeRowLabel *rowPtr, /* Returned item. */ + int flags /* RFO_xxx flags */ + ) +{ + TreeRowLabelList rows; + + if (TreeRowLabelList_FromObj(tree, objPtr, &rows, flags) != TCL_OK) + return TCL_ERROR; + /* May be NULL. */ + (*rowPtr) = TreeRowLabelList_Nth(&rows, 0); + TreeRowLabelList_Free(&rows); + return TCL_OK; +} + +typedef struct RowForEach RowForEach; +struct RowForEach { + TreeCtrl *tree; + int error; + int all; + TreeRowLabel current; + TreeRowLabelList *list; + int index; +}; + +#define ROW_FOR_EACH(row, rows, iter) \ + for (row = RowForEach_Start(rows, iter); \ + row != NULL; \ + row = RowForEach_Next(iter)) + +/* + *---------------------------------------------------------------------- + * + * RowForEach_Start -- + * + * Begin iterating over rowlabels. + * + * Results: + * Returns the first item to iterate over. If an error occurs + * then RowForEach.error is set to 1. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +TreeRowLabel +RowForEach_Start( + TreeRowLabelList *rows, /* List of rowlabels. */ + RowForEach *iter /* Returned info, pass to + RowForEach_Next. */ + ) +{ + TreeCtrl *tree = rows->tree; + TreeRowLabel row; + + row = TreeRowLabelList_Nth(rows, 0); + + iter->tree = tree; + iter->all = FALSE; + iter->error = 0; + iter->list = NULL; + + if (row == ROW_ALL) { + iter->all = TRUE; + return iter->current = tree->rows; + } + + iter->list = rows; + iter->index = 0; + return iter->current = row; +} + +/* + *---------------------------------------------------------------------- + * + * RowForEach_Next -- + * + * Returns the next column to iterate over. Keep calling this until + * the result is NULL. + * + * Results: + * Returns the next column to iterate over or NULL. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +TreeRowLabel +RowForEach_Next( + RowForEach *iter /* Initialized by RowForEach_Start. */ + ) +{ + if (iter->all) { + return iter->current = TreeRowLabel_Next(iter->current); + } + + if (iter->index >= TreeRowLabelList_Count(iter->list)) + return iter->current = NULL; + return iter->current = TreeRowLabelList_Nth(iter->list, ++iter->index); +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowLabel_ToObj -- + * + * Return a Tcl_Obj representing a column. + * + * Results: + * A Tcl_Obj. + * + * Side effects: + * Allocates a Tcl_Obj. + * + *---------------------------------------------------------------------- + */ + +Tcl_Obj * +TreeRowLabel_ToObj( + TreeCtrl *tree, /* Widget info. */ + TreeRowLabel row_ /* RowLabel token to get Tcl_Obj for. */ + ) +{ + RowLabel *row = (RowLabel *) row_; + + if (tree->rowPrefixLen) { + char buf[100 + TCL_INTEGER_SPACE]; + (void) sprintf(buf, "%s%d", tree->rowPrefix, row->id); + return Tcl_NewStringObj(buf, -1); + } + return Tcl_NewIntObj(row->id); +} + +/* + *---------------------------------------------------------------------- + * + * Tree_FindRow -- + * + * Get the N'th row in a TreeCtrl. + * + * Results: + * Token for the N'th row. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +TreeRowLabel +Tree_FindRow( + TreeCtrl *tree, /* Widget info. */ + int rowIndex /* 0-based index of the row to return. */ + ) +{ + RowLabel *row = (RowLabel *) tree->rows; + + while (row != NULL) { + if (row->index == rowIndex) + break; + row = row->next; + } + return (TreeRowLabel) row; +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowLabel_Next -- + * + * Return the row to the right of the given one. + * + * Results: + * Token for the next row. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +TreeRowLabel +TreeRowLabel_Next( + TreeRowLabel row_ /* RowLabel token. */ + ) +{ + return (TreeRowLabel) ((RowLabel *) row_)->next; +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowLabel_OnScreen -- + * + * Called by display code. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TreeRowLabel_OnScreen( + TreeRowLabel row_, /* RowLabel token. */ + int onScreen + ) +{ + RowLabel *row = (RowLabel *) row_; + TreeCtrl *tree = row->tree; + + if (onScreen == row->onScreen) + return; + row->onScreen = onScreen; + if (row->style != NULL) + TreeStyle_OnScreen(tree, row->style, onScreen); +} + +/* + *---------------------------------------------------------------------- + * + * Row_Config -- + * + * This procedure is called to process an objc/objv list to set + * configuration options for a Row. + * + * Results: + * The return value is a standard Tcl result. If TCL_ERROR is + * returned, then an error message is left in interp's result. + * + * Side effects: + * Configuration information, such as height, span, etc. get set + * for row; old resources get freed, if there were any. Display + * changes may occur. + * + *---------------------------------------------------------------------- + */ + +static int +Row_Config( + RowLabel *row, /* RowLabel record. */ + int objc, /* Number of arguments. */ + Tcl_Obj *CONST objv[], /* Argument values. */ + int createFlag /* TRUE if the RowLabel is being created. */ + ) +{ + TreeCtrl *tree = row->tree; + Tk_SavedOptions savedOptions; + RowLabel saved; + int error; + Tcl_Obj *errorResult = NULL; + int mask; + TreeStyle style = NULL; /* master style */ + + saved.visible = row->visible; + + for (error = 0; error <= 1; error++) { + if (error == 0) { + if (Tk_SetOptions(tree->interp, (char *) row, + row->optionTable, objc, objv, tree->tkwin, + &savedOptions, &mask) != TCL_OK) { + mask = 0; + continue; + } + + /* Wouldn't have to do this if Tk_InitOptions() would return + * a mask of configured options like Tk_SetOptions() does. */ + if (createFlag) { + if (row->styleObj != NULL) + mask |= ROW_CONF_STYLE; + } + + /* + * Step 1: Save old values + */ + + /* + * Step 2: Process new values + */ + + if (mask & ROW_CONF_STYLE) { + if (row->styleObj != NULL) { + if (TreeStyle_FromObj(tree, row->styleObj, &style) != TCL_OK) + continue; + } + } + + /* + * Step 3: Free saved values + */ + + Tk_FreeSavedOptions(&savedOptions); + break; + } else { + errorResult = Tcl_GetObjResult(tree->interp); + Tcl_IncrRefCount(errorResult); + Tk_RestoreSavedOptions(&savedOptions); + + /* *** */ + + Tcl_SetObjResult(tree->interp, errorResult); + Tcl_DecrRefCount(errorResult); + return TCL_ERROR; + } + } + + /* Wouldn't have to do this if Tk_InitOptions() would return + * a mask of configured options like Tk_SetOptions() does. */ + if (createFlag) { + } + + if (mask & ROW_CONF_STYLE) { + if (style == NULL) { + if (row->style != NULL) { + TreeStyle_FreeResources(tree, row->style); + row->style = NULL; + mask |= ROW_CONF_WIDTH | ROW_CONF_HEIGHT; + } + } else { + if ((row->style == NULL) || (TreeStyle_GetMaster(tree, row->style) != style)) { + if (row->style != NULL) { + TreeStyle_FreeResources(tree, row->style); + } + row->style = TreeStyle_NewInstance(tree, style); + mask |= ROW_CONF_WIDTH | ROW_CONF_HEIGHT; + } + } + } + + if (mask & ROW_CONF_HEIGHT) { + Tree_DInfoChanged(tree, DINFO_REDO_RANGES); + } + if (mask & ROW_CONF_WIDTH) { + tree->neededWidthOfRows = -1; /* requested width of row labels */ + } + if (mask & (ROW_CONF_WIDTH | ROW_CONF_HEIGHT)) { + Tree_DInfoChanged(tree, DINFO_DRAW_ROWLABELS); + } + if (saved.visible != row->visible) + Tree_DInfoChanged(tree, DINFO_REDO_RANGES); + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * Row_Alloc -- + * + * Allocate and initialize a new RowLabel record. + * + * Results: + * Pointer to the new RowLabel, or NULL if errors occurred. + * + * Side effects: + * Memory is allocated. + * + *---------------------------------------------------------------------- + */ + +static RowLabel * +Row_Alloc( + TreeCtrl *tree /* Widget info. */ + ) +{ + RowLabel *row; + Tcl_HashEntry *hPtr; + int isNew; + + row = (RowLabel *) ckalloc(sizeof(RowLabel)); + memset(row, '\0', sizeof(RowLabel)); + row->tree = tree; + row->optionTable = Tk_CreateOptionTable(tree->interp, rowSpecs); + if (Tk_InitOptions(tree->interp, (char *) row, row->optionTable, + tree->tkwin) != TCL_OK) { + WFREE(row, RowLabel); + return NULL; + } + row->id = tree->nextRowId++; + tree->rowCount++; + hPtr = Tcl_CreateHashEntry(&tree->rowIDHash, (char *) row->id, &isNew); + Tcl_SetHashValue(hPtr, (char *) row); + + return row; +} + +/* + *---------------------------------------------------------------------- + * + * Row_Free -- + * + * Free a Row. + * + * Results: + * Pointer to the next row. + * + * Side effects: + * Memory is deallocated. If this is the last row being + * deleted, the TreeCtrl.nextRowId field is reset to zero. + * + *---------------------------------------------------------------------- + */ + +static RowLabel * +Row_Free( + RowLabel *row /* RowLabel record. */ + ) +{ + TreeCtrl *tree = row->tree; + RowLabel *next = row->next; + Tcl_HashEntry *hPtr; + + hPtr = Tcl_FindHashEntry(&tree->rowIDHash, (char *) row->id); + Tcl_DeleteHashEntry(hPtr); + if (row->style != NULL) + TreeStyle_FreeResources(tree, row->style); + Tk_FreeConfigOptions((char *) row, row->optionTable, tree->tkwin); + WFREE(row, RowLabel); + tree->rowCount--; + if (tree->rowCount == 0) + tree->nextRowId = 0; + return next; +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowLabel_FixedHeight -- + * + * Return the value of the -height option. + * + * Results: + * The pixel height or -1 if the -height option is unspecified. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TreeRowLabel_FixedHeight( + TreeRowLabel row_ /* RowLabel token. */ + ) +{ + RowLabel *row = (RowLabel *) row_; + return row->heightObj ? row->height : -1; +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowLabel_MinHeight -- + * + * Return the value of the -minheight option. + * + * Results: + * The pixel height or -1 if the -minheight option is unspecified. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TreeRowLabel_MinHeight( + TreeRowLabel row_ /* RowLabel token. */ + ) +{ + RowLabel *row = (RowLabel *) row_; + return row->minHeightObj ? row->minHeight : -1; +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowLabel_MaxHeight -- + * + * Return the value of the -maxheight option. + * + * Results: + * The pixel height or -1 if the -maxheight option is unspecified. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TreeRowLabel_MaxHeight( + TreeRowLabel row_ /* RowLabel token. */ + ) +{ + RowLabel *row = (RowLabel *) row_; + return row->maxHeightObj ? row->maxHeight : -1; +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowLabel_NeededWidth -- + * + * Returns the requested width of a Row. + * + * Results: + * If the RowLabel has a style, the requested width of the style + * is returned (a positive pixel value). Otherwise 0 is returned. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TreeRowLabel_NeededWidth( + TreeRowLabel row_ /* RowLabel token. */ + ) +{ + RowLabel *row = (RowLabel *) row_; + TreeCtrl *tree = row->tree; + + if (row->style != NULL) + return TreeStyle_NeededWidth(tree, row->style, row->state); + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowLabel_NeededHeight -- + * + * Returns the requested height of a Row. + * + * Results: + * If the RowLabel has a style, the requested height of the style + * is returned (a positive pixel value). Otherwise 0 is returned. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TreeRowLabel_NeededHeight( + TreeRowLabel row_ /* RowLabel token. */ + ) +{ + RowLabel *row = (RowLabel *) row_; + TreeCtrl *tree = row->tree; + + if (row->style != NULL) + return TreeStyle_NeededHeight(tree, row->style, row->state); + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowLabel_Visible -- + * + * Return the value of the -visible config option for a row. + * + * Results: + * Boolean value. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TreeRowLabel_Visible( + TreeRowLabel row_ /* RowLabel token. */ + ) +{ + return ((RowLabel *) row_)->visible; +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowLabel_GetID -- + * + * Return the unique identifier for a row. + * + * Results: + * Unique integer id. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int TreeRowLabel_GetID( + TreeRowLabel row_ /* RowLabel token. */ + ) +{ + return ((RowLabel *) row_)->id; +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowLabel_GetStyle -- + * + * Returns the style assigned to a Row. + * + * Results: + * Returns the style, or NULL. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +TreeStyle TreeRowLabel_GetStyle( + TreeRowLabel row_ /* RowLabel token. */ + ) +{ + return ((RowLabel *) row_)->style; +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowLabel_ForgetStyle -- + * + * Free the style assigned to a Row. + * + * Results: + * RowLabel has no style assigned anymore. + * + * Side effects: + * Memory is freed. + * + *---------------------------------------------------------------------- + */ + +void +TreeRowLabel_ForgetStyle( + TreeRowLabel row_ /* RowLabel token. */ + ) +{ + RowLabel *row = (RowLabel *) row_; + TreeCtrl *tree = row->tree; + + if (row->style != NULL) { + TreeStyle_FreeResources(tree, row->style); + Tcl_DecrRefCount(row->styleObj); + row->styleObj = NULL; + row->style = NULL; + } +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowLabel_Index -- + * + * Return the 0-based index for a row. + * + * Results: + * Position of the row in the list of rows. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TreeRowLabel_Index( + TreeRowLabel row_ /* RowLabel token. */ + ) +{ + return ((RowLabel *) row_)->index; +} + +/* + *---------------------------------------------------------------------- + * + * NoStyleMsg -- + * + * Utility to set the interpreter result with a message indicating + * a RowLabel has no assigned style. + * + * Results: + * None. + * + * Side effects: + * Interpreter result is changed. + * + *---------------------------------------------------------------------- + */ + +static void +NoStyleMsg( + RowLabel *row /* RowLabel record. */ + ) +{ + TreeCtrl *tree = row->tree; + + FormatResult(tree->interp, "rowlabel %s%d has no style", + tree->rowPrefix, row->id); +} + +/* + *---------------------------------------------------------------------- + * + * RowElementCmd -- + * + * This procedure is invoked to process the [rowlabel element] widget + * command. See the user documentation for details on what + * it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +RowElementCmd( + ClientData clientData, /* Widget info. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *CONST objv[] /* Argument values. */ + ) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + static CONST char *commandNames[] = { "cget", "configure", "perstate", + (char *) NULL }; + enum { COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_PERSTATE }; + int index; + TreeRowLabelList rows; + TreeRowLabel row_; + RowLabel *row; + RowForEach iter; + + if (objc < 6) { + Tcl_WrongNumArgs(interp, 3, objv, "command row element ?arg ...?"); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(interp, objv[3], commandNames, "command", 0, + &index) != TCL_OK) + return TCL_ERROR; + + switch (index) { + /* T rowlabel element perstate R E option ?stateList? */ + case COMMAND_PERSTATE: + { + int state; + + if (objc < 7 || objc > 8) { + Tcl_WrongNumArgs(tree->interp, 4, objv, + "row element option ?stateList?"); + return TCL_ERROR; + } + if (TreeRowLabel_FromObj(tree, objv[4], &row_, + RFO_NOT_MANY | RFO_NOT_NULL) != TCL_OK) + return TCL_ERROR; + row = (RowLabel *) row_; + if (row->style == NULL) { + NoStyleMsg(row); + return TCL_ERROR; + } + state = row->state; + if (objc == 8) { + int states[3]; + + if (Tree_StateFromListObj(tree, objv[7], states, + SFO_NOT_OFF | SFO_NOT_TOGGLE) != TCL_OK) { + return TCL_ERROR; + } + state = states[STATE_OP_ON]; + } + return TreeStyle_ElementActual(tree, row->style, + state, objv[6], objv[7]); + } + + /* T rowlabel element cget R E option */ + case COMMAND_CGET: + { + if (objc != 7) { + Tcl_WrongNumArgs(tree->interp, 4, objv, + "row element option"); + return TCL_ERROR; + } + if (TreeRowLabel_FromObj(tree, objv[4], &row_, + RFO_NOT_MANY | RFO_NOT_NULL) != TCL_OK) + return TCL_ERROR; + row = (RowLabel *) row_; + if (row->style == NULL) { + NoStyleMsg(row); + return TCL_ERROR; + } + return TreeStyle_ElementCget(tree, (TreeItem) NULL, + (TreeItemColumn) NULL, row_, row->style, objv[5], objv[6]); + } + + /* T rowlabel element configure R E ... */ + case COMMAND_CONFIGURE: + { + int result = TCL_OK; + + if (TreeRowLabelList_FromObj(tree, objv[4], &rows, + RFO_NOT_NULL) != TCL_OK) + return TCL_ERROR; + + ROW_FOR_EACH(row_, &rows, &iter) { + row = (RowLabel *) row_; + + /* T row element configure R E option value \ + * + E option value + ... */ + int eMask, rMask = 0; + int indexElem = 5; + + while (1) { + int numArgs = 0; + char breakChar = '\0'; + + /* Look for a + */ + for (index = indexElem + 1; index < objc; index++) { + if (numArgs % 2 == 0) { + int length; + char *s = Tcl_GetStringFromObj(objv[index], &length); + + if ((length == 1) && (s[0] == '+')) { + breakChar = s[0]; + break; + } + } + numArgs++; + } + + /* Require at least one option-value pair if more than one + * element is specified. */ + if ((breakChar || indexElem != 5) && (numArgs < 2)) { + FormatResult(interp, + "missing option-value pair after element \"%s\"", + Tcl_GetString(objv[indexElem])); + result = TCL_ERROR; + break; + } + + result = TreeStyle_ElementConfigure(tree, (TreeItem) NULL, + (TreeItemColumn) NULL, row_, row->style, objv[indexElem], + numArgs, (Tcl_Obj **) objv + indexElem + 1, &eMask); + if (result != TCL_OK) + break; + + rMask |= eMask; + + if (breakChar) { + + if (index == objc - 1) { + FormatResult(interp, "missing %s after \"%c\"", + (breakChar == '+') ? "element name" : "column", + breakChar); + result = TCL_ERROR; + break; + } + + /* + indicates start of another element */ + if (breakChar == '+') { + indexElem = index + 1; + } + + } else if (index == objc) + break; + } + if (rMask & CS_LAYOUT) { + tree->neededWidthOfRows = -1; + } + if (rMask & CS_DISPLAY) + Tree_DInfoChanged(tree, DINFO_DRAW_ROWLABELS); + if (result != TCL_OK) + break; + } + TreeRowLabelList_Free(&rows); + return result; + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * Row_ChangeState -- + * + * Toggles zero or more STATE_xxx flags for a Row. + * + * Results: + * Bit mask of CS_LAYOUT and CS_DISPLAY flags, or zero if no + * changes occurred. + * + * Side effects: + * Display changes. + * + *---------------------------------------------------------------------- + */ + +static int +Row_ChangeState( + RowLabel *row, /* RowLabel info. */ + int stateOff, /* STATE_xxx flags to turn off. */ + int stateOn /* STATE_xxx flags to turn on. */ + ) +{ + TreeCtrl *tree = row->tree; + int state; + int sMask = 0; + + state = row->state; + state &= ~stateOff; + state |= stateOn; + + if (state == row->state) + return 0; + + if (row->style != NULL) { + sMask = TreeStyle_ChangeState(tree, row->style, row->state, state); + if (sMask & CS_LAYOUT) { + tree->neededWidthOfRows = -1; + } + if (sMask & CS_DISPLAY) + Tree_DInfoChanged(tree, DINFO_DRAW_ROWLABELS); + } + + row->state = state; + return sMask; +} + +/* + *---------------------------------------------------------------------- + * + * RowStateCmd -- + * + * This procedure is invoked to process the [row state] widget + * command. See the user documentation for details on what + * it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +RowStateCmd( + ClientData clientData, /* Widget info. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *CONST objv[] /* Argument values. */ + ) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + static CONST char *commandNames[] = { + "get", "set", (char *) NULL + }; + enum { + COMMAND_GET, COMMAND_SET + }; + int index; + TreeRowLabelList rows; + TreeRowLabel row_; + RowLabel *row; + RowForEach iter; + + if (objc < 5) { + Tcl_WrongNumArgs(interp, 3, objv, "command rowlabel ?arg ...?"); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(interp, objv[3], commandNames, "command", 0, + &index) != TCL_OK) + return TCL_ERROR; + + switch (index) { + + /* T row state get R ?state? */ + case COMMAND_GET: + { + Tcl_Obj *listObj; + int i, states[3]; + + if (objc > 6) { + Tcl_WrongNumArgs(interp, 5, objv, "?state?"); + return TCL_ERROR; + } + if (TreeRowLabel_FromObj(tree, objv[4], &row_, + RFO_NOT_MANY | RFO_NOT_NULL) != TCL_OK) + return TCL_ERROR; + row = (RowLabel *) row_; + if (objc == 6) { + states[STATE_OP_ON] = 0; + if (Tree_StateFromObj(tree, objv[5], states, NULL, + SFO_NOT_OFF | SFO_NOT_TOGGLE) != TCL_OK) + return TCL_ERROR; + Tcl_SetObjResult(interp, + Tcl_NewBooleanObj((row->state & states[STATE_OP_ON]) != 0)); + break; + } + listObj = Tcl_NewListObj(0, NULL); + for (i = 0; i < 32; i++) { + if (tree->stateNames[i] == NULL) + continue; + if (row->state & (1L << i)) { + Tcl_ListObjAppendElement(interp, listObj, + Tcl_NewStringObj(tree->stateNames[i], -1)); + } + } + Tcl_SetObjResult(interp, listObj); + break; + } + + /* T row state set R {state ...} */ + case COMMAND_SET: + { + int states[3], stateOn, stateOff; + + if (objc != 6) { + Tcl_WrongNumArgs(interp, 5, objv, "stateList"); + return TCL_ERROR; + } + if (TreeRowLabelList_FromObj(tree, objv[4], &rows, + RFO_NOT_NULL) != TCL_OK) + return TCL_ERROR; + if (Tree_StateFromListObj(tree, objv[5], states, + SFO_NOT_STATIC) != TCL_OK) { + TreeRowLabelList_Free(&rows); + return TCL_ERROR; + } + if (states[0] || states[1] || states[2]) { + ROW_FOR_EACH(row_, &rows, &iter) { + row = (RowLabel *) row_; + stateOn = states[STATE_OP_ON]; + stateOff = states[STATE_OP_OFF]; + stateOn |= ~row->state & states[STATE_OP_TOGGLE]; + stateOff |= row->state & states[STATE_OP_TOGGLE]; + Row_ChangeState(row, stateOff, stateOn); + } + } + TreeRowLabelList_Free(&rows); + return TCL_OK; + } + } + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * RowTagCmd -- + * + * This procedure is invoked to process the [rowlabel tag] widget + * command. See the user documentation for details on what + * it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +RowTagCmd( + ClientData clientData, /* Widget info. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *CONST objv[] /* Argument values. */ + ) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + static CONST char *commandNames[] = { + "add", "expr", "names", "remove", (char *) NULL + }; + enum { + COMMAND_ADD, COMMAND_EXPR, COMMAND_NAMES, COMMAND_REMOVE + }; + int index; + RowForEach iter; + TreeRowLabelList rows; + TreeRowLabel _row; + RowLabel *row; + int result = TCL_OK; + + if (objc < 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "command ?arg arg ...?"); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(interp, objv[3], commandNames, "command", 0, + &index) != TCL_OK) + { + return TCL_ERROR; + } + + switch (index) + { + /* T rowlabel tag add R tagList */ + case COMMAND_ADD: + { + int i, numTags; + Tcl_Obj **listObjv; + Tk_Uid staticTags[STATIC_SIZE], *tags = staticTags; + + if (objc != 6) + { + Tcl_WrongNumArgs(interp, 4, objv, "rowlabel tagList"); + return TCL_ERROR; + } + if (TreeRowLabelList_FromObj(tree, objv[4], &rows, 0) != TCL_OK) { + return TCL_ERROR; + } + if (Tcl_ListObjGetElements(interp, objv[5], &numTags, &listObjv) != TCL_OK) { + result = TCL_ERROR; + break; + } + STATIC_ALLOC(tags, Tk_Uid, numTags); + for (i = 0; i < numTags; i++) { + tags[i] = Tk_GetUid(Tcl_GetString(listObjv[i])); + } + ROW_FOR_EACH(_row, &rows, &iter) { + row = (RowLabel *) _row; + row->tagInfo = TagInfo_Add(row->tagInfo, tags, numTags); + } + STATIC_FREE(tags, Tk_Uid, numTags); + break; + } + + /* T rowlabel tag expr R tagExpr */ + case COMMAND_EXPR: + { + TagExpr expr; + int ok = TRUE; + + if (objc != 6) + { + Tcl_WrongNumArgs(interp, 4, objv, "rowlabel tagExpr"); + return TCL_ERROR; + } + if (TreeRowLabelList_FromObj(tree, objv[4], &rows, 0) != TCL_OK) { + return TCL_ERROR; + } + if (TagExpr_Init(tree, objv[5], &expr) != TCL_OK) { + result = TCL_ERROR; + break; + } + ROW_FOR_EACH(_row, &rows, &iter) { + row = (RowLabel *) _row; + if (!TagExpr_Eval(&expr, row->tagInfo)) { + ok = FALSE; + break; + } + } + TagExpr_Free(&expr); + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(ok)); + break; + } + + /* T rowlabel tag names R */ + case COMMAND_NAMES: + { + Tcl_Obj *listObj; + Tk_Uid *tags = NULL; + int i, tagSpace, numTags = 0; + + if (objc != 5) + { + Tcl_WrongNumArgs(interp, 4, objv, "rowlabel"); + return TCL_ERROR; + } + if (TreeRowLabelList_FromObj(tree, objv[4], &rows, 0) != TCL_OK) { + return TCL_ERROR; + } + ROW_FOR_EACH(_row, &rows, &iter) { + row = (RowLabel *) _row; + tags = TagInfo_Names(row->tagInfo, tags, &numTags, &tagSpace); + } + if (numTags) { + listObj = Tcl_NewListObj(0, NULL); + for (i = 0; i < numTags; i++) { + Tcl_ListObjAppendElement(NULL, listObj, + Tcl_NewStringObj((char *) tags[i], -1)); + } + Tcl_SetObjResult(interp, listObj); + ckfree((char *) tags); + } + break; + } + + /* T rowlabel tag remove R tagList */ + case COMMAND_REMOVE: + { + int i, numTags; + Tcl_Obj **listObjv; + Tk_Uid staticTags[STATIC_SIZE], *tags = staticTags; + + if (objc != 6) + { + Tcl_WrongNumArgs(interp, 4, objv, "rowlabel tagList"); + return TCL_ERROR; + } + if (TreeRowLabelList_FromObj(tree, objv[4], &rows, 0) != TCL_OK) { + return TCL_ERROR; + } + if (Tcl_ListObjGetElements(interp, objv[5], &numTags, &listObjv) != TCL_OK) { + result = TCL_ERROR; + break; + } + STATIC_ALLOC(tags, Tk_Uid, numTags); + for (i = 0; i < numTags; i++) { + tags[i] = Tk_GetUid(Tcl_GetString(listObjv[i])); + } + ROW_FOR_EACH(_row, &rows, &iter) { + row = (RowLabel *) _row; + row->tagInfo = TagInfo_Remove(row->tagInfo, tags, numTags); + } + STATIC_FREE(tags, Tk_Uid, numTags); + break; + } + } + + TreeRowLabelList_Free(&rows); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowCmd -- + * + * This procedure is invoked to process the [row] widget + * command. See the user documentation for details on what it + * does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +int +TreeRowLabelCmd( + ClientData clientData, /* Widget info. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *CONST objv[] /* Argument values. */ + ) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + static CONST char *commandNames[] = { + "bbox", "cget", "compare", "configure", "count", "create", "delete", + "dragcget", "dragconfigure", "element", "id", "image", "list", "move", + "neededheight", "order", "state", "tag", "text", "width", (char *) NULL + }; + enum { + COMMAND_BBOX, COMMAND_CGET, COMMAND_COMPARE, COMMAND_CONFIGURE, + COMMAND_COUNT, COMMAND_CREATE, COMMAND_DELETE, COMMAND_DRAGCGET, + COMMAND_DRAGCONF, COMMAND_ELEMENT, COMMAND_ID, COMMAND_IMAGE, + COMMAND_LIST, COMMAND_MOVE, COMMAND_NHEIGHT, COMMAND_ORDER, + COMMAND_STATE, COMMAND_TAG, COMMAND_TEXT, COMMAND_WIDTH + }; + int index; + TreeRowLabelList rows; + TreeRowLabel _row; + RowLabel *row; + RowForEach iter; + + if (objc < 3) { + Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg ...?"); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(interp, objv[2], commandNames, "command", 0, + &index) != TCL_OK) { + return TCL_ERROR; + } + + switch (index) { + case COMMAND_BBOX: + { + int x, y, w, h; + + if (objc != 4) { + Tcl_WrongNumArgs(interp, 3, objv, "rowlabel"); + return TCL_ERROR; + } + if (TreeRowLabel_FromObj(tree, objv[3], &_row, + RFO_NOT_MANY | RFO_NOT_NULL) != TCL_OK) + return TCL_ERROR; + if (Tree_RowLabelBbox(tree, _row, &x, &y, &w, &h) < 0) + break; + x -= tree->xOrigin; + y -= tree->yOrigin; + FormatResult(interp, "%d %d %d %d", x, y, x + w, y + h); + break; + } + + case COMMAND_CGET: + { + TreeRowLabel row; + Tcl_Obj *resultObjPtr; + + if (objc != 5) { + Tcl_WrongNumArgs(interp, 3, objv, "rowlabel option"); + return TCL_ERROR; + } + if (TreeRowLabel_FromObj(tree, objv[3], &row, + RFO_NOT_MANY | RFO_NOT_NULL) != TCL_OK) + return TCL_ERROR; + resultObjPtr = Tk_GetOptionValue(interp, (char *) row, + ((RowLabel *) row)->optionTable, objv[4], tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, resultObjPtr); + break; + } + + /* T row compare C op C */ + case COMMAND_COMPARE: + { + TreeRowLabel row1, row2; + static CONST char *opName[] = { "<", "<=", "==", ">=", ">", "!=", NULL }; + int op, compare = 0, index1, index2; + + if (objc != 6) { + Tcl_WrongNumArgs(interp, 3, objv, "rowlabel1 op rowlabel2"); + return TCL_ERROR; + } + if (TreeRowLabel_FromObj(tree, objv[3], &row1, + RFO_NOT_MANY | RFO_NOT_NULL) != TCL_OK) + return TCL_ERROR; + if (Tcl_GetIndexFromObj(interp, objv[4], opName, + "comparison operator", 0, &op) != TCL_OK) + return TCL_ERROR; + if (TreeRowLabel_FromObj(tree, objv[5], &row2, + RFO_NOT_MANY | RFO_NOT_NULL) != TCL_OK) + return TCL_ERROR; + index1 = TreeRowLabel_Index(row1); + index2 = TreeRowLabel_Index(row2); + switch (op) { + case 0: compare = index1 < index2; break; + case 1: compare = index1 <= index2; break; + case 2: compare = index1 == index2; break; + case 3: compare = index1 >= index2; break; + case 4: compare = index1 > index2; break; + case 5: compare = index1 != index2; break; + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(compare)); + break; + } + + /* T rowlabel configure R ?option? ?value? ?option value ...? */ + case COMMAND_CONFIGURE: + { + int result = TCL_OK; + + if (objc < 4) { + Tcl_WrongNumArgs(interp, 3, objv, "rowlabel ?option? ?value? ?option value ...?"); + return TCL_ERROR; + } + if (objc <= 5) { + Tcl_Obj *resultObjPtr; + if (TreeRowLabel_FromObj(tree, objv[3], &_row, + RFO_NOT_MANY | RFO_NOT_NULL) != TCL_OK) + return TCL_ERROR; + row = (RowLabel *) _row; + resultObjPtr = Tk_GetOptionInfo(interp, (char *) row, + row->optionTable, + (objc == 4) ? (Tcl_Obj *) NULL : objv[4], + tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, resultObjPtr); + break; + } + if (TreeRowLabelList_FromObj(tree, objv[3], &rows, + RFO_NOT_NULL) != TCL_OK) { + return TCL_ERROR; + } + ROW_FOR_EACH(_row, &rows, &iter) { + if (Row_Config((RowLabel *) _row, objc - 4, objv + 4, FALSE) + != TCL_OK) { + result = TCL_ERROR; + break; + }; + } + TreeRowLabelList_Free(&rows); + return result; + } + + case COMMAND_CREATE: + { + RowLabel *row, *last = (RowLabel *) tree->rowLabelLast; + + /* FIXME: -count N -tags $tags */ + row = Row_Alloc(tree); + if (Row_Config(row, objc - 3, objv + 3, TRUE) != TCL_OK) + { + Row_Free(row); + return TCL_ERROR; + } + + if (tree->rows == NULL) { + row->index = 0; + tree->rows = (TreeRowLabel) row; + } else { + last->next = row; + row->prev = last; + row->index = last->index + 1; + } + tree->rowLabelLast = (TreeRowLabel) row; + + tree->neededWidthOfRows = -1; + Tree_DInfoChanged(tree, DINFO_REDO_RANGES); + Tcl_SetObjResult(interp, TreeRowLabel_ToObj(tree, (TreeRowLabel) row)); + break; + } + + case COMMAND_DELETE: + { + RowLabel *prev, *next; + + if (objc != 4) { + Tcl_WrongNumArgs(interp, 3, objv, "rowlabel"); + return TCL_ERROR; + } + if (TreeRowLabelList_FromObj(tree, objv[3], &rows, 0) != TCL_OK) + return TCL_ERROR; + ROW_FOR_EACH(_row, &rows, &iter) { + if (iter.all) { + row = (RowLabel *) tree->rows; + while (row != NULL) { + row = Row_Free(row); + } + tree->rows = NULL; + tree->rowLabelLast = NULL; + tree->rowDrag.row = tree->rowDrag.indRow = NULL; + tree->neededWidthOfRows = -1; + Tree_DInfoChanged(tree, DINFO_DRAW_ROWLABELS); + break; + } + row = (RowLabel *) _row; + + if (row == (RowLabel *) tree->rowDrag.row) + tree->rowDrag.row = NULL; + if (row == (RowLabel *) tree->rowDrag.indRow) + tree->rowDrag.indRow = NULL; + + prev = row->prev; + next = row->next; + if (prev != NULL) + prev->next = next; + if (next != NULL) + next->prev = prev; + if (prev == NULL) + tree->rows = (TreeRowLabel) next; + if (next == NULL) + tree->rowLabelLast = (TreeRowLabel) prev; + (void) Row_Free(row); + } + TreeRowLabelList_Free(&rows); + + /* Renumber rows */ + index = 0; + row = (RowLabel *) tree->rows; + while (row != NULL) { + row->index = index++; + row = row->next; + } + + tree->neededWidthOfRows = -1; + Tree_DInfoChanged(tree, DINFO_REDO_RANGES); + break; + } + + /* T rowlabel dragcget option */ + case COMMAND_DRAGCGET: + { + Tcl_Obj *resultObjPtr; + + if (objc != 4) { + Tcl_WrongNumArgs(interp, 3, objv, "option"); + return TCL_ERROR; + } + resultObjPtr = Tk_GetOptionValue(interp, (char *) tree, + tree->rowDrag.optionTable, objv[3], tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, resultObjPtr); + break; + } + + /* T rowlabel dragconfigure ?option? ?value? ?option value ...? */ + case COMMAND_DRAGCONF: + { + Tcl_Obj *resultObjPtr; + Tk_SavedOptions savedOptions; + int mask, result; + + if (objc < 3) { + Tcl_WrongNumArgs(interp, 3, objv, "?option? ?value?"); + return TCL_ERROR; + } + if (objc <= 4) { + resultObjPtr = Tk_GetOptionInfo(interp, (char *) tree, + tree->rowDrag.optionTable, + (objc == 3) ? (Tcl_Obj *) NULL : objv[3], + tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, resultObjPtr); + break; + } + result = Tk_SetOptions(interp, (char *) tree, + tree->rowDrag.optionTable, objc - 3, objv + 3, tree->tkwin, + &savedOptions, &mask); + if (result != TCL_OK) { + Tk_RestoreSavedOptions(&savedOptions); + return TCL_ERROR; + } + Tk_FreeSavedOptions(&savedOptions); + + if (tree->rowDrag.alpha < 0) + tree->rowDrag.alpha = 0; + if (tree->rowDrag.alpha > 255) + tree->rowDrag.alpha = 255; + + Tree_DInfoChanged(tree, DINFO_DRAW_HEADER); + break; + } + + case COMMAND_COUNT: + { + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, (char *) NULL); + return TCL_ERROR; + } + Tcl_SetObjResult(interp, Tcl_NewIntObj(tree->rowCount)); + break; + } + + case COMMAND_ELEMENT: + { + return RowElementCmd(clientData, interp, objc, objv); + } + + case COMMAND_ID: + { + Tcl_Obj *listObj; + + if (objc != 4) { + Tcl_WrongNumArgs(interp, 3, objv, "rowlabel"); + return TCL_ERROR; + } + if (TreeRowLabelList_FromObj(tree, objv[3], &rows, 0) != TCL_OK) + return TCL_ERROR; + listObj = Tcl_NewListObj(0, NULL); + ROW_FOR_EACH(_row, &rows, &iter) { + Tcl_ListObjAppendElement(interp, listObj, + TreeRowLabel_ToObj(tree, _row)); + } + TreeRowLabelList_Free(&rows); + Tcl_SetObjResult(interp, listObj); + break; + } + + /* T rowlabel list ?-visible? */ + case COMMAND_LIST: + { + RowLabel *row = (RowLabel *) tree->rows; + Tcl_Obj *listObj; + int visible = FALSE; + + if (objc > 4) { + Tcl_WrongNumArgs(interp, 3, objv, "?-visible?"); + return TCL_ERROR; + } + if (objc == 4) { + int len; + char *s = Tcl_GetStringFromObj(objv[3], &len); + if ((s[0] == '-') && (strncmp(s, "-visible", len) == 0)) + visible = TRUE; + else { + FormatResult(interp, "bad switch \"%s\": must be -visible", + s); + return TCL_ERROR; + } + } + listObj = Tcl_NewListObj(0, NULL); + while (row != NULL) { + if (!visible || row->visible) + Tcl_ListObjAppendElement(interp, listObj, + TreeRowLabel_ToObj(tree, (TreeRowLabel) row)); + row = row->next; + } + Tcl_SetObjResult(interp, listObj); + break; + } + + /* T rowlabel move R before */ + case COMMAND_MOVE: + { + TreeRowLabel _move, _before; + RowLabel *move, *before, *prev, *next, *last, *rowAfterLast = NULL; + + if (objc != 5) { + Tcl_WrongNumArgs(interp, 3, objv, "rowlabel before"); + return TCL_ERROR; + } + if (TreeRowLabel_FromObj(tree, objv[3], &_move, + RFO_NOT_MANY | RFO_NOT_NULL ) != TCL_OK) + return TCL_ERROR; + move = (RowLabel *) _move; + if (TreeRowLabel_FromObj(tree, objv[4], &_before, + RFO_NOT_MANY | RFO_NOT_NULL) != TCL_OK) + return TCL_ERROR; + before = (RowLabel *) _before; + if (move == before) + break; + if (move->index == before->index - 1) + break; + + /* Unlink. */ + prev = move->prev; + next = move->next; + if (prev == NULL) + tree->rows = (TreeRowLabel) next; + else + prev->next = next; + if (next == NULL) + tree->rowLabelLast = (TreeRowLabel) prev; + else + next->prev = prev; + + /* Link. */ + if (before == rowAfterLast) { /* FIXME */ + last = (RowLabel *) tree->rowLabelLast; + last->next = move; + move->prev = last; + move->next = NULL; + tree->rowLabelLast = (TreeRowLabel) move; + } else { + prev = before->prev; + if (prev == NULL) + tree->rows = (TreeRowLabel) move; + else + prev->next = move; + before->prev = move; + move->prev = prev; + move->next = before; + } + + /* Renumber rows */ + index = 0; + row = (RowLabel *) tree->rows; + while (row != NULL) { + row->index = index++; + row = row->next; + } + + if (move->visible) { + Tree_DInfoChanged(tree, DINFO_REDO_RANGES); + } + break; + } + + case COMMAND_NHEIGHT: + { + TreeRowLabel _row; + + if (objc != 4) { + Tcl_WrongNumArgs(interp, 3, objv, "rowlabel"); + return TCL_ERROR; + } + if (TreeRowLabel_FromObj(tree, objv[3], &_row, + RFO_NOT_MANY | RFO_NOT_NULL) != TCL_OK) + return TCL_ERROR; + + Tcl_SetObjResult(interp, Tcl_NewIntObj(TreeRowLabel_NeededHeight(_row))); + break; + } + + /* T rowlabel order R ?-visible? */ + case COMMAND_ORDER: + { + int visible = FALSE; + int index = 0; + + if (objc < 4 || objc > 5) { + Tcl_WrongNumArgs(interp, 3, objv, "rowlabel ?-visible?"); + return TCL_ERROR; + } + if (objc == 5) { + int len; + char *s = Tcl_GetStringFromObj(objv[4], &len); + if ((s[0] == '-') && (strncmp(s, "-visible", len) == 0)) + visible = TRUE; + else { + FormatResult(interp, "bad switch \"%s\": must be -visible", + s); + return TCL_ERROR; + } + } + if (TreeRowLabel_FromObj(tree, objv[3], &_row, + RFO_NOT_MANY | RFO_NOT_NULL) != TCL_OK) + return TCL_ERROR; + row = (RowLabel *) _row; + if (visible) { + RowLabel *walk = (RowLabel *) tree->rows; + while (walk != NULL) { + if (walk == row) + break; + if (walk->visible) + index++; + walk = walk->next; + } + if (!row->visible) + index = -1; + } else { + index = row->index; + } + Tcl_SetObjResult(interp, Tcl_NewIntObj(index)); + break; + } + + case COMMAND_STATE: + { + return RowStateCmd(clientData, interp, objc, objv); + } + + case COMMAND_TAG: + { + return RowTagCmd(clientData, interp, objc, objv); + } + + /* T row image R ?image? */ + case COMMAND_IMAGE: + /* T row text R ?text? */ + case COMMAND_TEXT: + { + Tcl_Obj *objPtr; + int isImage = (index == COMMAND_IMAGE); + int result = TCL_OK; + + if (objc < 4 || objc > 5) { + Tcl_WrongNumArgs(interp, 3, objv, + isImage ? "rowlabel ?image?" : "rowlabel ?text?"); + return TCL_ERROR; + } + if (objc == 4) { + if (TreeRowLabel_FromObj(tree, objv[3], &_row, + RFO_NOT_MANY | RFO_NOT_NULL) != TCL_OK) + return TCL_ERROR; + row = (RowLabel *) _row; + if (row->style == NULL) { + NoStyleMsg(row); + return TCL_ERROR; + } + objPtr = isImage ? + TreeStyle_GetImage(tree, row->style) : + TreeStyle_GetText(tree, row->style); + if (objPtr != NULL) + Tcl_SetObjResult(interp, objPtr); + break; + } + if (TreeRowLabelList_FromObj(tree, objv[3], &rows, + RFO_NOT_NULL) != TCL_OK) + return TCL_ERROR; + ROW_FOR_EACH(_row, &rows, &iter) { + row = (RowLabel *) _row; + result = isImage ? + TreeStyle_SetImage(tree, (TreeItem) NULL, + (TreeItemColumn) NULL, _row, row->style, objv[4]) : + TreeStyle_SetText(tree, (TreeItem) NULL, + (TreeItemColumn) NULL, _row, row->style, objv[4]); + if (result != TCL_OK) + break; + } + TreeRowLabelList_Free(&rows); + tree->neededWidthOfRows = -1; + Tree_DInfoChanged(tree, DINFO_DRAW_ROWLABELS); + return result; + } + + case COMMAND_WIDTH: + { + if (objc != 3) { + Tcl_WrongNumArgs(interp, 3, objv, NULL); + return TCL_ERROR; + } + Tcl_SetObjResult(interp, Tcl_NewIntObj(Tree_WidthOfRowLabels(tree))); + break; + } + } + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowLabel_Draw -- + * + * Draw a row label. + * + * Results: + * None. + * + * Side effects: + * Stuff is drawn in a drawable. + * + *---------------------------------------------------------------------- + */ + +void +TreeRowLabel_Draw( + TreeRowLabel row_, /* RowLabel record. */ + int x, int y, /* Drawable coordinates of the row. */ + int width, int height, /* Total size of the row. */ + Drawable drawable /* Where to draw. */ + ) +{ + RowLabel *row = (RowLabel *) row_; + TreeCtrl *tree = row->tree; + StyleDrawArgs drawArgs; + + if (row->style == NULL) + return; + + drawArgs.tree = tree; + drawArgs.drawable = drawable; + drawArgs.state = row->state; + drawArgs.style = row->style; + drawArgs.indent = 0; + drawArgs.x = x; + drawArgs.y = y; + drawArgs.width = width; + drawArgs.height = height; + drawArgs.justify = TK_JUSTIFY_LEFT; + TreeStyle_Draw(&drawArgs); +} + +/* + *---------------------------------------------------------------------- + * + * TreeRowLabel_Identify -- + * + * Determine which element the given point is in. + * This is used by the [identify] widget command. + * + * Results: + * If the RowLabel is not visible or no Rows are visible + * then buf[] is untouched. Otherwise the given string may be + * appended with "elem E". + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TreeRowLabel_Identify( + TreeRowLabel row_, /* RowLabel token. */ + int x, int y, /* RowLabel coords to hit-test with. */ + char *buf /* NULL-terminated string which may be + * appended. */ + ) +{ + RowLabel *row = (RowLabel *) row_; + TreeCtrl *tree = row->tree; + int left, top, width, height; + StyleDrawArgs drawArgs; + char *elem; + + if (row->style == NULL) + return; + + if (Tree_RowLabelBbox(tree, row_, &left, &top, &width, &height) < 0) + return; + drawArgs.tree = tree; + drawArgs.drawable = None; + drawArgs.state = row->state; + drawArgs.style = row->style; + drawArgs.indent = 0; + drawArgs.x = 0; + drawArgs.y = 0; + drawArgs.width = width; + drawArgs.height = height; + drawArgs.justify = TK_JUSTIFY_LEFT; + elem = TreeStyle_Identify(&drawArgs, x, y); + if (elem != NULL) + sprintf(buf + strlen(buf), " elem %s", elem); +} + +#if 0 +/* + *---------------------------------------------------------------------- + * + * SetImageForRow -- + * + * Set a photo image containing a simplified picture of a row label. + * This image is used when dragging and dropping a row label. + * + * Results: + * Token for a photo image, or NULL if the image could not be + * created. + * + * Side effects: + * A photo image called "TreeCtrlRowImage" will be created if + * it doesn't exist. The image is set to contain a picture of the + * row label. + * + *---------------------------------------------------------------------- + */ + +static Tk_Image +SetImageForRow( + TreeCtrl *tree, /* Widget info. */ + RowLabel *row /* Row record. */ + ) +{ + Tk_PhotoHandle photoH; + Pixmap pixmap; + int width = Tree_WidthOfRowLabels(tree); + int height = tree->headerHeight; + XImage *ximage; + + photoH = Tk_FindPhoto(tree->interp, "TreeCtrlRowImage"); + if (photoH == NULL) { + Tcl_GlobalEval(tree->interp, "image create photo TreeCtrlRowImage"); + photoH = Tk_FindPhoto(tree->interp, "TreeCtrlRowImage"); + if (photoH == NULL) + return NULL; + } + + pixmap = Tk_GetPixmap(tree->display, Tk_WindowId(tree->tkwin), + width, height, Tk_Depth(tree->tkwin)); + + Row_Draw(row, pixmap, 0, 0, TRUE); + + /* Pixmap -> XImage */ + ximage = XGetImage(tree->display, pixmap, 0, 0, + (unsigned int)width, (unsigned int)height, AllPlanes, ZPixmap); + if (ximage == NULL) + panic("ximage is NULL"); + + /* XImage -> Tk_Image */ + XImage2Photo(tree->interp, photoH, ximage, tree->rowDrag.alpha); + + XDestroyImage(ximage); + Tk_FreePixmap(tree->display, pixmap); + + return Tk_GetImage(tree->interp, tree->tkwin, "TreeCtrlRowImage", + NULL, (ClientData) NULL); +} + +/* + *---------------------------------------------------------------------- + * + * Tree_DrawHeader -- + * + * Draw the header of every row. + * + * Results: + * None. + * + * Side effects: + * Stuff is drawn in a drawable. + * + *---------------------------------------------------------------------- + */ + +void +Tree_DrawHeader( + TreeCtrl *tree, /* Widget info. */ + Drawable drawable, /* Where to draw. */ + int x, int y /* Top-left corner of the header. */ + ) +{ + RowLabel *column = (RowLabel *) tree->columns; + Tk_Window tkwin = tree->tkwin; + int minX, maxX, width, height; + Drawable pixmap; + int x2 = x; + + /* Update layout if needed */ + (void) Tree_HeaderHeight(tree); + (void) Tree_WidthOfRows(tree); + + minX = tree->inset; + maxX = Tk_Width(tkwin) - tree->inset; + + if (tree->doubleBuffer == DOUBLEBUFFER_ITEM) + pixmap = Tk_GetPixmap(tree->display, Tk_WindowId(tkwin), + Tk_Width(tkwin), tree->inset + tree->headerHeight, Tk_Depth(tkwin)); + else + pixmap = drawable; + + while (column != NULL) { + if (column->visible) { + if ((x < maxX) && (x + column->useWidth > minX)) + Row_Draw(column, pixmap, x, y, FALSE); + x += column->useWidth; + } + column = column->next; + } + + /* Draw "tail" column */ + if (x < maxX) { + column = (RowLabel *) tree->columnTail; + width = maxX - x + column->borderWidth; + height = tree->headerHeight; + if (tree->useTheme && + (TreeTheme_DrawHeaderItem(tree, pixmap, 0, 0, x, y, width, height) == TCL_OK)) { + } else { + Tk_3DBorder border; + border = PerStateBorder_ForState(tree, &column->border, + Row_MakeState(column), NULL); + if (border == NULL) + border = tree->border; + Tk_Fill3DRectangle(tkwin, pixmap, border, + x, y, width, height, column->borderWidth, TK_RELIEF_RAISED); + } + } + + { + Tk_Image image = NULL; + int imageX = 0, imageW = 0, indDraw = FALSE, indX = 0; + + column = (RowLabel *) tree->columns; + while (column != NULL) { + if (column->visible) { + if (column == (RowLabel *) tree->columnDrag.column) { + image = SetImageForRow(tree, column); + imageX = x2; + imageW = column->useWidth; + } + if (column == (RowLabel *) tree->columnDrag.indRow) { + indX = x2 - 1; + indDraw = TRUE; + } + x2 += column->useWidth; + } + if (tree->columnDrag.indRow == tree->columnTail) { + indX = x2 - 1; + indDraw = TRUE; + } + column = column->next; + } + if (indDraw) { + GC gc = Tk_GCForColor(tree->columnDrag.indColor, Tk_WindowId(tree->tkwin)); + XFillRectangle(tree->display, pixmap, gc, + indX, y, 2, tree->headerHeight); + } + if (image != NULL) { +#if !defined(WIN32) && !defined(MAC_TCL) && !defined(MAC_OSX_TK) + int ix = 0, iy = 0, iw = imageW, ih = tree->headerHeight; + /* + * Do boundary clipping, so that Tk_RedrawImage is passed + * valid coordinates. [Tk Bug 979239] + */ + imageX += tree->columnDrag.offset; + if (imageX < minX) { + ix = minX - imageX; + iw -= ix; + imageX = minX; + } else if (imageX + imageW >= maxX) { + iw -= (imageX + imageW) - maxX; + } + Tk_RedrawImage(image, ix, iy, iw, ih, pixmap, + imageX, y); +#else + Tk_RedrawImage(image, 0, 0, imageW, + tree->headerHeight, pixmap, + imageX + tree->columnDrag.offset, y); +#endif + Tk_FreeImage(image); + } + } + + if (tree->doubleBuffer == DOUBLEBUFFER_ITEM) { + XCopyArea(tree->display, pixmap, drawable, + tree->copyGC, minX, y, + maxX - minX, tree->headerHeight, + tree->inset, y); + + Tk_FreePixmap(tree->display, pixmap); + } +} +#endif + +/* + *---------------------------------------------------------------------- + * + * Tree_WidthOfRowLabels -- + * + * Returns the display width of the row labels. + * + * Results: + * If the -rowlabelwidth option is set, that is the result. Otherwise + * the result is the maximum requested width of every visible row + * label, clipped to -minrowlabelwidth and -maxrowlabelwidth. + * + * Side effects: + * The size of elements and styles may be updated if they are + * marked out-of-date. + * + *---------------------------------------------------------------------- + */ + +int +Tree_WidthOfRowLabels( + TreeCtrl *tree /* Widget info. */ + ) +{ + RowLabel *row; + int width; + + if (!tree->showRowLabels) + return 0; + + /* The treectrl option -rowlabelwidth specifies a fixed width for + * the row labels. */ + if (tree->rowLabelWidthObj != NULL && tree->rowLabelWidth >= 0) + return tree->rowLabelWidth; + + /* Recalculate the maximum requested width of every visible row label. */ + if (tree->neededWidthOfRows < 0) { + width = 0; + row = (RowLabel *) tree->rows; + while (row != NULL) { + if (row->visible) { + width = MAX(TreeRowLabel_NeededWidth((TreeRowLabel) row), width); + } + row = row->next; + } + tree->neededWidthOfRows = width; + } + + width = tree->neededWidthOfRows; + if ((tree->minRowLabelWidthObj != NULL) && (tree->minRowLabelWidth >= 0) && + (width < tree->minRowLabelWidth)) + width = tree->minRowLabelWidth; + if ((tree->maxRowLabelWidthObj != NULL) && (tree->maxRowLabelWidth >= 0) && + (width > tree->maxRowLabelWidth)) + width = tree->maxRowLabelWidth; + return width; +} + +/* + *---------------------------------------------------------------------- + * + * Tree_InitRowLabels -- + * + * Perform row-related initialization when a new TreeCtrl is + * created. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Memory is allocated. + * + *---------------------------------------------------------------------- + */ + +void +Tree_InitRowLabels( + TreeCtrl *tree /* Widget info. */ + ) +{ + tree->rows = NULL; + tree->rowLabelLast = NULL; + tree->nextRowId = 0; + tree->rowCount = 0; + tree->neededWidthOfRows = -1; + + Tcl_InitHashTable(&tree->rowIDHash, TCL_ONE_WORD_KEYS); + + tree->rowDrag.optionTable = Tk_CreateOptionTable(tree->interp, dragSpecs); + (void) Tk_InitOptions(tree->interp, (char *) tree, + tree->rowDrag.optionTable, tree->tkwin); +} + +/* + *---------------------------------------------------------------------- + * + * Tree_FreeRowLabels -- + * + * Free row-related resources for a deleted TreeCtrl. + * + * Results: + * None. + * + * Side effects: + * Memory is deallocated. + * + *---------------------------------------------------------------------- + */ + +void Tree_FreeRowLabels( + TreeCtrl *tree /* Widget info. */ + ) +{ + RowLabel *row = (RowLabel *) tree->rows; + + while (row != NULL) { + row = Row_Free(row); + } + Tcl_DeleteHashTable(&tree->rowIDHash); +} + -- cgit v0.12