diff options
author | jenglish <jenglish@flightlab.com> | 2008-05-23 20:20:05 (GMT) |
---|---|---|
committer | jenglish <jenglish@flightlab.com> | 2008-05-23 20:20:05 (GMT) |
commit | ee1814c0cdbcfe9807b18e2b2732c299789897a3 (patch) | |
tree | 38468a3e3f80fe6a337d7e5bef1503a0780d1797 /generic/ttk/ttkTreeview.c | |
parent | c2ee900569916fac1b939549e153c9344dca8c0a (diff) | |
download | tk-ee1814c0cdbcfe9807b18e2b2732c299789897a3.zip tk-ee1814c0cdbcfe9807b18e2b2732c299789897a3.tar.gz tk-ee1814c0cdbcfe9807b18e2b2732c299789897a3.tar.bz2 |
Batch of ttk::treeview enhancements:
+ Added [$tv identify region], [$tv identify element],
and [$tv identify item] subcommands.
+ Simplified bindings.
+ Added [$tv tag has] subcommand.
+ Tag-related display improvements: setting a tag -background
or -foreground no longer overrides selection feedback.
+ Don't need separate 'Item', 'Cell', and 'Row' style
settings anymore, only the base "Treeview" style is used.
Diffstat (limited to 'generic/ttk/ttkTreeview.c')
-rw-r--r-- | generic/ttk/ttkTreeview.c | 591 |
1 files changed, 354 insertions, 237 deletions
diff --git a/generic/ttk/ttkTreeview.c b/generic/ttk/ttkTreeview.c index 95c9a99..ffe1351 100644 --- a/generic/ttk/ttkTreeview.c +++ b/generic/ttk/ttkTreeview.c @@ -1,4 +1,4 @@ -/* $Id: ttkTreeview.c,v 1.24 2008/04/27 22:41:12 dkf Exp $ +/* $Id: ttkTreeview.c,v 1.25 2008/05/23 20:20:05 jenglish Exp $ * Copyright (c) 2004, Joe English * * ttk::treeview widget implementation. @@ -33,8 +33,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 +49,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 +75,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 +97,9 @@ static TreeItem *NewItem(void) item->openObj = NULL; item->tagsObj = NULL; + item->tagset = NULL; + item->imagespec = NULL; + return item; } @@ -103,6 +113,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 +182,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 +197,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 +224,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 +272,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 +283,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 +291,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 +360,7 @@ static int GetEnumSetFromObj( * headingHeight: [layout] * rowHeight, indent: style */ -typedef struct -{ +typedef struct { /* Resources acquired at initialization-time: */ Tk_OptionTable itemOptionTable; @@ -425,8 +435,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", @@ -887,14 +896,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 +924,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 +937,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 +951,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,18 +964,21 @@ 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); } /*------------------------------------------------------------------------ @@ -991,7 +1000,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); @@ -1028,6 +1037,7 @@ static int TreeviewInitialize(Tcl_Interp *interp, void *recordPtr) 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); @@ -1122,9 +1132,12 @@ static int ConfigureItem( int objc, Tcl_Obj *const objv[]) { Tk_SavedOptions savedOptions; + unsigned 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 +1150,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 +1184,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 +1318,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 +1340,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 - tv->tree.yscroll.first * rowHeight; + return IdentifyRow(tv, tv->tree.root->children, &ypos, y); } /* + IdentifyDisplayColumn -- @@ -1361,6 +1371,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 +1443,93 @@ 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; + + 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, 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. */ @@ -1560,34 +1702,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 -- @@ -1620,7 +1744,7 @@ static void DrawCells( Ttk_MakeBox(b.x+x, b.y+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; @@ -1655,9 +1779,9 @@ static void DrawItem( if (tv->tree.showFlags & SHOW_TREE) { 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; + 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 { @@ -1802,51 +1926,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. */ @@ -2065,7 +2144,6 @@ static int TreeviewBBoxCommand( Treeview *tv = recordPtr; TreeItem *item = 0; TreeColumn *column = 0; - int row; Ttk_Box bbox; if (objc < 3 || objc > 4) { @@ -2081,47 +2159,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,16 +2207,17 @@ 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; + BoundingBox(tv, item, NULL, &itemBox); PrepareItem(tv, item, &displayItem); /*@@@ FIX: -text, etc*/ Ttk_RebindSublayout(layout, &displayItem); Ttk_PlaceLayout(layout, ItemState(tv,item), itemBox); @@ -2209,51 +2251,97 @@ done: static int TreeviewIdentifyCommand( Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) { - 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_LayoutNode *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_LayoutIdentify(layout, x, y); + + if (element) { + const char *elementName = Ttk_LayoutNodeName(element); + Tcl_SetObjResult(interp, Tcl_NewStringObj(elementName, -1)); + } + break; } } return TCL_OK; @@ -2501,6 +2589,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); @@ -2651,7 +2740,7 @@ static int TreeviewMoveCommand( if (p != item) { --index; } /* else -- moving node forward, count index+1 nodes */ - sibling = p; + sibling = p; } } @@ -2942,7 +3031,7 @@ static int TreeviewTagConfigureCommand( Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) { Treeview *tv = recordPtr; - void *tagRecord; + Ttk_TagTable tagTable = tv->tree.tagTable; Ttk_Tag tag; if (objc < 4) { @@ -2950,22 +3039,57 @@ 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 has $tag ?$item? + */ +static int TreeviewTagHasCommand( + Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *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 option args... @@ -2976,6 +3100,7 @@ static int TreeviewTagCommand( static WidgetCommandSpec TreeviewTagCommands[] = { { "bind", TreeviewTagBindCommand }, { "configure", TreeviewTagConfigureCommand }, + { "has", TreeviewTagHasCommand }, {0,0} }; return TtkWidgetEnsembleCommand( @@ -2985,8 +3110,7 @@ static int TreeviewTagCommand( /*------------------------------------------------------------------------ * +++ Widget commands record. */ -static WidgetCommandSpec TreeviewCommands[] = -{ +static WidgetCommandSpec TreeviewCommands[] = { { "bbox", TreeviewBBoxCommand }, { "children", TreeviewChildrenCommand }, { "cget", TtkWidgetCgetCommand }, @@ -3021,8 +3145,7 @@ static WidgetCommandSpec TreeviewCommands[] = * +++ Widget definition. */ -static WidgetSpec TreeviewWidgetSpec = -{ +static WidgetSpec TreeviewWidgetSpec = { "Treeview", /* className */ sizeof(Treeview), /* recordSize */ TreeviewOptionSpecs, /* optionSpecs */ @@ -3075,15 +3198,13 @@ 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, @@ -3135,8 +3256,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, @@ -3148,14 +3268,12 @@ 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, @@ -3174,8 +3292,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, |