diff options
Diffstat (limited to 'generic/ttk/ttkTreeview.c')
-rw-r--r-- | generic/ttk/ttkTreeview.c | 931 |
1 files changed, 581 insertions, 350 deletions
diff --git a/generic/ttk/ttkTreeview.c b/generic/ttk/ttkTreeview.c index d9f61f9..b144103 100644 --- a/generic/ttk/ttkTreeview.c +++ b/generic/ttk/ttkTreeview.c @@ -1,10 +1,11 @@ -/* $Id: ttkTreeview.c,v 1.23.2.1 2010/05/31 17:22:48 jenglish Exp $ +/* $Id: ttkTreeview.c,v 1.23.2.2 2010/08/26 02:06:10 hobbs Exp $ * Copyright (c) 2004, Joe English * * ttk::treeview widget implementation. */ #include <string.h> +#include <stdio.h> #include <tk.h> #include "ttkTheme.h" #include "ttkWidget.h" @@ -33,8 +34,7 @@ static const int HALO = 4; /* separator */ */ typedef struct TreeItemRec TreeItem; -struct TreeItemRec -{ +struct TreeItemRec { Tcl_HashEntry *entryPtr; /* Back-pointer to hash table entry */ TreeItem *parent; /* Parent item */ TreeItem *children; /* Linked list of child items */ @@ -50,16 +50,24 @@ struct TreeItemRec Tcl_Obj *valuesObj; Tcl_Obj *openObj; Tcl_Obj *tagsObj; + + /* + * Derived resources: + */ + Ttk_TagSet tagset; + Ttk_ImageSpec *imagespec; }; -static Tk_OptionSpec ItemOptionSpecs[] = -{ +#define ITEM_OPTION_TAGS_CHANGED 0x100 +#define ITEM_OPTION_IMAGE_CHANGED 0x200 + +static Tk_OptionSpec ItemOptionSpecs[] = { {TK_OPTION_STRING, "-text", "text", "Text", "", Tk_Offset(TreeItem,textObj), -1, 0,0,0 }, {TK_OPTION_STRING, "-image", "image", "Image", NULL, Tk_Offset(TreeItem,imageObj), -1, - TK_OPTION_NULL_OK,0,0 }, + TK_OPTION_NULL_OK,0,ITEM_OPTION_IMAGE_CHANGED }, {TK_OPTION_STRING, "-values", "values", "Values", NULL, Tk_Offset(TreeItem,valuesObj), -1, TK_OPTION_NULL_OK,0,0 }, @@ -68,7 +76,7 @@ static Tk_OptionSpec ItemOptionSpecs[] = 0,0,0 }, {TK_OPTION_STRING, "-tags", "tags", "Tags", NULL, Tk_Offset(TreeItem,tagsObj), -1, - TK_OPTION_NULL_OK,0,0 }, + TK_OPTION_NULL_OK,0,ITEM_OPTION_TAGS_CHANGED }, {TK_OPTION_END, 0,0,0, NULL, -1,-1, 0,0,0} }; @@ -90,6 +98,9 @@ static TreeItem *NewItem(void) item->openObj = NULL; item->tagsObj = NULL; + item->tagset = NULL; + item->imagespec = NULL; + return item; } @@ -103,6 +114,10 @@ static void FreeItem(TreeItem *item) if (item->valuesObj) { Tcl_DecrRefCount(item->valuesObj); } if (item->openObj) { Tcl_DecrRefCount(item->openObj); } if (item->tagsObj) { Tcl_DecrRefCount(item->tagsObj); } + + if (item->tagset) { Ttk_FreeTagSet(item->tagset); } + if (item->imagespec) { TtkFreeImageSpec(item->imagespec); } + ckfree((ClientData)item); } @@ -168,14 +183,13 @@ static TreeItem *NextPreorder(TreeItem *item) typedef struct { Tcl_Obj *textObj; /* taken from item / data cell */ Tcl_Obj *imageObj; /* taken from item */ - Tcl_Obj *anchorObj; /* from column */ + Tcl_Obj *anchorObj; /* from column <<NOTE-ANCHOR>> */ Tcl_Obj *backgroundObj; /* remainder from tag */ Tcl_Obj *foregroundObj; Tcl_Obj *fontObj; } DisplayItem; -static Tk_OptionSpec TagOptionSpecs[] = -{ +static Tk_OptionSpec TagOptionSpecs[] = { {TK_OPTION_STRING, "-text", "text", "Text", NULL, Tk_Offset(DisplayItem,textObj), -1, TK_OPTION_NULL_OK,0,0 }, @@ -184,14 +198,14 @@ static Tk_OptionSpec TagOptionSpecs[] = TK_OPTION_NULL_OK,0,0 }, {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor", NULL, Tk_Offset(DisplayItem,anchorObj), -1, - TK_OPTION_NULL_OK, 0, GEOMETRY_CHANGED}, - {TK_OPTION_STRING, "-background", "windowColor", "WindowColor", /*SB:COLOR*/ + TK_OPTION_NULL_OK, 0, GEOMETRY_CHANGED}, /* <<NOTE-ANCHOR>> */ + {TK_OPTION_COLOR, "-background", "windowColor", "WindowColor", NULL, Tk_Offset(DisplayItem,backgroundObj), -1, TK_OPTION_NULL_OK,0,0 }, - {TK_OPTION_STRING, "-foreground", "textColor", "TextColor", /*SB:COLOR*/ + {TK_OPTION_COLOR, "-foreground", "textColor", "TextColor", NULL, Tk_Offset(DisplayItem,foregroundObj), -1, TK_OPTION_NULL_OK,0,0 }, - {TK_OPTION_STRING, "-font", "font", "Font", /* SB:FONT */ + {TK_OPTION_FONT, "-font", "font", "Font", NULL, Tk_Offset(DisplayItem,fontObj), -1, TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED }, @@ -211,7 +225,7 @@ typedef struct { int stretch; /* Should column stretch while resizing? */ Tcl_Obj *idObj; /* Column identifier, from -columns option */ - Tcl_Obj *anchorObj; /* -anchor for cell data */ + Tcl_Obj *anchorObj; /* -anchor for cell data <<NOTE-ANCHOR>> */ /* Column heading data: */ @@ -259,8 +273,7 @@ static void FreeColumn(TreeColumn *column) /* Don't touch column->data, it's scratch storage */ } -static Tk_OptionSpec ColumnOptionSpecs[] = -{ +static Tk_OptionSpec ColumnOptionSpecs[] = { {TK_OPTION_INT, "-width", "width", "Width", DEF_COLWIDTH, -1, Tk_Offset(TreeColumn,width), 0,0,GEOMETRY_CHANGED }, @@ -271,7 +284,7 @@ static Tk_OptionSpec ColumnOptionSpecs[] = "1", -1, Tk_Offset(TreeColumn,stretch), 0,0,0 }, {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor", - "w", Tk_Offset(TreeColumn,anchorObj), -1, + "w", Tk_Offset(TreeColumn,anchorObj), -1, /* <<NOTE-ANCHOR>> */ 0,0,0 }, {TK_OPTION_STRING, "-id", "id", "ID", NULL, Tk_Offset(TreeColumn,idObj), -1, @@ -279,8 +292,7 @@ static Tk_OptionSpec ColumnOptionSpecs[] = {TK_OPTION_END, 0,0,0, NULL, -1,-1, 0,0,0} }; -static Tk_OptionSpec HeadingOptionSpecs[] = -{ +static Tk_OptionSpec HeadingOptionSpecs[] = { {TK_OPTION_STRING, "-text", "text", "Text", "", Tk_Offset(TreeColumn,headingObj), -1, 0,0,0 }, @@ -349,8 +361,7 @@ static int GetEnumSetFromObj( * headingHeight: [layout] * rowHeight, indent: style */ -typedef struct -{ +typedef struct { /* Resources acquired at initialization-time: */ Tk_OptionTable itemOptionTable; @@ -381,6 +392,7 @@ typedef struct TreeColumn *columns; /* Array of column options for data columns */ TreeItem *focus; /* Current focus item */ + TreeItem *endPtr; /* See EndPosition() */ /* Widget options: */ @@ -425,8 +437,7 @@ typedef struct { static const char *SelectModeStrings[] = { "none", "browse", "extended", NULL }; -static Tk_OptionSpec TreeviewOptionSpecs[] = -{ +static Tk_OptionSpec TreeviewOptionSpecs[] = { WIDGET_TAKES_FOCUS, {TK_OPTION_STRING, "-columns", "columns", "Columns", @@ -581,7 +592,7 @@ static TreeItem *FindItem( Tcl_AppendResult(interp, "Item ", itemName, " not found", NULL); return 0; } - return (TreeItem*)Tcl_GetHashValue(entryPtr); + return Tcl_GetHashValue(entryPtr); } /* + GetItemListFromObj -- @@ -887,14 +898,14 @@ static int DistributeWidth(Treeview *tv, int n) /* + ResizeColumns -- * Recompute column widths based on available width. - * Pick up slack first; + * Pick up slack first; * Distribute the remainder evenly across stretchable columns; * If any is still left over due to minwidth constraints, shove left. */ static void ResizeColumns(Treeview *tv, int newWidth) { int delta = newWidth - (TreeWidth(tv) + tv->tree.slack); - DepositSlack(tv, + DepositSlack(tv, ShoveLeft(tv, tv->tree.nDisplayColumns - 1, DistributeWidth(tv, PickupSlack(tv, delta)))); } @@ -915,7 +926,7 @@ static void DragColumn(Treeview *tv, int i, int delta) * +++ Event handlers. */ -static TreeItem *IdentifyItem(Treeview *tv,int y,Ttk_Box *itemPos); /*forward*/ +static TreeItem *IdentifyItem(Treeview *tv, int y); /*forward*/ static const unsigned int TreeviewBindEventMask = KeyPressMask|KeyReleaseMask @@ -928,14 +939,11 @@ static void TreeviewBindEventProc(void *clientData, XEvent *event) { Treeview *tv = clientData; TreeItem *item = NULL; - Ttk_Box unused; - void *taglist; - int nTags; + Ttk_TagSet tagset; /* * Figure out where to deliver the event. */ - switch (event->type) { case KeyPress: @@ -945,10 +953,10 @@ static void TreeviewBindEventProc(void *clientData, XEvent *event) break; case ButtonPress: case ButtonRelease: - item = IdentifyItem(tv, event->xbutton.y, &unused); + item = IdentifyItem(tv, event->xbutton.y); break; case MotionNotify: - item = IdentifyItem(tv, event->xmotion.y, &unused); + item = IdentifyItem(tv, event->xmotion.y); break; default: break; @@ -958,25 +966,28 @@ static void TreeviewBindEventProc(void *clientData, XEvent *event) return; } - /* ASSERT: Ttk_GetTagListFromObj returns TCL_OK. */ - Ttk_GetTagListFromObj(NULL, tv->tree.tagTable, item->tagsObj, - &nTags, &taglist); + /* ASSERT: Ttk_GetTagSetFromObj succeeds. + * NB: must use a local copy of the tagset, + * in case a binding script stomps on -tags. + */ + tagset = Ttk_GetTagSetFromObj(NULL, tv->tree.tagTable, item->tagsObj); /* * Fire binding: */ Tcl_Preserve(clientData); - Tk_BindEvent(tv->tree.bindingTable, event, tv->core.tkwin, nTags, taglist); + Tk_BindEvent(tv->tree.bindingTable, event, tv->core.tkwin, + tagset->nTags, (void **)tagset->tags); Tcl_Release(clientData); - Ttk_FreeTagList(taglist); + Ttk_FreeTagSet(tagset); } /*------------------------------------------------------------------------ * +++ Initialization and cleanup. */ -static int TreeviewInitialize(Tcl_Interp *interp, void *recordPtr) +static void TreeviewInitialize(Tcl_Interp *interp, void *recordPtr) { Treeview *tv = recordPtr; int unused; @@ -991,7 +1002,7 @@ static int TreeviewInitialize(Tcl_Interp *interp, void *recordPtr) Tk_CreateOptionTable(interp, TagOptionSpecs); tv->tree.tagTable = Ttk_CreateTagTable( - tv->tree.tagOptionTable, sizeof(DisplayItem)); + interp, tv->core.tkwin, TagOptionSpecs, sizeof(DisplayItem)); tv->tree.bindingTable = Tk_CreateBindingTable(interp); Tk_CreateEventHandler(tv->core.tkwin, TreeviewBindEventMask, TreeviewBindEventProc, tv); @@ -1021,13 +1032,14 @@ static int TreeviewInitialize(Tcl_Interp *interp, void *recordPtr) Tcl_InitHashTable(&tv->tree.items, TCL_STRING_KEYS); tv->tree.serial = 0; - tv->tree.focus = 0; + tv->tree.focus = tv->tree.endPtr = 0; /* Create root item "": */ tv->tree.root = NewItem(); Tk_InitOptions(interp, (ClientData)tv->tree.root, tv->tree.itemOptionTable, tv->core.tkwin); + tv->tree.root->tagset = Ttk_GetTagSetFromObj(NULL, tv->tree.tagTable, NULL); tv->tree.root->entryPtr = Tcl_CreateHashEntry(&tv->tree.items, "", &unused); Tcl_SetHashValue(tv->tree.root->entryPtr, tv->tree.root); @@ -1040,8 +1052,6 @@ static int TreeviewInitialize(Tcl_Interp *interp, void *recordPtr) */ tv->tree.treeArea = tv->tree.headingArea = Ttk_MakeBox(0,0,0,0); tv->tree.slack = 0; - - return TCL_OK; } static void TreeviewCleanup(void *recordPtr) @@ -1122,9 +1132,13 @@ static int ConfigureItem( int objc, Tcl_Obj *const objv[]) { Tk_SavedOptions savedOptions; + int mask; + Ttk_ImageSpec *newImageSpec = NULL; + Ttk_TagSet newTagSet = NULL; if (Tk_SetOptions(interp, (ClientData)item, tv->tree.itemOptionTable, - objc, objv, tv->core.tkwin,&savedOptions,0) != TCL_OK) + objc, objv, tv->core.tkwin, &savedOptions, &mask) + != TCL_OK) { return TCL_ERROR; } @@ -1137,15 +1151,24 @@ static int ConfigureItem( goto error; } - /* Validate -image option. + /* Check -image. */ - if (item->imageObj) { - Ttk_ImageSpec *imageSpec = - TtkGetImageSpec(interp, tv->core.tkwin, item->imageObj); - if (!imageSpec) { + if ((mask & ITEM_OPTION_IMAGE_CHANGED) && item->imageObj) { + newImageSpec = TtkGetImageSpec(interp, tv->core.tkwin, item->imageObj); + if (!newImageSpec) { + goto error; + } + } + + /* Check -tags. + * Side effect: may create new tags. + */ + if (mask & ITEM_OPTION_TAGS_CHANGED) { + newTagSet = Ttk_GetTagSetFromObj( + interp, tv->tree.tagTable, item->tagsObj); + if (!newTagSet) { goto error; } - TtkFreeImageSpec(imageSpec); /* @@@TODO: Keep this around */ } /* Keep TTK_STATE_OPEN flag in sync with item->openObj. @@ -1162,28 +1185,24 @@ static int ConfigureItem( item->state &= ~TTK_STATE_OPEN; } - /* Make sure -tags is a valid list - * (side effect: may create new tags) - */ - if (item->tagsObj) { - void *taglist; - int nTags; - if (Ttk_GetTagListFromObj(interp, tv->tree.tagTable, item->tagsObj, - &nTags, &taglist) != TCL_OK) - { - goto error; - } - Ttk_FreeTagList(taglist); - } - /* All OK. */ Tk_FreeSavedOptions(&savedOptions); + if (mask & ITEM_OPTION_TAGS_CHANGED) { + if (item->tagset) { Ttk_FreeTagSet(item->tagset); } + item->tagset = newTagSet; + } + if (mask & ITEM_OPTION_IMAGE_CHANGED) { + if (item->imagespec) { TtkFreeImageSpec(item->imagespec); } + item->imagespec = newImageSpec; + } TtkRedisplayWidget(&tv->core); return TCL_OK; error: Tk_RestoreSavedOptions(&savedOptions); + if (newTagSet) { Ttk_FreeTagSet(newTagSet); } + if (newImageSpec) { TtkFreeImageSpec(newImageSpec); } return TCL_ERROR; } @@ -1300,21 +1319,18 @@ static int CountRows(TreeItem *item) static TreeItem *IdentifyRow( Treeview *tv, /* Widget record */ TreeItem *item, /* Where to start search */ - Ttk_Box *bp, /* Scan position */ + int *ypos, /* Scan position */ int y) /* Target y coordinate */ { while (item) { - int next_ypos = bp->y + tv->tree.rowHeight; - if (bp->y <= y && y <= next_ypos) { - bp->height = tv->tree.rowHeight; + int next_ypos = *ypos + tv->tree.rowHeight; + if (*ypos <= y && y <= next_ypos) { return item; } - bp->y = next_ypos; + *ypos = next_ypos; if (item->state & TTK_STATE_OPEN) { - TreeItem *subitem = IdentifyRow(tv, item->children, bp, y); + TreeItem *subitem = IdentifyRow(tv, item->children, ypos, y); if (subitem) { - bp->x += tv->tree.indent; - bp->width -= tv->tree.indent; return subitem; } } @@ -1325,17 +1341,12 @@ static TreeItem *IdentifyRow( /* + IdentifyItem -- * Locate the item at the specified y position, if any. - * On return, *itemPos holds the parcel of the tree item. */ -static TreeItem *IdentifyItem(Treeview *tv, int y, Ttk_Box *itemPos) +static TreeItem *IdentifyItem(Treeview *tv, int y) { int rowHeight = tv->tree.rowHeight; - *itemPos = Ttk_MakeBox( - tv->tree.treeArea.x, - tv->tree.treeArea.y - tv->tree.yscroll.first * rowHeight, - tv->tree.column0.width, - rowHeight); - return IdentifyRow(tv, tv->tree.root->children, itemPos, y); + int ypos = tv->tree.treeArea.y - rowHeight * tv->tree.yscroll.first; + return IdentifyRow(tv, tv->tree.root->children, &ypos, y); } /* + IdentifyDisplayColumn -- @@ -1345,7 +1356,7 @@ static TreeItem *IdentifyItem(Treeview *tv, int y, Ttk_Box *itemPos) static int IdentifyDisplayColumn(Treeview *tv, int x, int *x1) { int colno = FirstColumn(tv); - int xpos = tv->tree.treeArea.x; + int xpos = tv->tree.treeArea.x - tv->tree.xscroll.first; while (colno < tv->tree.nDisplayColumns) { TreeColumn *column = tv->tree.displayColumns[colno]; @@ -1361,6 +1372,51 @@ static int IdentifyDisplayColumn(Treeview *tv, int x, int *x1) return -1; } +/* + RowNumber -- + * Calculate which row the specified item appears on; + * returns -1 if the item is not viewable. + * Xref: DrawForest, IdentifyItem. + */ +static int RowNumber(Treeview *tv, TreeItem *item) +{ + TreeItem *p = tv->tree.root->children; + int n = 0; + + while (p) { + if (p == item) + return n; + + ++n; + + /* Find next viewable item in preorder traversal order + */ + if (p->children && (p->state & TTK_STATE_OPEN)) { + p = p->children; + } else { + while (!p->next && p && p->parent) + p = p->parent; + if (p) + p = p->next; + } + } + + return -1; +} + +/* + ItemDepth -- return the depth of a tree item. + * The depth of an item is equal to the number of proper ancestors, + * not counting the root node. + */ +static int ItemDepth(TreeItem *item) +{ + int depth = 0; + while (item->parent) { + ++depth; + item = item->parent; + } + return depth-1; +} + /* + ItemRow -- * Returns row number of specified item relative to root, * -1 if item is not viewable. @@ -1388,6 +1444,97 @@ static int ItemRow(Treeview *tv, TreeItem *p) } } +/* + BoundingBox -- + * Compute the parcel of the specified column of the specified item, + * (or the entire item if column is NULL) + * Returns: 0 if item or column is not viewable, 1 otherwise. + */ +static int BoundingBox( + Treeview *tv, /* treeview widget */ + TreeItem *item, /* desired item */ + TreeColumn *column, /* desired column */ + Ttk_Box *bbox_rtn) /* bounding box of item */ +{ + int row = ItemRow(tv, item); + Ttk_Box bbox = tv->tree.treeArea; + + if (row < tv->tree.yscroll.first || row > tv->tree.yscroll.last) { + /* not viewable, or off-screen */ + return 0; + } + + bbox.y += (row - tv->tree.yscroll.first) * tv->tree.rowHeight; + bbox.height = tv->tree.rowHeight; + + bbox.x -= tv->tree.xscroll.first; + bbox.width = TreeWidth(tv); + + if (column) { + int xpos = 0, i = FirstColumn(tv); + while (i < tv->tree.nDisplayColumns) { + if (tv->tree.displayColumns[i] == column) { + break; + } + xpos += tv->tree.displayColumns[i]->width; + ++i; + } + if (i == tv->tree.nDisplayColumns) { /* specified column unviewable */ + return 0; + } + bbox.x += xpos; + bbox.width = column->width; + + /* Account for indentation in tree column: + */ + if (column == &tv->tree.column0) { + int indent = tv->tree.indent * ItemDepth(item); + bbox.x += indent; + bbox.width -= indent; + } + } + *bbox_rtn = bbox; + return 1; +} + +/* + IdentifyRegion -- + */ + +typedef enum { + REGION_NOTHING = 0, + REGION_HEADING, + REGION_SEPARATOR, + REGION_TREE, + REGION_CELL +} TreeRegion; + +static const char *regionStrings[] = { + "nothing", "heading", "separator", "tree", "cell", 0 +}; + +static TreeRegion IdentifyRegion(Treeview *tv, int x, int y) +{ + int x1 = 0, colno; + + colno = IdentifyDisplayColumn(tv, x, &x1); + if (Ttk_BoxContains(tv->tree.headingArea, x, y)) { + if (colno < 0) { + return REGION_NOTHING; + } else if (-HALO <= x1 - x && x1 - x <= HALO) { + return REGION_SEPARATOR; + } else { + return REGION_HEADING; + } + } else if (Ttk_BoxContains(tv->tree.treeArea, x, y)) { + TreeItem *item = IdentifyItem(tv, y); + if (item && colno > 0) { + return REGION_CELL; + } else if (item) { + return REGION_TREE; + } + } + return REGION_NOTHING; +} + /*------------------------------------------------------------------------ * +++ Display routines. */ @@ -1430,11 +1577,11 @@ static Ttk_Layout TreeviewGetLayout( && GetSublayout(interp, themePtr, treeLayout, ".Item", tv->tree.tagOptionTable, &tv->tree.itemLayout) && GetSublayout(interp, themePtr, treeLayout, ".Cell", - tv->tree.tagOptionTable, &tv->tree.cellLayout) /*@@@HERE*/ + tv->tree.tagOptionTable, &tv->tree.cellLayout) && GetSublayout(interp, themePtr, treeLayout, ".Heading", tv->tree.headingOptionTable, &tv->tree.headingLayout) && GetSublayout(interp, themePtr, treeLayout, ".Row", - tv->tree.tagOptionTable, &tv->tree.rowLayout) /*@@@HERE*/ + tv->tree.tagOptionTable, &tv->tree.rowLayout) )) { return 0; } @@ -1470,15 +1617,12 @@ static Ttk_Layout TreeviewGetLayout( static void TreeviewDoLayout(void *clientData) { Treeview *tv = clientData; - Ttk_LayoutNode *clientNode = Ttk_LayoutFindNode(tv->core.layout,"treearea"); int visibleRows; /* ASSERT: SLACKINVARIANT */ Ttk_PlaceLayout(tv->core.layout,tv->core.state,Ttk_WinBox(tv->core.tkwin)); - tv->tree.treeArea = clientNode - ? Ttk_LayoutNodeInternalParcel(tv->core.layout,clientNode) - : Ttk_WinBox(tv->core.tkwin) ; + tv->tree.treeArea = Ttk_ClientRegion(tv->core.layout, "treearea"); ResizeColumns(tv, tv->tree.treeArea.width); /* ASSERT: SLACKINVARIANT */ @@ -1488,7 +1632,6 @@ static void TreeviewDoLayout(void *clientData) tv->tree.xscroll.first + tv->tree.treeArea.width, TreeWidth(tv)); - tv->tree.treeArea.x -= tv->tree.xscroll.first; if (tv->tree.showFlags & SHOW_HEADINGS) { tv->tree.headingArea = Ttk_PackBox( &tv->tree.treeArea, 1, tv->tree.headingHeight, TTK_SIDE_TOP); @@ -1502,7 +1645,6 @@ static void TreeviewDoLayout(void *clientData) tv->tree.yscroll.first, tv->tree.yscroll.first + visibleRows, CountRows(tv->tree.root) - 1); - } /* + TreeviewSize -- @@ -1544,14 +1686,17 @@ static Ttk_State ItemState(Treeview *tv, TreeItem *item) /* + DrawHeadings -- * Draw tree headings. */ -static void DrawHeadings(Treeview *tv, Drawable d, Ttk_Box b) +static void DrawHeadings(Treeview *tv, Drawable d) { + const int x0 = tv->tree.headingArea.x - tv->tree.xscroll.first; + const int y0 = tv->tree.headingArea.y; + const int h0 = tv->tree.headingArea.height; int i = FirstColumn(tv); int x = 0; while (i < tv->tree.nDisplayColumns) { TreeColumn *column = tv->tree.displayColumns[i]; - Ttk_Box parcel = Ttk_MakeBox(b.x+x, b.y, column->width, b.height); + Ttk_Box parcel = Ttk_MakeBox(x0+x, y0, column->width, h0); DisplayLayout(tv->tree.headingLayout, column, column->headingState, parcel, d); x += column->width; @@ -1560,34 +1705,16 @@ static void DrawHeadings(Treeview *tv, Drawable d, Ttk_Box b) } /* + PrepareItem -- - * Fill in a displayItem record from tag settings. + * Fill in a displayItem record. */ -static void PrepareItem(Treeview *tv, TreeItem *item, DisplayItem *displayItem) +static void PrepareItem( + Treeview *tv, TreeItem *item, DisplayItem *displayItem) { - const int nOptions = sizeof(*displayItem)/sizeof(Tcl_Obj*); - Tcl_Obj **dest = (Tcl_Obj**)displayItem; - Tcl_Obj **objv = NULL; - int objc = 0; - - memset(displayItem, 0, sizeof(*displayItem)); + Ttk_Style style = Ttk_LayoutStyle(tv->core.layout); + Ttk_State state = ItemState(tv, item); - if ( item->tagsObj - && Tcl_ListObjGetElements(NULL, item->tagsObj, &objc, &objv) == TCL_OK) - { - int i, j; - for (i=0; i<objc; ++i) { - Ttk_Tag tag = Ttk_GetTagFromObj(tv->tree.tagTable, objv[i]); - Tcl_Obj **tagRecord = Ttk_TagRecord(tag); - - if (tagRecord) { - for (j=0; j<nOptions; ++j) { - if (tagRecord[j] != 0) { - dest[j] = tagRecord[j]; - } - } - } - } - } + Ttk_TagSetValues(tv->tree.tagTable, item->tagset, displayItem); + Ttk_TagSetApplyStyle(tv->tree.tagTable, style, state, displayItem); } /* + DrawCells -- @@ -1595,7 +1722,7 @@ static void PrepareItem(Treeview *tv, TreeItem *item, DisplayItem *displayItem) */ static void DrawCells( Treeview *tv, TreeItem *item, DisplayItem *displayItem, - Drawable d, Ttk_Box b, int x, int y) + Drawable d, int x, int y) { Ttk_Layout layout = tv->tree.cellLayout; Ttk_State state = ItemState(tv, item); @@ -1617,10 +1744,10 @@ static void DrawCells( for (i = 1; i < tv->tree.nDisplayColumns; ++i) { TreeColumn *column = tv->tree.displayColumns[i]; Ttk_Box parcel = Ttk_PadBox( - Ttk_MakeBox(b.x+x, b.y+y, column->width, rowHeight), cellPadding); + Ttk_MakeBox(x, y, column->width, rowHeight), cellPadding); displayItem->textObj = column->data; - displayItem->anchorObj = column->anchorObj; + displayItem->anchorObj = column->anchorObj; /* <<NOTE-ANCHOR>> */ DisplayLayout(layout, displayItem, state, parcel, d); x += column->width; @@ -1631,13 +1758,13 @@ static void DrawCells( * Draw an item (row background, tree label, and cells). */ static void DrawItem( - Treeview *tv, TreeItem *item, Drawable d, Ttk_Box b, int depth, int row) + Treeview *tv, TreeItem *item, Drawable d, int depth, int row) { Ttk_State state = ItemState(tv, item); DisplayItem displayItem; int rowHeight = tv->tree.rowHeight; - int x = depth * tv->tree.indent; - int y = (row - tv->tree.yscroll.first) * tv->tree.rowHeight; + int x = tv->tree.treeArea.x - tv->tree.xscroll.first; + int y = tv->tree.treeArea.y + rowHeight * (row - tv->tree.yscroll.first); if (row % 2) state |= TTK_STATE_ALTERNATE; @@ -1646,27 +1773,27 @@ static void DrawItem( /* Draw row background: */ { - Ttk_Box rowBox = Ttk_MakeBox(b.x, b.y+y, TreeWidth(tv), rowHeight); + Ttk_Box rowBox = Ttk_MakeBox(x, y, TreeWidth(tv), rowHeight); DisplayLayout(tv->tree.rowLayout, &displayItem, state, rowBox, d); } /* Draw tree label: */ if (tv->tree.showFlags & SHOW_TREE) { + int indent = depth * tv->tree.indent; int colwidth = tv->tree.column0.width; - Ttk_Box parcel = Ttk_MakeBox(b.x + x, b.y + y, colwidth - x, rowHeight); - displayItem.textObj = item->textObj; - displayItem.imageObj = item->imageObj; - displayItem.anchorObj = 0; + Ttk_Box parcel = Ttk_MakeBox( + x+indent, y, colwidth-indent, rowHeight); + if (item->textObj) { displayItem.textObj = item->textObj; } + if (item->imageObj) { displayItem.imageObj = item->imageObj; } + /* ??? displayItem.anchorObj = 0; <<NOTE-ANCHOR>> */ DisplayLayout(tv->tree.itemLayout, &displayItem, state, parcel, d); - x = colwidth; - } else { - x = 0; + x += colwidth; } /* Draw data cells: */ - DrawCells(tv, item, &displayItem, d, b, x, y); + DrawCells(tv, item, &displayItem, d, x, y); } /* + DrawSubtree -- @@ -1677,17 +1804,17 @@ static void DrawItem( */ static int DrawForest( /* forward */ - Treeview *tv, TreeItem *item, Drawable d, Ttk_Box b, int depth, int row); + Treeview *tv, TreeItem *item, Drawable d, int depth, int row); static int DrawSubtree( - Treeview *tv, TreeItem *item, Drawable d, Ttk_Box b, int depth, int row) + Treeview *tv, TreeItem *item, Drawable d, int depth, int row) { if (row >= tv->tree.yscroll.first) { - DrawItem(tv, item, d, b, depth, row); + DrawItem(tv, item, d, depth, row); } if (item->state & TTK_STATE_OPEN) { - return DrawForest(tv, item->children, d, b, depth + 1, row + 1); + return DrawForest(tv, item->children, d, depth + 1, row + 1); } else { return row + 1; } @@ -1700,10 +1827,10 @@ static int DrawSubtree( * Row number of the last item drawn. */ static int DrawForest( - Treeview *tv, TreeItem *item, Drawable d, Ttk_Box b, int depth, int row) + Treeview *tv, TreeItem *item, Drawable d, int depth, int row) { while (item && row <= tv->tree.yscroll.last) { - row = DrawSubtree(tv, item, d, b, depth, row); + row = DrawSubtree(tv, item, d, depth, row); item = item->next; } return row; @@ -1718,9 +1845,9 @@ static void TreeviewDisplay(void *clientData, Drawable d) Ttk_DrawLayout(tv->core.layout, tv->core.state, d); if (tv->tree.showFlags & SHOW_HEADINGS) { - DrawHeadings(tv, d, tv->tree.headingArea); + DrawHeadings(tv, d); } - DrawForest(tv, tv->tree.root->children, d, tv->tree.treeArea, 0,0); + DrawForest(tv, tv->tree.root->children, d, 0,0); } /*------------------------------------------------------------------------ @@ -1748,16 +1875,33 @@ static TreeItem *InsertPosition(TreeItem *parent, int index) /* + EndPosition -- * Locate the last child of the specified node. + * + * To avoid quadratic-time behavior in the common cases + * where the treeview is populated in breadth-first or + * depth-first order using [$tv insert $parent end ...], + * we cache the result from the last call to EndPosition() + * and start the search from there on a cache hit. + * */ -static TreeItem *EndPosition(TreeItem *parent) +static TreeItem *EndPosition(Treeview *tv, TreeItem *parent) { - TreeItem *sibling = parent->children; - if (sibling) { - while (sibling->next) { - sibling = sibling->next; + TreeItem *endPtr = tv->tree.endPtr; + + while (endPtr && endPtr->parent != parent) { + endPtr = endPtr->parent; + } + if (!endPtr) { + endPtr = parent->children; + } + + if (endPtr) { + while (endPtr->next) { + endPtr = endPtr->next; } + tv->tree.endPtr = endPtr; } - return sibling; + + return endPtr; } /* + AncestryCheck -- @@ -1802,51 +1946,6 @@ static TreeItem *DeleteItems(TreeItem *item, TreeItem *delq) return delq; } -/* + RowNumber -- - * Calculate which row the specified item appears on; - * returns -1 if the item is not viewable. - * Xref: DrawForest, IdentifyItem. - */ -static int RowNumber(Treeview *tv, TreeItem *item) -{ - TreeItem *p = tv->tree.root->children; - int n = 0; - - while (p) { - if (p == item) - return n; - - ++n; - - /* Find next viewable item in preorder traversal order - */ - if (p->children && (p->state & TTK_STATE_OPEN)) { - p = p->children; - } else { - while (!p->next && p && p->parent) - p = p->parent; - if (p) - p = p->next; - } - } - - return -1; -} - -/* + ItemDepth -- return the depth of a tree item. - * The depth of an item is equal to the number of proper ancestors, - * not counting the root node. - */ -static int ItemDepth(TreeItem *item) -{ - int depth = 0; - while (item->parent) { - ++depth; - item = item->parent; - } - return depth-1; -} - /*------------------------------------------------------------------------ * +++ Widget commands -- item inquiry. */ @@ -1855,7 +1954,7 @@ static int ItemDepth(TreeItem *item) * Return the list of children associated with $item */ static int TreeviewChildrenCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; TreeItem *item; @@ -1935,7 +2034,7 @@ static int TreeviewChildrenCommand( * Return the item ID of $item's parent. */ static int TreeviewParentCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; TreeItem *item; @@ -1963,7 +2062,7 @@ static int TreeviewParentCommand( * Return the ID of $item's next sibling. */ static int TreeviewNextCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; TreeItem *item; @@ -1988,7 +2087,7 @@ static int TreeviewNextCommand( * Return the ID of $item's previous sibling. */ static int TreeviewPrevCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; TreeItem *item; @@ -2013,7 +2112,7 @@ static int TreeviewPrevCommand( * Return the index of $item within its parent. */ static int TreeviewIndexCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; TreeItem *item; @@ -2041,7 +2140,7 @@ static int TreeviewIndexCommand( * Test if the specified item id is present in the tree. */ static int TreeviewExistsCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; Tcl_HashEntry *entryPtr; @@ -2060,12 +2159,11 @@ static int TreeviewExistsCommand( * Return bounding box [x y width height] of specified item. */ static int TreeviewBBoxCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; TreeItem *item = 0; TreeColumn *column = 0; - int row; Ttk_Box bbox; if (objc < 3 || objc > 4) { @@ -2081,47 +2179,10 @@ static int TreeviewBBoxCommand( return TCL_ERROR; } - /* Compute bounding box of item: - */ - row = ItemRow(tv, item); - if (row < tv->tree.yscroll.first || row > tv->tree.yscroll.last) { - /* not viewable, or off-screen */ - return TCL_OK; - } - - bbox = tv->tree.treeArea; - bbox.y += (row - tv->tree.yscroll.first) * tv->tree.rowHeight; - bbox.height = tv->tree.rowHeight; - - /* If column has been specified, compute bounding box of cell - */ - if (column) { - int xpos = 0, i = FirstColumn(tv); - while (i < tv->tree.nDisplayColumns) { - if (tv->tree.displayColumns[i] == column) { - break; - } - xpos += tv->tree.displayColumns[i]->width; - ++i; - } - if (i == tv->tree.nDisplayColumns) { /* specified column unviewable */ - return TCL_OK; - } - bbox.x += xpos; - bbox.width = column->width; - - /* Special case for tree column -- account for indentation: - * (@@@ NOTE: doesn't account for tree indicator or image; - * @@@ this may or may not be the right thing.) - */ - if (column == &tv->tree.column0) { - int indent = tv->tree.indent * ItemDepth(item); - bbox.x += indent; - bbox.width -= indent; - } + if (BoundingBox(tv, item, column, &bbox)) { + Tcl_SetObjResult(interp, Ttk_NewBoxObj(bbox)); } - Tcl_SetObjResult(interp, Ttk_NewBoxObj(bbox)); return TCL_OK; } @@ -2166,24 +2227,25 @@ static int TreeviewHorribleIdentify( } detail = dcolbuf; } else if (Ttk_BoxContains(tv->tree.treeArea,x,y)) { - Ttk_Box itemBox; - item = IdentifyItem(tv, y, &itemBox); + item = IdentifyItem(tv, y); if (item && dColumnNumber > 0) { what = "cell"; detail = dcolbuf; } else if (item) { Ttk_Layout layout = tv->tree.itemLayout; + Ttk_Box itemBox; DisplayItem displayItem; - Ttk_LayoutNode *element; + Ttk_Element element; + BoundingBox(tv, item, NULL, &itemBox); PrepareItem(tv, item, &displayItem); /*@@@ FIX: -text, etc*/ Ttk_RebindSublayout(layout, &displayItem); Ttk_PlaceLayout(layout, ItemState(tv,item), itemBox); - element = Ttk_LayoutIdentify(layout, x, y); + element = Ttk_IdentifyElement(layout, x, y); if (element) { what = "item"; - detail = Ttk_LayoutNodeName(element); + detail = Ttk_ElementName(element); } else { what = "row"; } @@ -2207,53 +2269,99 @@ done: */ static int TreeviewIdentifyCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - static const char *componentStrings[] = - { "row", "column", NULL }; - enum { I_ROW, I_COLUMN }; + static const char *submethodStrings[] = + { "region", "item", "column", "row", "element", NULL }; + enum { I_REGION, I_ITEM, I_COLUMN, I_ROW, I_ELEMENT }; Treeview *tv = recordPtr; - int component, x, y; + int submethod; + int x, y; + + TreeRegion region; + Ttk_Box bbox; + TreeItem *item; + TreeColumn *column = 0; + int colno, x1; if (objc == 4) { /* Old form */ return TreeviewHorribleIdentify(interp, objc, objv, tv); } else if (objc != 5) { - Tcl_WrongNumArgs(interp, 2, objv, "component x y"); + Tcl_WrongNumArgs(interp, 2, objv, "command x y"); return TCL_ERROR; } if ( Tcl_GetIndexFromObj(interp, objv[2], - componentStrings, "component", TCL_EXACT, &component) != TCL_OK + submethodStrings, "command", TCL_EXACT, &submethod) != TCL_OK || Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK || Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK ) { return TCL_ERROR; } - switch (component) + region = IdentifyRegion(tv, x, y); + item = IdentifyItem(tv, y); + colno = IdentifyDisplayColumn(tv, x, &x1); + column = (colno >= 0) ? tv->tree.displayColumns[colno] : NULL; + + switch (submethod) { + case I_REGION : + Tcl_SetObjResult(interp,Tcl_NewStringObj(regionStrings[region],-1)); + break; + + case I_ITEM : case I_ROW : - { - Ttk_Box itemBox; - TreeItem *item = IdentifyItem(tv, y, &itemBox); if (item) { Tcl_SetObjResult(interp, ItemID(tv, item)); } break; - } case I_COLUMN : - { - int x1; - int column = IdentifyDisplayColumn(tv, x, &x1); - - if (column >= 0) { + if (colno >= 0) { char dcolbuf[16]; - sprintf(dcolbuf, "#%d", column); + sprintf(dcolbuf, "#%d", colno); Tcl_SetObjResult(interp, Tcl_NewStringObj(dcolbuf, -1)); } break; + + case I_ELEMENT : + { + Ttk_Layout layout = 0; + DisplayItem displayItem; + Ttk_Element element; + + switch (region) { + case REGION_NOTHING: + layout = tv->core.layout; + return TCL_OK; /* @@@ NYI */ + case REGION_HEADING: + case REGION_SEPARATOR: + layout = tv->tree.headingLayout; + return TCL_OK; /* @@@ NYI */ + case REGION_TREE: + layout = tv->tree.itemLayout; + break; + case REGION_CELL: + layout = tv->tree.cellLayout; + break; + } + + if (!BoundingBox(tv, item, column, &bbox)) { + return TCL_OK; + } + + PrepareItem(tv, item, &displayItem); /*@@@ FIX: fill in -text,etc */ + Ttk_RebindSublayout(layout, &displayItem); + Ttk_PlaceLayout(layout, ItemState(tv,item), bbox); + element = Ttk_IdentifyElement(layout, x, y); + + if (element) { + const char *elementName = Ttk_ElementName(element); + Tcl_SetObjResult(interp, Tcl_NewStringObj(elementName, -1)); + } + break; } } return TCL_OK; @@ -2267,7 +2375,7 @@ static int TreeviewIdentifyCommand( * Query or configure item options. */ static int TreeviewItemCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; TreeItem *item; @@ -2295,7 +2403,7 @@ static int TreeviewItemCommand( * Column data accessor */ static int TreeviewColumnCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; TreeColumn *column; @@ -2323,7 +2431,7 @@ static int TreeviewColumnCommand( * Heading data accessor */ static int TreeviewHeadingCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; Tk_OptionTable optionTable = tv->tree.headingOptionTable; @@ -2353,7 +2461,7 @@ static int TreeviewHeadingCommand( * Query or configure cell values */ static int TreeviewSetCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; TreeItem *item; @@ -2441,11 +2549,11 @@ static int TreeviewSetCommand( * +++ Widget commands -- tree modification. */ -/* + $tv insert $parent $index ?-id id? ?-option value...? +/* + $tv insert $parent $index ?-id id? ?-option value ...? * Insert a new item. */ static int TreeviewInsertCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; TreeItem *parent, *sibling, *newItem; @@ -2466,7 +2574,7 @@ static int TreeviewInsertCommand( /* Locate previous sibling based on $index: */ if (!strcmp(Tcl_GetString(objv[3]), "end")) { - sibling = EndPosition(parent); + sibling = EndPosition(tv, parent); } else { int index; if (Tcl_GetIntFromObj(interp, objv[3], &index) != TCL_OK) @@ -2501,6 +2609,7 @@ static int TreeviewInsertCommand( newItem = NewItem(); Tk_InitOptions( interp, (ClientData)newItem, tv->tree.itemOptionTable, tv->core.tkwin); + newItem->tagset = Ttk_GetTagSetFromObj(NULL, tv->tree.tagTable, NULL); if (ConfigureItem(interp, tv, newItem, objc, objv) != TCL_OK) { Tcl_DeleteHashEntry(entryPtr); FreeItem(newItem); @@ -2522,7 +2631,7 @@ static int TreeviewInsertCommand( * Unlink $item from the tree. */ static int TreeviewDetachCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; TreeItem **items; @@ -2567,7 +2676,7 @@ static int TreeviewDetachCommand( */ static int TreeviewDeleteCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; TreeItem **items, *delq; @@ -2605,6 +2714,8 @@ static int TreeviewDeleteCommand( TreeItem *next = delq->next; if (tv->tree.focus == delq) tv->tree.focus = 0; + if (tv->tree.endPtr == delq) + tv->tree.endPtr = 0; FreeItem(delq); delq = next; } @@ -2618,7 +2729,7 @@ static int TreeviewDeleteCommand( * Move $item to the specified $index in $parent's child list. */ static int TreeviewMoveCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; TreeItem *item, *parent; @@ -2637,7 +2748,7 @@ static int TreeviewMoveCommand( /* Locate previous sibling based on $index: */ if (!strcmp(Tcl_GetString(objv[4]), "end")) { - sibling = EndPosition(parent); + sibling = EndPosition(tv, parent); } else { TreeItem *p; int index; @@ -2651,7 +2762,7 @@ static int TreeviewMoveCommand( if (p != item) { --index; } /* else -- moving node forward, count index+1 nodes */ - sibling = p; + sibling = p; } } @@ -2681,14 +2792,14 @@ static int TreeviewMoveCommand( */ static int TreeviewXViewCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; return TtkScrollviewCommand(interp, objc, objv, tv->tree.xscrollHandle); } static int TreeviewYViewCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; return TtkScrollviewCommand(interp, objc, objv, tv->tree.yscrollHandle); @@ -2698,7 +2809,7 @@ static int TreeviewYViewCommand( * Ensure that $item is visible. */ static int TreeviewSeeCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; TreeItem *item, *parent; @@ -2744,10 +2855,10 @@ static int TreeviewSeeCommand( * Set right edge of display column $column to x position $X */ static int TreeviewDragCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; - int left = tv->tree.treeArea.x; + int left = tv->tree.treeArea.x - tv->tree.xscroll.first; int i = FirstColumn(tv); TreeColumn *column; int newx; @@ -2789,7 +2900,7 @@ static int TreeviewDragCommand( /* + $tree focus ?item? */ static int TreeviewFocusCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; @@ -2814,7 +2925,7 @@ static int TreeviewFocusCommand( /* + $tree selection ?add|remove|set|toggle $items? */ static int TreeviewSelectionCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { enum { SELECTION_SET, SELECTION_ADD, SELECTION_REMOVE, SELECTION_TOGGLE @@ -2891,7 +3002,7 @@ static int TreeviewSelectionCommand( /* + $tv tag bind $tag ?$sequence ?$script?? */ static int TreeviewTagBindCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; Ttk_TagTable tagTable = tv->tree.tagTable; @@ -2944,10 +3055,10 @@ static int TreeviewTagBindCommand( /* + $tv tag configure $tag ?-option ?value -option value...?? */ static int TreeviewTagConfigureCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Treeview *tv = recordPtr; - void *tagRecord; + Ttk_TagTable tagTable = tv->tree.tagTable; Ttk_Tag tag; if (objc < 4) { @@ -2955,79 +3066,205 @@ static int TreeviewTagConfigureCommand( return TCL_ERROR; } - tag = Ttk_GetTagFromObj(tv->tree.tagTable, objv[3]); - tagRecord = Ttk_TagRecord(tag); + tag = Ttk_GetTagFromObj(tagTable, objv[3]); if (objc == 4) { - return TtkEnumerateOptions(interp, tagRecord, TagOptionSpecs, - tv->tree.tagOptionTable, tv->core.tkwin); + return Ttk_EnumerateTagOptions(interp, tagTable, tag); } else if (objc == 5) { - return TtkGetOptionValue(interp, tagRecord, objv[4], - tv->tree.tagOptionTable, tv->core.tkwin); + Tcl_Obj *result = Ttk_TagOptionValue(interp, tagTable, tag, objv[4]); + if (result) { + Tcl_SetObjResult(interp, result); + return TCL_OK; + } /* else */ + return TCL_ERROR; } /* else */ TtkRedisplayWidget(&tv->core); - return Tk_SetOptions( - interp, tagRecord, tv->tree.tagOptionTable, - objc - 4, objv + 4, tv->core.tkwin, - NULL/*savedOptions*/, NULL/*mask*/); + return Ttk_ConfigureTag(interp, tagTable, tag, objc - 4, objv + 4); } -/* + $tv tag option args... +/* + $tv tag has $tag ?$item? */ -static int TreeviewTagCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) +static int TreeviewTagHasCommand( + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - static WidgetCommandSpec TreeviewTagCommands[] = { - { "bind", TreeviewTagBindCommand }, - { "configure", TreeviewTagConfigureCommand }, - {0,0} - }; - return TtkWidgetEnsembleCommand( - TreeviewTagCommands, 2, interp, objc, objv, recordPtr); + Treeview *tv = recordPtr; + + if (objc == 4) { /* Return list of all items with tag */ + Ttk_Tag tag = Ttk_GetTagFromObj(tv->tree.tagTable, objv[3]); + TreeItem *item = tv->tree.root; + Tcl_Obj *result = Tcl_NewListObj(0,0); + + while (item) { + if (Ttk_TagSetContains(item->tagset, tag)) { + Tcl_ListObjAppendElement(NULL, result, ItemID(tv, item)); + } + item = NextPreorder(item); + } + + Tcl_SetObjResult(interp, result); + return TCL_OK; + } else if (objc == 5) { /* Test if item has specified tag */ + Ttk_Tag tag = Ttk_GetTagFromObj(tv->tree.tagTable, objv[3]); + TreeItem *item = FindItem(interp, tv, objv[4]); + if (!item) { + return TCL_ERROR; + } + Tcl_SetObjResult(interp, + Tcl_NewBooleanObj(Ttk_TagSetContains(item->tagset, tag))); + return TCL_OK; + } else { + Tcl_WrongNumArgs(interp, 3, objv, "tagName ?item?"); + return TCL_ERROR; + } } +/* + $tv tag names $tag + */ +static int TreeviewTagNamesCommand( + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) +{ + Treeview *tv = recordPtr; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 3, objv, ""); + return TCL_ERROR; + } + + return Ttk_EnumerateTags(interp, tv->tree.tagTable); +} + +/* + $tv tag add $tag $items + */ +static void AddTag(TreeItem *item, Ttk_Tag tag) +{ + if (Ttk_TagSetAdd(item->tagset, tag)) { + Tcl_DecrRefCount(item->tagsObj); + item->tagsObj = Ttk_NewTagSetObj(item->tagset); + Tcl_IncrRefCount(item->tagsObj); + } +} + +static int TreeviewTagAddCommand( + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) +{ + Treeview *tv = recordPtr; + Ttk_Tag tag; + TreeItem **items; + int i; + + if (objc != 5) { + Tcl_WrongNumArgs(interp, 3, objv, "tagName items"); + return TCL_ERROR; + } + + tag = Ttk_GetTagFromObj(tv->tree.tagTable, objv[3]); + items = GetItemListFromObj(interp, tv, objv[4]); + + if (!items) { + return TCL_ERROR; + } + + for (i=0; items[i]; ++i) { + AddTag(items[i], tag); + } + + return TCL_OK; +} + +/* + $tv tag remove $tag $items + */ +static void RemoveTag(TreeItem *item, Ttk_Tag tag) +{ + if (Ttk_TagSetRemove(item->tagset, tag)) { + Tcl_DecrRefCount(item->tagsObj); + item->tagsObj = Ttk_NewTagSetObj(item->tagset); + Tcl_IncrRefCount(item->tagsObj); + } +} + +static int TreeviewTagRemoveCommand( + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) +{ + Treeview *tv = recordPtr; + Ttk_Tag tag; + + if (objc < 4) { + Tcl_WrongNumArgs(interp, 3, objv, "tagName items"); + return TCL_ERROR; + } + + tag = Ttk_GetTagFromObj(tv->tree.tagTable, objv[3]); + + if (objc == 5) { + TreeItem **items = GetItemListFromObj(interp, tv, objv[4]); + int i; + + if (!items) { + return TCL_ERROR; + } + for (i=0; items[i]; ++i) { + RemoveTag(items[i], tag); + } + } else if (objc == 4) { + TreeItem *item = tv->tree.root; + while (item) { + RemoveTag(item, tag); + item=NextPreorder(item); + } + } + return TCL_OK; +} + +static const Ttk_Ensemble TreeviewTagCommands[] = { + { "add", TreeviewTagAddCommand,0 }, + { "bind", TreeviewTagBindCommand,0 }, + { "configure", TreeviewTagConfigureCommand,0 }, + { "has", TreeviewTagHasCommand,0 }, + { "names", TreeviewTagNamesCommand,0 }, + { "remove", TreeviewTagRemoveCommand,0 }, + { 0,0,0 } +}; + /*------------------------------------------------------------------------ * +++ Widget commands record. */ -static WidgetCommandSpec TreeviewCommands[] = -{ - { "bbox", TreeviewBBoxCommand }, - { "children", TreeviewChildrenCommand }, - { "cget", TtkWidgetCgetCommand }, - { "column", TreeviewColumnCommand }, - { "configure", TtkWidgetConfigureCommand }, - { "delete", TreeviewDeleteCommand }, - { "detach", TreeviewDetachCommand }, - { "drag", TreeviewDragCommand }, - { "exists", TreeviewExistsCommand }, - { "focus", TreeviewFocusCommand }, - { "heading", TreeviewHeadingCommand }, - { "identify", TreeviewIdentifyCommand }, - { "index", TreeviewIndexCommand }, - { "instate", TtkWidgetInstateCommand }, - { "insert", TreeviewInsertCommand }, - { "item", TreeviewItemCommand }, - { "move", TreeviewMoveCommand }, - { "next", TreeviewNextCommand }, - { "parent", TreeviewParentCommand }, - { "prev", TreeviewPrevCommand }, - { "see", TreeviewSeeCommand }, - { "selection" , TreeviewSelectionCommand }, - { "set", TreeviewSetCommand }, - { "state", TtkWidgetStateCommand }, - { "tag", TreeviewTagCommand }, - { "xview", TreeviewXViewCommand }, - { "yview", TreeviewYViewCommand }, - { NULL, NULL } +static const Ttk_Ensemble TreeviewCommands[] = { + { "bbox", TreeviewBBoxCommand,0 }, + { "children", TreeviewChildrenCommand,0 }, + { "cget", TtkWidgetCgetCommand,0 }, + { "column", TreeviewColumnCommand,0 }, + { "configure", TtkWidgetConfigureCommand,0 }, + { "delete", TreeviewDeleteCommand,0 }, + { "detach", TreeviewDetachCommand,0 }, + { "drag", TreeviewDragCommand,0 }, + { "exists", TreeviewExistsCommand,0 }, + { "focus", TreeviewFocusCommand,0 }, + { "heading", TreeviewHeadingCommand,0 }, + { "identify", TreeviewIdentifyCommand,0 }, + { "index", TreeviewIndexCommand,0 }, + { "instate", TtkWidgetInstateCommand,0 }, + { "insert", TreeviewInsertCommand,0 }, + { "item", TreeviewItemCommand,0 }, + { "move", TreeviewMoveCommand,0 }, + { "next", TreeviewNextCommand,0 }, + { "parent", TreeviewParentCommand,0 }, + { "prev", TreeviewPrevCommand,0 }, + { "see", TreeviewSeeCommand,0 }, + { "selection" , TreeviewSelectionCommand,0 }, + { "set", TreeviewSetCommand,0 }, + { "state", TtkWidgetStateCommand,0 }, + { "tag", 0,TreeviewTagCommands }, + { "xview", TreeviewXViewCommand,0 }, + { "yview", TreeviewYViewCommand,0 }, + { 0,0,0 } }; /*------------------------------------------------------------------------ * +++ Widget definition. */ -static WidgetSpec TreeviewWidgetSpec = -{ +static WidgetSpec TreeviewWidgetSpec = { "Treeview", /* className */ sizeof(Treeview), /* recordSize */ TreeviewOptionSpecs, /* optionSpecs */ @@ -3080,22 +3317,20 @@ TTK_END_LAYOUT_TABLE * +++ Tree indicator element. */ -typedef struct -{ +typedef struct { Tcl_Obj *colorObj; Tcl_Obj *sizeObj; Tcl_Obj *marginsObj; } TreeitemIndicator; -static Ttk_ElementOptionSpec TreeitemIndicatorOptions[] = -{ +static Ttk_ElementOptionSpec TreeitemIndicatorOptions[] = { { "-foreground", TK_OPTION_COLOR, Tk_Offset(TreeitemIndicator,colorObj), DEFAULT_FOREGROUND }, { "-indicatorsize", TK_OPTION_PIXELS, Tk_Offset(TreeitemIndicator,sizeObj), "12" }, { "-indicatormargins", TK_OPTION_STRING, Tk_Offset(TreeitemIndicator,marginsObj), "2 2 4 2" }, - {NULL} + { NULL, 0, 0, NULL } }; static void TreeitemIndicatorSize( @@ -3140,8 +3375,7 @@ static void TreeitemIndicatorDraw( Tk_FreeGC(Tk_Display(tkwin), gc); } -static Ttk_ElementSpec TreeitemIndicatorElementSpec = -{ +static Ttk_ElementSpec TreeitemIndicatorElementSpec = { TK_STYLE_VERSION_2, sizeof(TreeitemIndicator), TreeitemIndicatorOptions, @@ -3153,19 +3387,17 @@ static Ttk_ElementSpec TreeitemIndicatorElementSpec = * +++ Row element. */ -typedef struct -{ +typedef struct { Tcl_Obj *backgroundObj; Tcl_Obj *rowNumberObj; } RowElement; -static Ttk_ElementOptionSpec RowElementOptions[] = -{ +static Ttk_ElementOptionSpec RowElementOptions[] = { { "-background", TK_OPTION_COLOR, Tk_Offset(RowElement,backgroundObj), DEFAULT_BACKGROUND }, { "-rownumber", TK_OPTION_INT, Tk_Offset(RowElement,rowNumberObj), "0" }, - {NULL} + { NULL, 0, 0, NULL } }; static void RowElementDraw( @@ -3179,8 +3411,7 @@ static void RowElementDraw( b.x, b.y, b.width, b.height); } -static Ttk_ElementSpec RowElementSpec = -{ +static Ttk_ElementSpec RowElementSpec = { TK_STYLE_VERSION_2, sizeof(RowElement), RowElementOptions, @@ -3192,7 +3423,7 @@ static Ttk_ElementSpec RowElementSpec = * +++ Initialisation. */ -MODULE_SCOPE +MODULE_SCOPE void TtkTreeview_Init(Tcl_Interp *interp) { Ttk_Theme theme = Ttk_GetDefaultTheme(interp); |