1/* 2 * tkTreeItem.c -- 3 * 4 * This module implements items for treectrl widgets. 5 * 6 * Copyright (c) 2002-2009 Tim Baker 7 * 8 * RCS: @(#) $Id: tkTreeItem.c,v 1.113 2010/03/08 17:04:58 treectrl Exp $ 9 */ 10 11#include "tkTreeCtrl.h" 12 13typedef struct TreeItem_ TreeItem_; 14typedef struct Column Column; 15 16/* 17 * A data structure of the following type is kept for a single column in a 18 * single item. 19 */ 20struct Column { 21 int cstate; /* STATE_xxx flags manipulated with the 22 * [item state forcolumn] command */ 23 int span; /* Number of tree-columns this column covers */ 24 TreeStyle style; /* Instance style. */ 25 Column *next; /* Column to the right of this one */ 26}; 27 28/* 29 * A data structure of the following type is kept for each item. 30 */ 31struct TreeItem_ { 32 int id; /* unique id */ 33 int depth; /* tree depth (-1 for the unique root item) */ 34 int fixedHeight; /* -height: desired height of this item (0 for 35 * no-such-value) */ 36 int numChildren; 37 int index; /* "row" in flattened tree */ 38 int indexVis; /* visible "row" in flattened tree, -1 if hidden */ 39 int state; /* STATE_xxx flags */ 40 TreeItem parent; 41 TreeItem firstChild; 42 TreeItem lastChild; 43 TreeItem prevSibling; 44 TreeItem nextSibling; 45 TreeItemDInfo dInfo; /* display info, or NULL */ 46 TreeItemRInfo rInfo; /* range info, or NULL */ 47 Column *columns; 48 int *spans; /* 1 per tree-column. spans[N] is the column index of 49 * the item-column displayed in column N. If this 50 * item's columns all have a span of 1, this field 51 * is NULL (unless it was previously allocated 52 * because some spans were > 1). */ 53 int spanAlloc; /* Size of spans[]. */ 54#define ITEM_FLAG_DELETED 0x0001 /* Item is being deleted */ 55#define ITEM_FLAG_SPANS_SIMPLE 0x0002 /* All spans are 1 */ 56#define ITEM_FLAG_SPANS_VALID 0x0004 /* Some spans are > 1, but we don't 57 * need to redo them. Also indicates 58 * we have an entry in 59 * TreeCtrl.itemSpansHash. */ 60#define ITEM_FLAG_BUTTON 0x0008 /* -button true */ 61#define ITEM_FLAG_BUTTON_AUTO 0x0010 /* -button auto */ 62#define ITEM_FLAG_VISIBLE 0x0020 /* -visible */ 63#define ITEM_FLAG_WRAP 0x0040 /* -wrap */ 64 int flags; 65 TagInfo *tagInfo; /* Tags. May be NULL. */ 66}; 67 68static CONST char *ItemUid = "Item", *ItemColumnUid = "ItemColumn"; 69 70/* 71 * Macro to test whether an item is the unique root item 72 */ 73#define IS_ROOT(i) ((i)->depth == -1) 74 75#define IS_ALL(i) ((i) == ITEM_ALL) 76 77#define IS_DELETED(i) (((i)->flags & ITEM_FLAG_DELETED) != 0) 78#define IS_VISIBLE(i) (((i)->flags & ITEM_FLAG_VISIBLE) != 0) 79#define IS_WRAP(i) (((i)->flags & ITEM_FLAG_WRAP) != 0) 80 81/* 82 * Flags returned by Tk_SetOptions() (see itemOptionSpecs below). 83 */ 84#define ITEM_CONF_BUTTON 0x0001 85#define ITEM_CONF_SIZE 0x0002 86#define ITEM_CONF_VISIBLE 0x0004 87#define ITEM_CONF_WRAP 0x0008 88 89/* 90 * Information used for Item objv parsing. 91 */ 92static Tk_OptionSpec itemOptionSpecs[] = { 93 {TK_OPTION_CUSTOM, "-button", (char *) NULL, (char *) NULL, 94 "0", -1, Tk_Offset(TreeItem_, flags), 95 0, (ClientData) NULL, ITEM_CONF_BUTTON}, 96 {TK_OPTION_PIXELS, "-height", (char *) NULL, (char *) NULL, 97 (char *) NULL, -1, Tk_Offset(TreeItem_, fixedHeight), 98 TK_OPTION_NULL_OK, (ClientData) NULL, ITEM_CONF_SIZE}, 99 {TK_OPTION_CUSTOM, "-tags", (char *) NULL, (char *) NULL, 100 (char *) NULL, -1, Tk_Offset(TreeItem_, tagInfo), 101 TK_OPTION_NULL_OK, (ClientData) &TreeCtrlCO_tagInfo, 0}, 102 {TK_OPTION_CUSTOM, "-visible", (char *) NULL, (char *) NULL, 103 "1", -1, Tk_Offset(TreeItem_, flags), 104 0, (ClientData) NULL, ITEM_CONF_VISIBLE}, 105 {TK_OPTION_CUSTOM, "-wrap", (char *) NULL, (char *) NULL, 106 "0", -1, Tk_Offset(TreeItem_, flags), 107 0, (ClientData) NULL, ITEM_CONF_WRAP}, 108 {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, 109 (char *) NULL, 0, -1, 0, 0, 0} 110}; 111 112/* 113 *---------------------------------------------------------------------- 114 * 115 * Column_Alloc -- 116 * 117 * Allocate and initialize a new Column record. 118 * 119 * Results: 120 * Pointer to allocated Column. 121 * 122 * Side effects: 123 * Memory is allocated. 124 * 125 *---------------------------------------------------------------------- 126 */ 127 128static Column * 129Column_Alloc( 130 TreeCtrl *tree /* Widget info. */ 131 ) 132{ 133#ifdef ALLOC_HAX 134 Column *column = (Column *) TreeAlloc_Alloc(tree->allocData, ItemColumnUid, 135 sizeof(Column)); 136#else 137 Column *column = (Column *) ckalloc(sizeof(Column)); 138#endif 139 memset(column, '\0', sizeof(Column)); 140 column->span = 1; 141 return column; 142} 143 144/* 145 *---------------------------------------------------------------------- 146 * 147 * TreeItemColumn_InvalidateSize -- 148 * 149 * Marks the needed height and width of the column as out-of-date. 150 * 151 * Results: 152 * None. 153 * 154 * Side effects: 155 * None. 156 * 157 *---------------------------------------------------------------------- 158 */ 159 160void 161TreeItemColumn_InvalidateSize( 162 TreeCtrl *tree, /* Widget info. */ 163 TreeItemColumn column_ /* Column token. */ 164 ) 165{ 166} 167 168/* 169 *---------------------------------------------------------------------- 170 * 171 * TreeItemColumn_NeededWidth -- 172 * 173 * Returns the requested width of a Column. 174 * 175 * Results: 176 * If the Column has a style, the requested width of the style 177 * is returned (a positive pixel value). Otherwise 0 is returned. 178 * 179 * Side effects: 180 * None. 181 * 182 *---------------------------------------------------------------------- 183 */ 184 185int 186TreeItemColumn_NeededWidth( 187 TreeCtrl *tree, /* Widget info. */ 188 TreeItem item, /* Item token. */ 189 TreeItemColumn column_ /* Column token. */ 190 ) 191{ 192 Column *self = (Column *) column_; 193 194 if (self->style != NULL) 195 return TreeStyle_NeededWidth(tree, self->style, 196 item->state | self->cstate); 197 return 0; 198} 199 200#ifdef EXPENSIVE_SPAN_WIDTH /* NOT USED */ 201 202/* 203 * When a style spans 2 or more columns, all of the requested width goes 204 * to the first column in the span. Ideally the width needed by the style 205 * would be distributed over each column in the span. To be done properly, 206 * the visibility and fixed-width of each column needs to be considered. 207 * 208 * This routine does an okay job of calculating the requested width of a 209 * column in an item when spans are involved; however there is a lot of 210 * processing involved, and this routine is called for each column in 211 * a span separately (it would be better to calculate the width of all 212 * the columns in a span at the same time). 213 */ 214 215int 216TreeItem_NeededWidthOfColumn( 217 TreeCtrl *tree, /* Widget info. */ 218 TreeItem item, /* Item token. */ 219 int _columnIndex /* Column index. */ 220 ) 221{ 222 TreeColumn treeColumn = tree->columns; 223 Column *column = item->columns; 224 int columnIndex = 0, itemColumnIndex = 0, span = 1; 225 int selfIndex = _columnIndex, fixedWidth = 0, width = 0; 226 int spanStart, spanEnd; 227 struct span { 228 Column *itemColumn; 229 int itemColumnIndex; 230 int fixedWidth; 231 int spanWidth; 232 int visible; 233 } staticSpans[STATIC_SIZE], *spans = staticSpans; 234 235 STATIC_ALLOC(spans, struct span, tree->columnCount); 236 237 while (treeColumn != NULL) { 238 spans[columnIndex].visible = TreeColumn_Visible(treeColumn); 239 if (--span == 0) { 240 if (spans[columnIndex].visible) 241 span = column ? column->span : 1; 242 else 243 span = 1; 244 itemColumnIndex = columnIndex; 245 } 246 spans[columnIndex].itemColumn = column; 247 spans[columnIndex].itemColumnIndex = itemColumnIndex; 248 spans[columnIndex].spanWidth = 0; 249 spans[columnIndex].fixedWidth = TreeColumn_FixedWidth(treeColumn); 250 ++columnIndex; 251 treeColumn = TreeColumn_Next(treeColumn); 252 if (column != NULL) 253 column = column->next; 254 } 255 256 span = 0; 257 columnIndex = spanStart = spanEnd = spans[selfIndex].itemColumnIndex; 258 while (spans[columnIndex].itemColumnIndex == spanStart) { 259 if (spans[columnIndex].visible) { 260 if (spans[columnIndex].fixedWidth >= 0) { 261 fixedWidth += spans[columnIndex].fixedWidth; 262 spans[columnIndex].spanWidth = spans[columnIndex].fixedWidth; 263 } else 264 ++span; /* number of visible columns in the span with no 265 * fixed width */ 266 } 267 spanEnd = columnIndex; 268 ++columnIndex; 269 if (columnIndex == tree->columnCount) 270 break; 271 } 272 273 column = spans[spanStart].itemColumn; 274 if (column != NULL && column->style != NULL) { 275 width = TreeStyle_NeededWidth(tree, column->style, 276 item->state | column->cstate); 277 if (width > fixedWidth && span > 0) { 278 width -= fixedWidth; 279 while (width > 0) { 280 int each = (width > span) ? width / span : 1; 281 columnIndex = spanStart; 282 while (columnIndex <= spanEnd) { 283 if (spans[columnIndex].visible) { 284 if (spans[columnIndex].fixedWidth < 0) { 285 spans[columnIndex].spanWidth += each; 286 width -= each; 287 if (width <= 0) 288 break; 289 } 290 } 291 ++columnIndex; 292 } 293 } 294 }; 295 } 296 297if (0) 298{ 299 int i; 300 for (i = 0; i < tree->columnCount; i++) { 301 dbwin("%d,%d: itemColumn %p itemColumnIndex %d fixedWidth %d spanWidth %d\n", 302 item->id, i, spans[i].itemColumn, spans[i].itemColumnIndex, spans[i].fixedWidth, 303 spans[i].spanWidth); 304 } 305} 306 width = spans[selfIndex].spanWidth; 307 STATIC_FREE(spans, struct span, tree->columnCount); 308 309 return width; 310} 311 312#endif /* EXPENSIVE_SPAN_WIDTH */ 313 314/* 315 *---------------------------------------------------------------------- 316 * 317 * TreeItemColumn_GetStyle -- 318 * 319 * Returns the style assigned to a Column. 320 * 321 * Results: 322 * Returns the style, or NULL. 323 * 324 * Side effects: 325 * None. 326 * 327 *---------------------------------------------------------------------- 328 */ 329 330TreeStyle 331TreeItemColumn_GetStyle( 332 TreeCtrl *tree, /* Widget info. */ 333 TreeItemColumn column /* Column token. */ 334 ) 335{ 336 return ((Column *) column)->style; 337} 338 339/* 340 *---------------------------------------------------------------------- 341 * 342 * TreeItemColumn_Index -- 343 * 344 * Return the 0-based index of a Column in an Item's linked list of 345 * Columns. 346 * 347 * Results: 348 * Integer index of the Column. 349 * 350 * Side effects: 351 * Tcl_Panic() if the Column isn't found. 352 * 353 *---------------------------------------------------------------------- 354 */ 355 356int 357TreeItemColumn_Index( 358 TreeCtrl *tree, /* Widget info. */ 359 TreeItem item, /* Item token. */ 360 TreeItemColumn column_ /* Column token. */ 361 ) 362{ 363 Column *column; 364 int i = 0; 365 366 column = item->columns; 367 while ((column != NULL) && (column != (Column *) column_)) { 368 i++; 369 column = column->next; 370 } 371 if (column == NULL) 372 panic("TreeItemColumn_Index: couldn't find the column\n"); 373 return i; 374} 375 376/* 377 *---------------------------------------------------------------------- 378 * 379 * TreeItemColumn_ForgetStyle -- 380 * 381 * Free the style assigned to a Column. 382 * 383 * Results: 384 * Column has no style assigned anymore. 385 * 386 * Side effects: 387 * Memory is freed. 388 * 389 *---------------------------------------------------------------------- 390 */ 391 392void 393TreeItemColumn_ForgetStyle( 394 TreeCtrl *tree, /* Widget info. */ 395 TreeItemColumn column_ /* Column token. */ 396 ) 397{ 398 Column *self = (Column *) column_; 399 400 if (self->style != NULL) { 401 TreeStyle_FreeResources(tree, self->style); 402 self->style = NULL; 403 } 404} 405 406/* 407 *---------------------------------------------------------------------- 408 * 409 * TreeItemColumn_GetNext -- 410 * 411 * Return the Column to the right of this one. 412 * 413 * Results: 414 * The next Column in the linked list, or NULL. 415 * 416 * Side effects: 417 * None. 418 * 419 *---------------------------------------------------------------------- 420 */ 421 422TreeItemColumn 423TreeItemColumn_GetNext( 424 TreeCtrl *tree, /* Widget info. */ 425 TreeItemColumn column /* Column token. */ 426 ) 427{ 428 return (TreeItemColumn) ((Column *) column)->next; 429} 430 431/* 432 *---------------------------------------------------------------------- 433 * 434 * Column_FreeResources -- 435 * 436 * Free the style and memory associated with the given Column. 437 * 438 * Results: 439 * The next Column in the linked list, or NULL. 440 * 441 * Side effects: 442 * Memory is freed. 443 * 444 *---------------------------------------------------------------------- 445 */ 446 447static Column * 448Column_FreeResources( 449 TreeCtrl *tree, /* Widget info. */ 450 Column *self /* Column to free. */ 451 ) 452{ 453 Column *next = self->next; 454 455 if (self->style != NULL) 456 TreeStyle_FreeResources(tree, self->style); 457#ifdef ALLOC_HAX 458 TreeAlloc_Free(tree->allocData, ItemColumnUid, (char *) self, sizeof(Column)); 459#else 460 WFREE(self, Column); 461#endif 462 return next; 463} 464 465/* 466 *---------------------------------------------------------------------- 467 * 468 * Item_UpdateIndex -- 469 * 470 * Set the Item.depth, Item.index and Item.indexVis fields of the 471 * given Item and all its descendants. 472 * 473 * Results: 474 * None. 475 * 476 * Side effects: 477 * The TreeCtrl.depth field may be updated to track the maximum 478 * depth of all items. 479 * 480 *---------------------------------------------------------------------- 481 */ 482 483static void 484Item_UpdateIndex(TreeCtrl *tree, 485 TreeItem item, /* Item to update. */ 486 int *index, /* New Item.index value for the item. 487 * Value is incremented. */ 488 int *indexVis /* New Item.indexVis value for the item if 489 * the item is ReallyVisible(). 490 * Value is incremented if the item is 491 * ReallyVisible(). */ 492 ) 493{ 494 TreeItem child, parent = item->parent; 495 int parentVis, parentOpen; 496 497 /* Also track max depth */ 498 if (parent != NULL) 499 item->depth = parent->depth + 1; 500 else 501 item->depth = 0; 502 if (item->depth > tree->depth) 503 tree->depth = item->depth; 504 505 item->index = (*index)++; 506 item->indexVis = -1; 507 if (parent != NULL) { 508 parentOpen = (parent->state & STATE_OPEN) != 0; 509 parentVis = parent->indexVis != -1; 510 if (IS_ROOT(parent) && !tree->showRoot) { 511 parentOpen = TRUE; 512 parentVis = IS_VISIBLE(parent); 513 } 514 if (parentVis && parentOpen && IS_VISIBLE(item)) { 515 item->indexVis = (*indexVis)++; 516 if (IS_WRAP(item)) 517 tree->itemWrapCount++; 518 } 519 520 } 521 child = item->firstChild; 522 while (child != NULL) { 523 Item_UpdateIndex(tree, child, index, indexVis); 524 child = child->nextSibling; 525 } 526} 527 528/* 529 *---------------------------------------------------------------------- 530 * 531 * Tree_UpdateItemIndex -- 532 * 533 * Set the Item.depth, Item.index and Item.indexVis fields of the 534 * every Item. Set TreeCtrl.depth to the maximum depth of all 535 * Items. Set TreeCtrl.itemVisCount to the count of all visible 536 * items. 537 * 538 * Because this is slow we try not to do it until necessary. 539 * The tree->updateIndex flags indicates when this is needed. 540 * 541 * Results: 542 * None. 543 * 544 * Side effects: 545 * None. 546 * 547 *---------------------------------------------------------------------- 548 */ 549 550void 551Tree_UpdateItemIndex( 552 TreeCtrl *tree /* Widget info. */ 553 ) 554{ 555 TreeItem item = tree->root; 556 int index = 1, indexVis = 0; 557 558 if (!tree->updateIndex) 559 return; 560 561 if (tree->debug.enable && tree->debug.data) 562 dbwin("Tree_UpdateItemIndex %s\n", Tk_PathName(tree->tkwin)); 563 564 /* Also track max depth */ 565 tree->depth = -1; 566 567 /* Count visible items with -wrap=true */ 568 tree->itemWrapCount = 0; 569 570 item->index = 0; 571 item->indexVis = -1; 572 if (tree->showRoot && IS_VISIBLE(item)) { 573 item->indexVis = indexVis++; 574 if (IS_WRAP(item)) 575 tree->itemWrapCount++; 576 } 577 item = item->firstChild; 578 while (item != NULL) { 579 Item_UpdateIndex(tree, item, &index, &indexVis); 580 item = item->nextSibling; 581 } 582 tree->itemVisCount = indexVis; 583 tree->updateIndex = 0; 584} 585 586/* 587 *---------------------------------------------------------------------- 588 * 589 * Item_Alloc -- 590 * 591 * Allocate an initialize a new Item record. 592 * 593 * Results: 594 * Pointer to the allocated Item record. 595 * 596 * Side effects: 597 * Memory is allocated. 598 * 599 *---------------------------------------------------------------------- 600 */ 601 602static TreeItem 603Item_Alloc( 604 TreeCtrl *tree /* Widget info. */ 605 ) 606{ 607#ifdef ALLOC_HAX 608 TreeItem item = (TreeItem) TreeAlloc_Alloc(tree->allocData, ItemUid, sizeof(TreeItem_)); 609#else 610 TreeItem item = (TreeItem) ckalloc(sizeof(TreeItem_)); 611#endif 612 memset(item, '\0', sizeof(TreeItem_)); 613 if (Tk_InitOptions(tree->interp, (char *) item, 614 tree->itemOptionTable, tree->tkwin) != TCL_OK) 615 panic("Tk_InitOptions() failed in Item_Alloc()"); 616 item->state = 617 STATE_OPEN | 618 STATE_ENABLED; 619 if (tree->gotFocus) 620 item->state |= STATE_FOCUS; 621 item->indexVis = -1; 622 /* In the typical case all spans are 1. */ 623 item->flags |= ITEM_FLAG_SPANS_SIMPLE; 624 Tree_AddItem(tree, item); 625 return item; 626} 627 628/* 629 *---------------------------------------------------------------------- 630 * 631 * Item_AllocRoot -- 632 * 633 * Allocate an initialize a new Item record for the root item. 634 * 635 * Results: 636 * Pointer to the allocated Item record. 637 * 638 * Side effects: 639 * Memory is allocated. 640 * 641 *---------------------------------------------------------------------- 642 */ 643 644static TreeItem 645Item_AllocRoot( 646 TreeCtrl *tree /* Widget info. */ 647 ) 648{ 649 TreeItem item; 650 651 item = Item_Alloc(tree); 652 item->depth = -1; 653 item->state |= STATE_ACTIVE; 654 return item; 655} 656 657/* 658 *---------------------------------------------------------------------- 659 * 660 * TreeItem_GetFirstColumn -- 661 * 662 * Return the first Column record for an Item. 663 * 664 * Results: 665 * Token for the column, or NULL. 666 * 667 * Side effects: 668 * None. 669 * 670 *---------------------------------------------------------------------- 671 */ 672 673TreeItemColumn 674TreeItem_GetFirstColumn( 675 TreeCtrl *tree, /* Widget info. */ 676 TreeItem item /* Item token. */ 677 ) 678{ 679 return (TreeItemColumn) item->columns; 680} 681 682/* 683 *---------------------------------------------------------------------- 684 * 685 * TreeItem_GetState -- 686 * 687 * Return the state flags for an Item. 688 * 689 * Results: 690 * Bit mask of STATE_xxx flags. 691 * 692 * Side effects: 693 * None. 694 * 695 *---------------------------------------------------------------------- 696 */ 697 698int 699TreeItem_GetState( 700 TreeCtrl *tree, /* Widget info. */ 701 TreeItem item /* Item token. */ 702 ) 703{ 704 return item->state; 705} 706 707/* 708 *---------------------------------------------------------------------- 709 * 710 * Column_ChangeState -- 711 * 712 * Toggles zero or more STATE_xxx flags for a Column. If the 713 * Column has a style assigned, its state may be changed. 714 * 715 * Results: 716 * Bit mask of CS_LAYOUT and CS_DISPLAY flags, or zero if no 717 * changes occurred. 718 * 719 * Side effects: 720 * Display changes. 721 * 722 *---------------------------------------------------------------------- 723 */ 724 725static int 726Column_ChangeState( 727 TreeCtrl *tree, /* Widget info. */ 728 TreeItem item, /* Item containing the column. */ 729 Column *column, /* Column to modify the state of. */ 730 TreeColumn treeColumn, /* Tree column. */ 731 int stateOff, /* STATE_xxx flags to turn off. */ 732 int stateOn /* STATE_xxx flags to turn on. */ 733 ) 734{ 735 int cstate, state; 736 int sMask, iMask = 0; 737 738 cstate = column->cstate; 739 cstate &= ~stateOff; 740 cstate |= stateOn; 741 742 if (cstate == column->cstate) 743 return 0; 744 745 state = item->state | column->cstate; 746 state &= ~stateOff; 747 state |= stateOn; 748 749 if (column->style != NULL) { 750 sMask = TreeStyle_ChangeState(tree, column->style, 751 item->state | column->cstate, state); 752 if (sMask) { 753 if (sMask & CS_LAYOUT) 754 Tree_InvalidateColumnWidth(tree, treeColumn); 755 iMask |= sMask; 756 } 757 758 if (iMask & CS_LAYOUT) { 759 TreeItem_InvalidateHeight(tree, item); 760 TreeItemColumn_InvalidateSize(tree, (TreeItemColumn) column); 761 Tree_FreeItemDInfo(tree, item, NULL); 762 Tree_DInfoChanged(tree, DINFO_REDO_RANGES); 763 } else if (iMask & CS_DISPLAY) { 764 Tree_InvalidateItemDInfo(tree, treeColumn, item, NULL); 765 } 766 } 767 768 column->cstate = cstate; 769 770 return iMask; 771} 772 773/* 774 *---------------------------------------------------------------------- 775 * 776 * TreeItem_ChangeState -- 777 * 778 * Toggles zero or more STATE_xxx flags for an Item. If the 779 * Column has a style assigned, its state may be changed. 780 * 781 * Results: 782 * Bit mask of CS_LAYOUT and CS_DISPLAY flags, or zero if no 783 * changes occurred. 784 * 785 * Side effects: 786 * Display changes. 787 * 788 *---------------------------------------------------------------------- 789 */ 790 791int 792TreeItem_ChangeState( 793 TreeCtrl *tree, /* Widget info. */ 794 TreeItem item, /* Item token. */ 795 int stateOff, /* STATE_xxx flags to turn off. */ 796 int stateOn /* STATE_xxx flags to turn on. */ 797 ) 798{ 799 Column *column; 800 TreeColumn treeColumn; 801 int columnIndex = 0, state, cstate; 802 int sMask, iMask = 0; 803 804 state = item->state; 805 state &= ~stateOff; 806 state |= stateOn; 807 808 if (state == item->state) 809 return 0; 810 811 treeColumn = tree->columns; 812 column = item->columns; 813 while (column != NULL) { 814 if (column->style != NULL) { 815 cstate = item->state | column->cstate; 816 cstate &= ~stateOff; 817 cstate |= stateOn; 818 sMask = TreeStyle_ChangeState(tree, column->style, 819 item->state | column->cstate, cstate); 820 if (sMask) { 821 if (sMask & CS_LAYOUT) { 822 Tree_InvalidateColumnWidth(tree, treeColumn); 823 TreeItemColumn_InvalidateSize(tree, (TreeItemColumn) column); 824 } else if (sMask & CS_DISPLAY) { 825 Tree_InvalidateItemDInfo(tree, treeColumn, item, NULL); 826 } 827 iMask |= sMask; 828 } 829 } 830 columnIndex++; 831 column = column->next; 832 treeColumn = TreeColumn_Next(treeColumn); 833 } 834 835 /* This item has a button */ 836 if (TreeItem_HasButton(tree, item)) { 837 838 Tk_Image image1, image2; 839 Pixmap bitmap1, bitmap2; 840 /* NOTE: These next 2 lines must have 'static' to work around a 841 * Microsoft compiler optimization bug. */ 842 static int butOpen, butClosed; 843 static int themeOpen, themeClosed; 844 int w1, h1, w2, h2; 845 void *ptr1 = NULL, *ptr2 = NULL; 846 847 /* 848 * Compare the image/bitmap/theme/xlib button for the old state 849 * to the image/bitmap/theme/xlib button for the new state. Figure 850 * out if the size or appearance has changed. 851 */ 852 853 /* image > bitmap > theme > draw */ 854 image1 = PerStateImage_ForState(tree, &tree->buttonImage, item->state, NULL); 855 if (image1 != NULL) { 856 Tk_SizeOfImage(image1, &w1, &h1); 857 ptr1 = image1; 858 } 859 if (ptr1 == NULL) { 860 bitmap1 = PerStateBitmap_ForState(tree, &tree->buttonBitmap, item->state, NULL); 861 if (bitmap1 != None) { 862 Tk_SizeOfBitmap(tree->display, bitmap1, &w1, &h1); 863 ptr1 = (void *) bitmap1; 864 } 865 } 866 if (ptr1 == NULL) { 867 if (tree->useTheme && 868 TreeTheme_GetButtonSize(tree, Tk_WindowId(tree->tkwin), 869 (item->state & STATE_OPEN) != 0, &w1, &h1) == TCL_OK) { 870 ptr1 = (item->state & STATE_OPEN) ? &themeOpen : &themeClosed; 871 } 872 } 873 if (ptr1 == NULL) { 874 w1 = h1 = tree->buttonSize; 875 ptr1 = (item->state & STATE_OPEN) ? &butOpen : &butClosed; 876 } 877 878 /* image > bitmap > theme > draw */ 879 image2 = PerStateImage_ForState(tree, &tree->buttonImage, state, NULL); 880 if (image2 != NULL) { 881 Tk_SizeOfImage(image2, &w2, &h2); 882 ptr2 = image2; 883 } 884 if (ptr2 == NULL) { 885 bitmap2 = PerStateBitmap_ForState(tree, &tree->buttonBitmap, state, NULL); 886 if (bitmap2 != None) { 887 Tk_SizeOfBitmap(tree->display, bitmap2, &w2, &h2); 888 ptr2 = (void *) bitmap2; 889 } 890 } 891 if (ptr2 == NULL) { 892 if (tree->useTheme && 893 TreeTheme_GetButtonSize(tree, Tk_WindowId(tree->tkwin), 894 (state & STATE_OPEN) != 0, &w2, &h2) == TCL_OK) { 895 ptr2 = (state & STATE_OPEN) ? &themeOpen : &themeClosed; 896 } 897 } 898 if (ptr2 == NULL) { 899 w2 = h2 = tree->buttonSize; 900 ptr2 = (state & STATE_OPEN) ? &butOpen : &butClosed; 901 } 902 903 if ((w1 != w2) || (h1 != h2)) { 904 iMask |= CS_LAYOUT | CS_DISPLAY; 905 } else if (ptr1 != ptr2) { 906 iMask |= CS_DISPLAY; 907 if (tree->columnTree != NULL) 908 Tree_InvalidateItemDInfo(tree, tree->columnTree, item, NULL); 909 } 910 } 911 912 if (iMask & CS_LAYOUT) { 913 TreeItem_InvalidateHeight(tree, item); 914 Tree_FreeItemDInfo(tree, item, NULL); 915 Tree_DInfoChanged(tree, DINFO_REDO_RANGES); 916 } 917 918 item->state = state; 919 920 return iMask; 921} 922 923/* 924 *---------------------------------------------------------------------- 925 * 926 * TreeItem_UndefineState -- 927 * 928 * Clear a STATE_xxx flag in an Item and its Columns. This is 929 * called when a user-defined state is undefined via the 930 * [state undefine] widget command. 931 * 932 * Results: 933 * None. 934 * 935 * Side effects: 936 * None. 937 * 938 *---------------------------------------------------------------------- 939 */ 940 941void 942TreeItem_UndefineState( 943 TreeCtrl *tree, /* Widget info. */ 944 TreeItem item, /* Item token. */ 945 int state /* STATE_xxx flag that is undefined. */ 946 ) 947{ 948 Column *column = item->columns; 949 950 while (column != NULL) { 951 column->cstate &= ~state; 952 column = column->next; 953 } 954 955 item->state &= ~state; 956} 957 958/* 959 *---------------------------------------------------------------------- 960 * 961 * TreeItem_HasButton -- 962 * 963 * Determine whether an item should have a button displayed next to 964 * it. This considers the value of the item option -button as well 965 * as the treectrl options -showbuttons, -showrootchildbuttons and 966 * -showrootbutton. 967 * 968 * Results: 969 * None. 970 * 971 * Side effects: 972 * None. 973 * 974 *---------------------------------------------------------------------- 975 */ 976 977int 978TreeItem_HasButton( 979 TreeCtrl *tree, /* Widget info. */ 980 TreeItem item /* Item token. */ 981 ) 982{ 983 if (!tree->showButtons || (IS_ROOT(item) && !tree->showRootButton)) 984 return 0; 985 if (item->parent == tree->root && !tree->showRootChildButtons) 986 return 0; 987 if (item->flags & ITEM_FLAG_BUTTON) 988 return 1; 989 if (item->flags & ITEM_FLAG_BUTTON_AUTO) { 990 TreeItem child = item->firstChild; 991 while (child != NULL) { 992 if (IS_VISIBLE(child)) 993 return 1; 994 child = child->nextSibling; 995 } 996 } 997 return 0; 998} 999 1000/* 1001 *---------------------------------------------------------------------- 1002 * 1003 * TreeItem_GetDepth -- 1004 * 1005 * Return the depth of an Item. 1006 * 1007 * Results: 1008 * Integer >= -1. (-1 for the root) 1009 * 1010 * Side effects: 1011 * None. 1012 * 1013 *---------------------------------------------------------------------- 1014 */ 1015 1016int 1017TreeItem_GetDepth( 1018 TreeCtrl *tree, /* Widget info. */ 1019 TreeItem item /* Item token. */ 1020 ) 1021{ 1022#if 0 1023 Tree_UpdateItemIndex(tree); 1024#endif 1025 return item->depth; 1026} 1027 1028/* 1029 *---------------------------------------------------------------------- 1030 * 1031 * TreeItem_GetID -- 1032 * 1033 * Return the unique ID of an Item. 1034 * 1035 * Results: 1036 * Integer >= 0. (0 for the root) 1037 * 1038 * Side effects: 1039 * None. 1040 * 1041 *---------------------------------------------------------------------- 1042 */ 1043 1044int 1045TreeItem_GetID( 1046 TreeCtrl *tree, /* Widget info. */ 1047 TreeItem item /* Item token. */ 1048 ) 1049{ 1050 return item->id; 1051} 1052 1053/* 1054 *---------------------------------------------------------------------- 1055 * 1056 * TreeItem_SetID -- 1057 * 1058 * Set the unique ID of an Item. This is called when the item 1059 * is created. 1060 * 1061 * Results: 1062 * The given ID. 1063 * 1064 * Side effects: 1065 * None. 1066 * 1067 *---------------------------------------------------------------------- 1068 */ 1069 1070int 1071TreeItem_SetID( 1072 TreeCtrl *tree, /* Widget info. */ 1073 TreeItem item, /* Item token. */ 1074 int id /* Unique ID for the item. */ 1075 ) 1076{ 1077 return item->id = id; 1078} 1079 1080/* 1081 *---------------------------------------------------------------------- 1082 * 1083 * TreeItem_GetEnabled -- 1084 * 1085 * Return whether an Item is enabled or not. 1086 * 1087 * Results: 1088 * TRUE if the item is enabled, FALSE otherwise. 1089 * 1090 * Side effects: 1091 * None. 1092 * 1093 *---------------------------------------------------------------------- 1094 */ 1095 1096int 1097TreeItem_GetEnabled( 1098 TreeCtrl *tree, /* Widget info. */ 1099 TreeItem item /* Item token. */ 1100 ) 1101{ 1102 return (item->state & STATE_ENABLED) != 0; 1103} 1104 1105/* 1106 *---------------------------------------------------------------------- 1107 * 1108 * TreeItem_GetSelected -- 1109 * 1110 * Return whether an Item is selected or not. 1111 * 1112 * Results: 1113 * TRUE if the item is part of the selection, FALSE otherwise. 1114 * 1115 * Side effects: 1116 * None. 1117 * 1118 *---------------------------------------------------------------------- 1119 */ 1120 1121int 1122TreeItem_GetSelected( 1123 TreeCtrl *tree, /* Widget info. */ 1124 TreeItem item /* Item token. */ 1125 ) 1126{ 1127 return (item->state & STATE_SELECTED) != 0; 1128} 1129 1130/* 1131 *---------------------------------------------------------------------- 1132 * 1133 * TreeItem_GetParent -- 1134 * 1135 * Return the parent of an Item. 1136 * 1137 * Results: 1138 * Token for parent Item, or NULL. 1139 * 1140 * Side effects: 1141 * None. 1142 * 1143 *---------------------------------------------------------------------- 1144 */ 1145 1146TreeItem 1147TreeItem_GetParent( 1148 TreeCtrl *tree, /* Widget info. */ 1149 TreeItem item /* Item token. */ 1150 ) 1151{ 1152 return item->parent; 1153} 1154 1155/* 1156 *---------------------------------------------------------------------- 1157 * 1158 * TreeItem_GetWrap -- 1159 * 1160 * Return whether an Item -wrap is TRUE or FALSE. 1161 * 1162 * Results: 1163 * TRUE if the item should wrap, FALSE otherwise. 1164 * 1165 * Side effects: 1166 * None. 1167 * 1168 *---------------------------------------------------------------------- 1169 */ 1170 1171int 1172TreeItem_GetWrap( 1173 TreeCtrl *tree, /* Widget info. */ 1174 TreeItem item /* Item token. */ 1175 ) 1176{ 1177 return (item->flags & ITEM_FLAG_WRAP) != 0; 1178} 1179 1180/* 1181 *---------------------------------------------------------------------- 1182 * 1183 * TreeItem_GetNextSibling -- 1184 * 1185 * Return the next sibling of an Item. 1186 * 1187 * Results: 1188 * Token for next sibling Item, or NULL. 1189 * 1190 * Side effects: 1191 * None. 1192 * 1193 *---------------------------------------------------------------------- 1194 */ 1195 1196TreeItem 1197TreeItem_GetNextSibling( 1198 TreeCtrl *tree, /* Widget info. */ 1199 TreeItem item /* Item token. */ 1200 ) 1201{ 1202 return item->nextSibling; 1203} 1204 1205/* 1206 *---------------------------------------------------------------------- 1207 * 1208 * TreeItem_NextSiblingVisible -- 1209 * 1210 * Find a following sibling that is ReallyVisible(). 1211 * 1212 * Results: 1213 * Token for a sibling Item, or NULL. 1214 * 1215 * Side effects: 1216 * None. 1217 * 1218 *---------------------------------------------------------------------- 1219 */ 1220 1221TreeItem 1222TreeItem_NextSiblingVisible( 1223 TreeCtrl *tree, /* Widget info. */ 1224 TreeItem item /* Item token. */ 1225 ) 1226{ 1227 item = TreeItem_GetNextSibling(tree, item); 1228 while (item != NULL) { 1229 if (TreeItem_ReallyVisible(tree, item)) 1230 return item; 1231 item = TreeItem_GetNextSibling(tree, item); 1232 } 1233 return NULL; 1234} 1235 1236/* 1237 *---------------------------------------------------------------------- 1238 * 1239 * TreeItem_SetDInfo -- 1240 * 1241 * Store a display-info token in an Item. Called by the display 1242 * code. 1243 * 1244 * Results: 1245 * None. 1246 * 1247 * Side effects: 1248 * None. 1249 * 1250 *---------------------------------------------------------------------- 1251 */ 1252 1253void 1254TreeItem_SetDInfo( 1255 TreeCtrl *tree, /* Widget info. */ 1256 TreeItem item, /* Item token. */ 1257 TreeItemDInfo dInfo /* Display-info token. */ 1258 ) 1259{ 1260 item->dInfo = dInfo; 1261} 1262 1263/* 1264 *---------------------------------------------------------------------- 1265 * 1266 * TreeItem_GetDInfo -- 1267 * 1268 * Return the display-info token of an Item. Called by the display 1269 * code. 1270 * 1271 * Results: 1272 * The display-info token or NULL. 1273 * 1274 * Side effects: 1275 * None. 1276 * 1277 *---------------------------------------------------------------------- 1278 */ 1279 1280TreeItemDInfo 1281TreeItem_GetDInfo( 1282 TreeCtrl *tree, /* Widget info. */ 1283 TreeItem item /* Item token. */ 1284 ) 1285{ 1286 return item->dInfo; 1287} 1288 1289/* 1290 *---------------------------------------------------------------------- 1291 * 1292 * TreeItem_SetRInfo -- 1293 * 1294 * Store a range-info token in an Item. Called by the display 1295 * code. 1296 * 1297 * Results: 1298 * None. 1299 * 1300 * Side effects: 1301 * None. 1302 * 1303 *---------------------------------------------------------------------- 1304 */ 1305 1306void 1307TreeItem_SetRInfo( 1308 TreeCtrl *tree, /* Widget info. */ 1309 TreeItem item, /* Item token. */ 1310 TreeItemRInfo rInfo /* Range-info token */ 1311 ) 1312{ 1313 item->rInfo = rInfo; 1314} 1315 1316/* 1317 *---------------------------------------------------------------------- 1318 * 1319 * TreeItem_GetRInfo -- 1320 * 1321 * Return the range-info token of an Item. Called by the display 1322 * code. 1323 * 1324 * Results: 1325 * The range-info token or NULL. 1326 * 1327 * Side effects: 1328 * None. 1329 * 1330 *---------------------------------------------------------------------- 1331 */ 1332 1333TreeItemRInfo 1334TreeItem_GetRInfo( 1335 TreeCtrl *tree, /* Widget info. */ 1336 TreeItem item /* Item token. */ 1337 ) 1338{ 1339 return item->rInfo; 1340} 1341 1342/* 1343 *---------------------------------------------------------------------- 1344 * 1345 * TreeItem_Next -- 1346 * 1347 * Return the Item after the given one. 1348 * 1349 * Results: 1350 * Result will be: 1351 * a) the first child, or 1352 * b) the next sibling, or 1353 * c) the next sibling of an ancestor, or 1354 * d) NULL if no item follows this one 1355 * 1356 * Side effects: 1357 * None. 1358 * 1359 *---------------------------------------------------------------------- 1360 */ 1361 1362TreeItem 1363TreeItem_Next( 1364 TreeCtrl *tree, /* Widget info. */ 1365 TreeItem item /* Item token. */ 1366 ) 1367{ 1368 if (item->firstChild != NULL) 1369 return item->firstChild; 1370 if (item->nextSibling != NULL) 1371 return item->nextSibling; 1372 while (1) { 1373 item = item->parent; 1374 if (item == NULL) 1375 break; 1376 if (item->nextSibling != NULL) 1377 return item->nextSibling; 1378 } 1379 return NULL; 1380} 1381 1382/* 1383 *---------------------------------------------------------------------- 1384 * 1385 * TreeItem_NextVisible -- 1386 * 1387 * Return the ReallyVisible() Item after the given one. 1388 * 1389 * Results: 1390 * Result will be: 1391 * a) the first ReallyVisible() child, or 1392 * b) the next ReallyVisible() sibling, or 1393 * c) the next ReallyVisible() sibling of an ancestor, or 1394 * d) NULL if no ReallyVisible() item follows this one 1395 * 1396 * Side effects: 1397 * None. 1398 * 1399 *---------------------------------------------------------------------- 1400 */ 1401 1402TreeItem 1403TreeItem_NextVisible( 1404 TreeCtrl *tree, /* Widget info. */ 1405 TreeItem item /* Item token. */ 1406 ) 1407{ 1408 item = TreeItem_Next(tree, item); 1409 while (item != NULL) { 1410 if (TreeItem_ReallyVisible(tree, item)) 1411 return item; 1412 item = TreeItem_Next(tree, item); 1413 } 1414 return NULL; 1415} 1416 1417/* 1418 *---------------------------------------------------------------------- 1419 * 1420 * TreeItem_Prev -- 1421 * 1422 * Return the Item before the given one. 1423 * 1424 * Results: 1425 * Result will be: 1426 * a) the last descendant of the previous sibling, or 1427 * b) the parent, or 1428 * c) NULL if the given Item is the root 1429 * 1430 * Side effects: 1431 * None. 1432 * 1433 *---------------------------------------------------------------------- 1434 */ 1435 1436TreeItem 1437TreeItem_Prev( 1438 TreeCtrl *tree, /* Widget info. */ 1439 TreeItem item /* Item token. */ 1440 ) 1441{ 1442 TreeItem walk; 1443 1444 if (item->parent == NULL) /* root */ 1445 return NULL; 1446 walk = item->parent; 1447 if (item->prevSibling) { 1448 walk = item->prevSibling; 1449 while (walk->lastChild != NULL) 1450 walk = walk->lastChild; 1451 } 1452 return walk; 1453} 1454 1455/* 1456 *---------------------------------------------------------------------- 1457 * 1458 * TreeItem_PrevVisible -- 1459 * 1460 * Return the ReallyVisible() Item before the given one. 1461 * 1462 * Results: 1463 * Result will be: 1464 * a) the last descendant of the previous sibling, or 1465 * b) the parent, or 1466 * c) NULL if the given Item is the root 1467 * 1468 * Side effects: 1469 * None. 1470 * 1471 *---------------------------------------------------------------------- 1472 */ 1473 1474TreeItem 1475TreeItem_PrevVisible( 1476 TreeCtrl *tree, /* Widget info. */ 1477 TreeItem item /* Item token. */ 1478 ) 1479{ 1480 item = TreeItem_Prev(tree, item); 1481 while (item != NULL) { 1482 if (TreeItem_ReallyVisible(tree, item)) 1483 return item; 1484 item = TreeItem_Prev(tree, item); 1485 } 1486 return NULL; 1487} 1488 1489/* 1490 *---------------------------------------------------------------------- 1491 * 1492 * TreeItem_ToIndex -- 1493 * 1494 * Return Item.index and Item.indexVis. 1495 * 1496 * Results: 1497 * The zero-based indexes of the Item. 1498 * 1499 * Side effects: 1500 * Will recalculate the indexes of every item if needed. 1501 * 1502 *---------------------------------------------------------------------- 1503 */ 1504 1505void 1506TreeItem_ToIndex( 1507 TreeCtrl *tree, /* Widget info. */ 1508 TreeItem item, /* Item token. */ 1509 int *index, /* Returned Item.index, may be NULL */ 1510 int *indexVis /* Returned Item.indexVis, may be NULL */ 1511 ) 1512{ 1513 Tree_UpdateItemIndex(tree); 1514 if (index != NULL) (*index) = item->index; 1515 if (indexVis != NULL) (*indexVis) = item->indexVis; 1516} 1517 1518/* 1519 *---------------------------------------------------------------------- 1520 * 1521 * ItemHasTag -- 1522 * 1523 * Checks whether an item has a certain tag. 1524 * 1525 * Results: 1526 * Returns TRUE if the item has the given tag. 1527 * 1528 * Side effects: 1529 * None. 1530 * 1531 *---------------------------------------------------------------------- 1532 */ 1533 1534static int 1535ItemHasTag( 1536 TreeItem item, /* The item to test. */ 1537 Tk_Uid tag /* Tag to look for. */ 1538 ) 1539{ 1540 TagInfo *tagInfo = item->tagInfo; 1541 Tk_Uid *tagPtr; 1542 int count; 1543 1544 if (tagInfo == NULL) 1545 return 0; 1546 1547 for (tagPtr = tagInfo->tagPtr, count = tagInfo->numTags; 1548 count > 0; tagPtr++, count--) { 1549 if (*tagPtr == tag) { 1550 return 1; 1551 } 1552 } 1553 return 0; 1554} 1555 1556typedef struct Qualifiers { 1557 TreeCtrl *tree; 1558 int visible; /* 1 if the item must be ReallyVisible(), 1559 0 if the item must not be ReallyVisible(), 1560 -1 for unspecified. */ 1561 int states[3]; /* Item states that must be on or off. */ 1562 TagExpr expr; /* Tag expression. */ 1563 int exprOK; /* TRUE if expr is valid. */ 1564 int depth; /* >= 0 for depth, -1 for unspecified */ 1565 Tk_Uid tag; /* Tag (without operators) or NULL. */ 1566} Qualifiers; 1567 1568/* 1569 *---------------------------------------------------------------------- 1570 * 1571 * Qualifiers_Init -- 1572 * 1573 * Helper routine for TreeItem_FromObj. 1574 * 1575 * Results: 1576 * None. 1577 * 1578 * Side effects: 1579 * None. 1580 * 1581 *---------------------------------------------------------------------- 1582 */ 1583 1584static void 1585Qualifiers_Init( 1586 TreeCtrl *tree, /* Widget info. */ 1587 Qualifiers *q /* Out: Initialized qualifiers. */ 1588 ) 1589{ 1590 q->tree = tree; 1591 q->visible = -1; 1592 q->states[0] = q->states[1] = q->states[2] = 0; 1593 q->exprOK = FALSE; 1594 q->depth = -1; 1595 q->tag = NULL; 1596} 1597 1598/* 1599 *---------------------------------------------------------------------- 1600 * 1601 * Qualifiers_Scan -- 1602 * 1603 * Helper routine for TreeItem_FromObj. 1604 * 1605 * Results: 1606 * TCL_OK or TCL_ERROR. 1607 * 1608 * Side effects: 1609 * None. 1610 * 1611 *---------------------------------------------------------------------- 1612 */ 1613 1614static int 1615Qualifiers_Scan( 1616 Qualifiers *q, /* Must call Qualifiers_Init first, 1617 * and Qualifiers_Free if result is TCL_OK. */ 1618 int objc, /* Number of arguments. */ 1619 Tcl_Obj **objv, /* Argument values. */ 1620 int startIndex, /* First objv[] index to look at. */ 1621 int *argsUsed /* Out: number of objv[] used. */ 1622 ) 1623{ 1624 TreeCtrl *tree = q->tree; 1625 Tcl_Interp *interp = tree->interp; 1626 int qual, j = startIndex; 1627 1628 static CONST char *qualifiers[] = { 1629 "depth", "state", "tag", "visible", "!visible", NULL 1630 }; 1631 enum qualEnum { 1632 QUAL_DEPTH, QUAL_STATE, QUAL_TAG, QUAL_VISIBLE, QUAL_NOT_VISIBLE 1633 }; 1634 /* Number of arguments used by qualifiers[]. */ 1635 static int qualArgs[] = { 1636 2, 2, 2, 1, 1 1637 }; 1638 1639 *argsUsed = 0; 1640 1641 for (; j < objc; ) { 1642 if (Tcl_GetIndexFromObj(NULL, objv[j], qualifiers, NULL, 0, 1643 &qual) != TCL_OK) 1644 break; 1645 if (objc - j < qualArgs[qual]) { 1646 Tcl_AppendResult(interp, "missing arguments to \"", 1647 Tcl_GetString(objv[j]), "\" qualifier", NULL); 1648 goto errorExit; 1649 } 1650 switch ((enum qualEnum) qual) { 1651 case QUAL_DEPTH: { 1652 if (Tcl_GetIntFromObj(interp, objv[j + 1], &q->depth) != TCL_OK) 1653 goto errorExit; 1654 break; 1655 } 1656 case QUAL_STATE: { 1657 if (Tree_StateFromListObj(tree, objv[j + 1], q->states, 1658 SFO_NOT_TOGGLE) != TCL_OK) 1659 goto errorExit; 1660 break; 1661 } 1662 case QUAL_TAG: { 1663 if (tree->itemTagExpr) { 1664 if (q->exprOK) 1665 TagExpr_Free(&q->expr); 1666 if (TagExpr_Init(tree, objv[j + 1], &q->expr) != TCL_OK) 1667 return TCL_ERROR; 1668 q->exprOK = TRUE; 1669 } else { 1670 q->tag = Tk_GetUid(Tcl_GetString(objv[j + 1])); 1671 } 1672 break; 1673 } 1674 case QUAL_VISIBLE: { 1675 q->visible = 1; 1676 break; 1677 } 1678 case QUAL_NOT_VISIBLE: { 1679 q->visible = 0; 1680 break; 1681 } 1682 } 1683 *argsUsed += qualArgs[qual]; 1684 j += qualArgs[qual]; 1685 } 1686 return TCL_OK; 1687errorExit: 1688 if (q->exprOK) 1689 TagExpr_Free(&q->expr); 1690 return TCL_ERROR; 1691} 1692 1693/* 1694 *---------------------------------------------------------------------- 1695 * 1696 * Qualifies -- 1697 * 1698 * Helper routine for TreeItem_FromObj. 1699 * 1700 * Results: 1701 * Returns TRUE if the item meets the given criteria. 1702 * 1703 * Side effects: 1704 * None. 1705 * 1706 *---------------------------------------------------------------------- 1707 */ 1708 1709static int 1710Qualifies( 1711 Qualifiers *q, /* Qualifiers to check. */ 1712 TreeItem item /* The item to test. May be NULL. */ 1713 ) 1714{ 1715 TreeCtrl *tree = q->tree; 1716 1717 /* Note: if the item is NULL it is a "match" because we have run 1718 * out of items to check. */ 1719 if (item == NULL) 1720 return 1; 1721 if ((q->visible == 1) && !TreeItem_ReallyVisible(tree, item)) 1722 return 0; 1723 else if ((q->visible == 0) && TreeItem_ReallyVisible(tree, (TreeItem) item)) 1724 return 0; 1725 if (q->states[STATE_OP_OFF] & item->state) 1726 return 0; 1727 if ((q->states[STATE_OP_ON] & item->state) != q->states[STATE_OP_ON]) 1728 return 0; 1729 if (q->exprOK && !TagExpr_Eval(&q->expr, item->tagInfo)) 1730 return 0; 1731 if ((q->depth >= 0) && (item->depth + 1 != q->depth)) 1732 return 0; 1733 if ((q->tag != NULL) && !ItemHasTag(item, q->tag)) 1734 return 0; 1735 return 1; 1736} 1737 1738/* 1739 *---------------------------------------------------------------------- 1740 * 1741 * Qualifiers_Free -- 1742 * 1743 * Helper routine for TreeItem_FromObj. 1744 * 1745 * Results: 1746 * None. 1747 * 1748 * Side effects: 1749 * None. 1750 * 1751 *---------------------------------------------------------------------- 1752 */ 1753 1754static void 1755Qualifiers_Free( 1756 Qualifiers *q /* Out: Initialized qualifiers. */ 1757 ) 1758{ 1759 if (q->exprOK) 1760 TagExpr_Free(&q->expr); 1761} 1762 1763/* 1764 *---------------------------------------------------------------------- 1765 * 1766 * TreeItemList_FromObj -- 1767 * 1768 * Parse a Tcl_Obj item description to get a list of items. 1769 * 1770 * -- returning a single item -- 1771 * "active MODIFIERS" 1772 * "anchor MODIFIERS" 1773 * "nearest x y MODIFIERS" 1774 * "root MODIFIERS" 1775 * "first QUALIFIERS MODIFIERS" 1776 * "last QUALIFIERS MODIFIERS" 1777 * "end QUALIFIERS MODIFIERS" 1778 * "rnc row col MODIFIERS" 1779 * "ID MODIFIERS" 1780 * -- returning multiple items -- 1781 * all QUALIFIERS 1782 * QUALIFIERS (like "all QUALIFIERS") 1783 * list listOfItemDescs 1784 * range QUALIFIERS 1785 * tag tagExpr QUALIFIERS 1786 * "TAG-EXPR QUALFIERS" 1787 * 1788 * MODIFIERS: 1789 * -- returning a single item -- 1790 * above 1791 * below 1792 * left 1793 * right 1794 * top 1795 * bottom 1796 * leftmost 1797 * rightmost 1798 * next QUALIFIERS 1799 * prev QUALIFIERS 1800 * parent 1801 * firstchild QUALIFIERS 1802 * lastchild QUALIFIERS 1803 * child N|end?-offset? QUALIFIERS 1804 * nextsibling QUALIFIERS 1805 * prevsibling QUALIFIERS 1806 * sibling N|end?-offset? QUALIFIERS 1807 * -- returning multiple items -- 1808 * ancestors QUALIFIERS 1809 * children QUALIFIERS 1810 * descendants QUALIFIERS 1811 * 1812 * QUALIFIERS: 1813 * depth integer 1814 * state stateList 1815 * tag tagExpr 1816 * visible 1817 * !visible 1818 * 1819 * Examples: 1820 * $T item id "first visible firstchild" 1821 * $T item id "first visible firstchild visible" 1822 * $T item id "nearest x y nextsibling visible" 1823 * $T item id "last visible state enabled" 1824 * 1825 * Results: 1826 * TCL_OK or TCL_ERROR. 1827 * 1828 * Side effects: 1829 * None. 1830 * 1831 *---------------------------------------------------------------------- 1832 */ 1833 1834int 1835TreeItemList_FromObj( 1836 TreeCtrl *tree, /* Widget info. */ 1837 Tcl_Obj *objPtr, /* Object to parse to a list of items. */ 1838 TreeItemList *items, /* Uninitialized item list. Caller must free 1839 * it with TreeItemList_Free unless the 1840 * result of this function is TCL_ERROR. */ 1841 int flags /* IFO_xxx flags */ 1842 ) 1843{ 1844 Tcl_Interp *interp = tree->interp; 1845 int i, objc, index, listIndex, id; 1846 Tcl_HashEntry *hPtr; 1847 Tcl_HashSearch search; 1848 Tcl_Obj **objv, *elemPtr; 1849 TreeItem item = NULL; 1850 Qualifiers q; 1851 int qualArgsTotal; 1852 1853 static CONST char *indexName[] = { 1854 "active", "all", "anchor", "end", "first", "last", "list", 1855 "nearest", "range", "rnc", "root", (char *) NULL 1856 }; 1857 enum indexEnum { 1858 INDEX_ACTIVE, INDEX_ALL, INDEX_ANCHOR, INDEX_END, INDEX_FIRST, 1859 INDEX_LAST, INDEX_LIST, INDEX_NEAREST, INDEX_RANGE, INDEX_RNC, 1860 INDEX_ROOT 1861 }; 1862 /* Number of arguments used by indexName[]. */ 1863 static int indexArgs[] = { 1864 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 1 1865 }; 1866 /* Boolean: can indexName[] be followed by 1 or more qualifiers. */ 1867 static int indexQual[] = { 1868 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1 1869 }; 1870 1871 static CONST char *modifiers[] = { 1872 "above", "ancestors", "below", "bottom", "child", "children", 1873 "descendants", "firstchild", "lastchild", "left", "leftmost", "next", 1874 "nextsibling", "parent", "prev", "prevsibling", "right", "rightmost", 1875 "sibling", "top", (char *) NULL 1876 }; 1877 enum modEnum { 1878 TMOD_ABOVE, TMOD_ANCESTORS, TMOD_BELOW, TMOD_BOTTOM, TMOD_CHILD, 1879 TMOD_CHILDREN, TMOD_DESCENDANTS, TMOD_FIRSTCHILD, TMOD_LASTCHILD, 1880 TMOD_LEFT, TMOD_LEFTMOST, TMOD_NEXT, TMOD_NEXTSIBLING, TMOD_PARENT, 1881 TMOD_PREV, TMOD_PREVSIBLING, TMOD_RIGHT, TMOD_RIGHTMOST, TMOD_SIBLING, 1882 TMOD_TOP 1883 }; 1884 /* Number of arguments used by modifiers[]. */ 1885 static int modArgs[] = { 1886 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1 1887 }; 1888 /* Boolean: can modifiers[] be followed by 1 or more qualifiers. */ 1889 static int modQual[] = { 1890 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0 1891 }; 1892 1893 TreeItemList_Init(tree, items, 0); 1894 Qualifiers_Init(tree, &q); 1895 1896 if (Tcl_ListObjGetElements(NULL, objPtr, &objc, &objv) != TCL_OK) 1897 goto baditem; 1898 if (objc == 0) 1899 goto baditem; 1900 1901 listIndex = 0; 1902 elemPtr = objv[listIndex]; 1903 if (Tcl_GetIndexFromObj(NULL, elemPtr, indexName, NULL, 0, &index) 1904 == TCL_OK) { 1905 1906 if (objc - listIndex < indexArgs[index]) { 1907 Tcl_AppendResult(interp, "missing arguments to \"", 1908 Tcl_GetString(elemPtr), "\" keyword", NULL); 1909 goto errorExit; 1910 } 1911 1912 qualArgsTotal = 0; 1913 if (indexQual[index]) { 1914 if (Qualifiers_Scan(&q, objc, objv, listIndex + indexArgs[index], 1915 &qualArgsTotal) != TCL_OK) { 1916 goto errorExit; 1917 } 1918 } 1919 1920 switch ((enum indexEnum) index) { 1921 case INDEX_ACTIVE: { 1922 item = tree->activeItem; 1923 break; 1924 } 1925 case INDEX_ALL: { 1926 if (qualArgsTotal) { 1927 hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); 1928 while (hPtr != NULL) { 1929 item = (TreeItem) Tcl_GetHashValue(hPtr); 1930 if (Qualifies(&q, item)) { 1931 TreeItemList_Append(items, (TreeItem) item); 1932 } 1933 hPtr = Tcl_NextHashEntry(&search); 1934 } 1935 item = NULL; 1936 } else if (flags & IFO_LIST_ALL) { 1937 hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); 1938 while (hPtr != NULL) { 1939 item = (TreeItem) Tcl_GetHashValue(hPtr); 1940 TreeItemList_Append(items, item); 1941 hPtr = Tcl_NextHashEntry(&search); 1942 } 1943 item = NULL; 1944 } else { 1945 item = ITEM_ALL; 1946 } 1947 break; 1948 } 1949 case INDEX_ANCHOR: { 1950 item = tree->anchorItem; 1951 break; 1952 } 1953 case INDEX_FIRST: { 1954 item = tree->root; 1955 while (!Qualifies(&q, item)) 1956 item = TreeItem_Next(tree, item); 1957 break; 1958 } 1959 case INDEX_END: 1960 case INDEX_LAST: { 1961 item = tree->root; 1962 while (item->lastChild) { 1963 item = item->lastChild; 1964 } 1965 while (!Qualifies(&q, item)) 1966 item = TreeItem_Prev(tree, item); 1967 break; 1968 } 1969 case INDEX_LIST: { 1970 int listObjc; 1971 Tcl_Obj **listObjv; 1972 int count; 1973 1974 if (Tcl_ListObjGetElements(interp, objv[listIndex + 1], 1975 &listObjc, &listObjv) != TCL_OK) { 1976 goto errorExit; 1977 } 1978 for (i = 0; i < listObjc; i++) { 1979 TreeItemList item2s; 1980 if (TreeItemList_FromObj(tree, listObjv[i], &item2s, flags) 1981 != TCL_OK) 1982 goto errorExit; 1983 TreeItemList_Concat(items, &item2s); 1984 TreeItemList_Free(&item2s); 1985 } 1986 /* If any of the item descriptions in the list is "all", then 1987 * clear the list of items and use "all". */ 1988 count = TreeItemList_Count(items); 1989 for (i = 0; i < count; i++) { 1990 TreeItem item2 = TreeItemList_Nth(items, i); 1991 if (IS_ALL(item2)) 1992 break; 1993 } 1994 if (i < count) { 1995 TreeItemList_Free(items); 1996 item = ITEM_ALL; 1997 } else 1998 item = NULL; 1999 break; 2000 } 2001 case INDEX_NEAREST: { 2002 int x, y; 2003 2004 if (Tk_GetPixelsFromObj(interp, tree->tkwin, 2005 objv[listIndex + 1], &x) != TCL_OK) { 2006 goto errorExit; 2007 } 2008 if (Tk_GetPixelsFromObj(interp, tree->tkwin, 2009 objv[listIndex + 2], &y) != TCL_OK) { 2010 goto errorExit; 2011 } 2012 item = Tree_ItemUnderPoint(tree, &x, &y, TRUE); 2013 break; 2014 } 2015 case INDEX_RANGE: { 2016 TreeItem itemFirst, itemLast; 2017 2018 if (TreeItem_FromObj(tree, objv[listIndex + 1], &itemFirst, 2019 IFO_NOT_NULL) != TCL_OK) 2020 goto errorExit; 2021 if (TreeItem_FromObj(tree, objv[listIndex + 2], &itemLast, 2022 IFO_NOT_NULL) != TCL_OK) 2023 goto errorExit; 2024 if (TreeItem_FirstAndLast(tree, &itemFirst, &itemLast) == 0) 2025 goto errorExit; 2026 while (1) { 2027 if (Qualifies(&q, itemFirst)) { 2028 TreeItemList_Append(items, itemFirst); 2029 } 2030 if (itemFirst == itemLast) 2031 break; 2032 itemFirst = TreeItem_Next(tree, itemFirst); 2033 } 2034 item = NULL; 2035 break; 2036 } 2037 case INDEX_RNC: { 2038 int row, col; 2039 2040 if (Tcl_GetIntFromObj(interp, objv[listIndex + 1], &row) != TCL_OK) 2041 goto errorExit; 2042 if (Tcl_GetIntFromObj(interp, objv[listIndex + 2], &col) != TCL_OK) 2043 goto errorExit; 2044 item = Tree_RNCToItem(tree, row, col); 2045 break; 2046 } 2047 case INDEX_ROOT: { 2048 item = tree->root; 2049 break; 2050 } 2051 } 2052 2053 listIndex += indexArgs[index] + qualArgsTotal; 2054 2055 /* No indexName[] was found. */ 2056 } else { 2057 int gotId = FALSE; 2058 TagExpr expr; 2059 2060 /* Try an itemPrefix + item ID. */ 2061 if (tree->itemPrefixLen) { 2062 char *end, *t = Tcl_GetString(elemPtr); 2063 if (strncmp(t, tree->itemPrefix, tree->itemPrefixLen) == 0) { 2064 t += tree->itemPrefixLen; 2065 id = strtoul(t, &end, 10); 2066 if ((end != t) && (*end == '\0')) 2067 gotId = TRUE; 2068 } 2069 2070 /* Try an item ID. */ 2071 } else if (Tcl_GetIntFromObj(NULL, elemPtr, &id) == TCL_OK) { 2072 gotId = TRUE; 2073 } 2074 if (gotId) { 2075 hPtr = Tcl_FindHashEntry(&tree->itemHash, (char *) id); 2076 if (hPtr != NULL) { 2077 item = (TreeItem) Tcl_GetHashValue(hPtr); 2078 } else { 2079 item = NULL; 2080 } 2081 listIndex++; 2082 goto gotFirstPart; 2083 } 2084 2085 /* Try a list of qualifiers. This has the same effect as 2086 * "all QUALIFIERS". */ 2087 if (Qualifiers_Scan(&q, objc, objv, listIndex, &qualArgsTotal) 2088 != TCL_OK) { 2089 goto errorExit; 2090 } 2091 if (qualArgsTotal) { 2092 hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); 2093 while (hPtr != NULL) { 2094 item = (TreeItem) Tcl_GetHashValue(hPtr); 2095 if (Qualifies(&q, item)) { 2096 TreeItemList_Append(items, item); 2097 } 2098 hPtr = Tcl_NextHashEntry(&search); 2099 } 2100 item = NULL; 2101 listIndex += qualArgsTotal; 2102 goto gotFirstPart; 2103 } 2104 2105 /* Try a tag or tag expression followed by qualifiers. */ 2106 if (objc > 1) { 2107 if (Qualifiers_Scan(&q, objc, objv, listIndex + 1, 2108 &qualArgsTotal) != TCL_OK) { 2109 goto errorExit; 2110 } 2111 } 2112 if (tree->itemTagExpr) { 2113 if (TagExpr_Init(tree, elemPtr, &expr) != TCL_OK) 2114 goto errorExit; 2115 hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); 2116 while (hPtr != NULL) { 2117 item = (TreeItem) Tcl_GetHashValue(hPtr); 2118 if (TagExpr_Eval(&expr, item->tagInfo) && Qualifies(&q, item)) { 2119 TreeItemList_Append(items, item); 2120 } 2121 hPtr = Tcl_NextHashEntry(&search); 2122 } 2123 TagExpr_Free(&expr); 2124 } else { 2125 Tk_Uid tag = Tk_GetUid(Tcl_GetString(elemPtr)); 2126 hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); 2127 while (hPtr != NULL) { 2128 item = (TreeItem) Tcl_GetHashValue(hPtr); 2129 if (ItemHasTag(item, tag) && Qualifies(&q, item)) { 2130 TreeItemList_Append(items, item); 2131 } 2132 hPtr = Tcl_NextHashEntry(&search); 2133 } 2134 } 2135 item = NULL; 2136 listIndex += 1 + qualArgsTotal; 2137 } 2138 2139gotFirstPart: 2140 /* If 1 item, use it and clear the list. */ 2141 if (TreeItemList_Count(items) == 1) { 2142 item = TreeItemList_Nth(items, 0); 2143 items->count = 0; 2144 } 2145 2146 /* If "all" but only root exists, use it. */ 2147 if (IS_ALL(item) && (tree->itemCount == 1) && !(flags & IFO_NOT_ROOT)) { 2148 item = tree->root; 2149 } 2150 2151 /* If > 1 item, no modifiers may follow. */ 2152 if ((TreeItemList_Count(items) > 1) || IS_ALL(item)) { 2153 if (listIndex < objc) { 2154 Tcl_AppendResult(interp, "unexpected arguments after \"", 2155 (char *) NULL); 2156 for (i = 0; i < listIndex; i++) { 2157 Tcl_AppendResult(interp, Tcl_GetString(objv[i]), (char *) NULL); 2158 if (i != listIndex - 1) 2159 Tcl_AppendResult(interp, " ", (char *) NULL); 2160 } 2161 Tcl_AppendResult(interp, "\"", (char *) NULL); 2162 goto errorExit; 2163 } 2164 } 2165 2166 /* This means a valid specification was given, but there is no such item */ 2167 if ((TreeItemList_Count(items) == 0) && (item == NULL)) { 2168 if (flags & IFO_NOT_NULL) 2169 goto noitem; 2170 /* Empty list returned */ 2171 goto goodExit; 2172 } 2173 2174 /* Process any modifiers following the item we matched above. */ 2175 for (; listIndex < objc; /* nothing */) { 2176 2177 elemPtr = objv[listIndex]; 2178 if (Tcl_GetIndexFromObj(interp, elemPtr, modifiers, "modifier", 0, 2179 &index) != TCL_OK) { 2180 goto errorExit; 2181 } 2182 if (objc - listIndex < modArgs[index]) { 2183 Tcl_AppendResult(interp, "missing arguments to \"", 2184 Tcl_GetString(elemPtr), "\" modifier", NULL); 2185 goto errorExit; 2186 } 2187 2188 qualArgsTotal = 0; 2189 if (modQual[index]) { 2190 Qualifiers_Free(&q); 2191 Qualifiers_Init(tree, &q); 2192 if (Qualifiers_Scan(&q, objc, objv, listIndex + modArgs[index], 2193 &qualArgsTotal) != TCL_OK) { 2194 goto errorExit; 2195 } 2196 } 2197 2198 switch ((enum modEnum) index) { 2199 case TMOD_ABOVE: { 2200 item = Tree_ItemAbove(tree, item); 2201 break; 2202 } 2203 case TMOD_ANCESTORS: { 2204 item = item->parent; 2205 while (item != NULL) { 2206 if (Qualifies(&q, item)) { 2207 TreeItemList_Append(items, item); 2208 } 2209 item = item->parent; 2210 } 2211 item = NULL; 2212 break; 2213 } 2214 case TMOD_BELOW: { 2215 item = Tree_ItemBelow(tree, item); 2216 break; 2217 } 2218 case TMOD_BOTTOM: { 2219 item = Tree_ItemBottom(tree, item); 2220 break; 2221 } 2222 case TMOD_CHILD: { 2223 int n, endRelative; 2224 2225 if (Tree_GetIntForIndex(tree, objv[listIndex + 1], &n, 2226 &endRelative) != TCL_OK) { 2227 goto errorExit; 2228 } 2229 if (endRelative) { 2230 item = item->lastChild; 2231 while (item != NULL) { 2232 if (Qualifies(&q, item)) 2233 if (n-- <= 0) 2234 break; 2235 item = item->prevSibling; 2236 } 2237 } else { 2238 item = item->firstChild; 2239 while (item != NULL) { 2240 if (Qualifies(&q, item)) 2241 if (n-- <= 0) 2242 break; 2243 item = item->nextSibling; 2244 } 2245 } 2246 break; 2247 } 2248 case TMOD_CHILDREN: { 2249 item = item->firstChild; 2250 while (item != NULL) { 2251 if (Qualifies(&q, item)) { 2252 TreeItemList_Append(items, item); 2253 } 2254 item = item->nextSibling; 2255 } 2256 item = NULL; 2257 break; 2258 } 2259 case TMOD_DESCENDANTS: { 2260 TreeItem last = item; 2261 2262 while (last->lastChild != NULL) 2263 last = last->lastChild; 2264 item = item->firstChild; 2265 while (item != NULL) { 2266 if (Qualifies(&q, item)) { 2267 TreeItemList_Append(items, item); 2268 } 2269 if (item == last) 2270 break; 2271 item = TreeItem_Next(tree, item); 2272 } 2273 item = NULL; 2274 break; 2275 } 2276 case TMOD_FIRSTCHILD: { 2277 item = item->firstChild; 2278 while (!Qualifies(&q, item)) 2279 item = item->nextSibling; 2280 break; 2281 } 2282 case TMOD_LASTCHILD: { 2283 item = item->lastChild; 2284 while (!Qualifies(&q, item)) 2285 item = item->prevSibling; 2286 break; 2287 } 2288 case TMOD_LEFT: { 2289 item = Tree_ItemLeft(tree, item); 2290 break; 2291 } 2292 case TMOD_LEFTMOST: { 2293 item = Tree_ItemLeftMost(tree, item); 2294 break; 2295 } 2296 case TMOD_NEXT: { 2297 item = TreeItem_Next(tree, item); 2298 while (!Qualifies(&q, item)) 2299 item = TreeItem_Next(tree, item); 2300 break; 2301 } 2302 case TMOD_NEXTSIBLING: { 2303 item = item->nextSibling; 2304 while (!Qualifies(&q, item)) 2305 item = item->nextSibling; 2306 break; 2307 } 2308 case TMOD_PARENT: { 2309 item = item->parent; 2310 break; 2311 } 2312 case TMOD_PREV: { 2313 item = TreeItem_Prev(tree, item); 2314 while (!Qualifies(&q, item)) 2315 item = TreeItem_Prev(tree, item); 2316 break; 2317 } 2318 case TMOD_PREVSIBLING: { 2319 item = item->prevSibling; 2320 while (!Qualifies(&q, item)) 2321 item = item->prevSibling; 2322 break; 2323 } 2324 case TMOD_RIGHT: { 2325 item = Tree_ItemRight(tree, item); 2326 break; 2327 } 2328 case TMOD_RIGHTMOST: { 2329 item = Tree_ItemRightMost(tree, item); 2330 break; 2331 } 2332 case TMOD_SIBLING: { 2333 int n, endRelative; 2334 2335 if (Tree_GetIntForIndex(tree, objv[listIndex + 1], &n, 2336 &endRelative) != TCL_OK) { 2337 goto errorExit; 2338 } 2339 item = item->parent; 2340 if (item == NULL) 2341 break; 2342 if (endRelative) { 2343 item = item->lastChild; 2344 while (item != NULL) { 2345 if (Qualifies(&q, item)) 2346 if (n-- <= 0) 2347 break; 2348 item = item->prevSibling; 2349 } 2350 } else { 2351 item = item->firstChild; 2352 while (item != NULL) { 2353 if (Qualifies(&q, item)) 2354 if (n-- <= 0) 2355 break; 2356 item = item->nextSibling; 2357 } 2358 } 2359 break; 2360 } 2361 case TMOD_TOP: { 2362 item = Tree_ItemTop(tree, item); 2363 break; 2364 } 2365 } 2366 if ((TreeItemList_Count(items) > 1) || IS_ALL(item)) { 2367 int end = listIndex + modArgs[index] + qualArgsTotal; 2368 if (end < objc) { 2369 Tcl_AppendResult(interp, "unexpected arguments after \"", 2370 (char *) NULL); 2371 for (i = 0; i < end; i++) { 2372 Tcl_AppendResult(interp, Tcl_GetString(objv[i]), (char *) NULL); 2373 if (i != end - 1) 2374 Tcl_AppendResult(interp, " ", (char *) NULL); 2375 } 2376 Tcl_AppendResult(interp, "\"", (char *) NULL); 2377 goto errorExit; 2378 } 2379 } 2380 if ((TreeItemList_Count(items) == 0) && (item == NULL)) { 2381 if (flags & IFO_NOT_NULL) 2382 goto noitem; 2383 /* Empty list returned. */ 2384 goto goodExit; 2385 } 2386 listIndex += modArgs[index] + qualArgsTotal; 2387 } 2388 if ((flags & IFO_NOT_MANY) && (IS_ALL(item) || 2389 (TreeItemList_Count(items) > 1))) { 2390 FormatResult(interp, "can't specify > 1 item for this command"); 2391 goto errorExit; 2392 } 2393 if (TreeItemList_Count(items)) { 2394 if (flags & (IFO_NOT_ROOT | IFO_NOT_ORPHAN)) { 2395 int i; 2396 for (i = 0; i < TreeItemList_Count(items); i++) { 2397 item = TreeItemList_Nth(items, i); 2398 if (IS_ROOT(item) && (flags & IFO_NOT_ROOT)) 2399 goto notRoot; 2400 if ((item->parent == NULL) && (flags & IFO_NOT_ORPHAN)) 2401 goto notOrphan; 2402 } 2403 } 2404 } else if (IS_ALL(item)) { 2405 TreeItemList_Append(items, ITEM_ALL); 2406 } else { 2407 if (IS_ROOT(item) && (flags & IFO_NOT_ROOT)) { 2408notRoot: 2409 FormatResult(interp, "can't specify \"root\" for this command"); 2410 goto errorExit; 2411 } 2412 if ((item->parent == NULL) && (flags & IFO_NOT_ORPHAN)) { 2413notOrphan: 2414 FormatResult(interp, "item \"%s\" has no parent", 2415 Tcl_GetString(objPtr)); 2416 goto errorExit; 2417 } 2418 TreeItemList_Append(items, item); 2419 } 2420goodExit: 2421 Qualifiers_Free(&q); 2422 return TCL_OK; 2423 2424baditem: 2425 Tcl_AppendResult(interp, "bad item description \"", Tcl_GetString(objPtr), 2426 "\"", NULL); 2427 goto errorExit; 2428 2429noitem: 2430 Tcl_AppendResult(interp, "item \"", Tcl_GetString(objPtr), 2431 "\" doesn't exist", NULL); 2432 2433errorExit: 2434 Qualifiers_Free(&q); 2435 TreeItemList_Free(items); 2436 return TCL_ERROR; 2437} 2438 2439/* 2440 *---------------------------------------------------------------------- 2441 * 2442 * TreeItem_FromObj -- 2443 * 2444 * Parse a Tcl_Obj item description to get a single item. 2445 * 2446 * Results: 2447 * TCL_OK or TCL_ERROR. 2448 * 2449 * Side effects: 2450 * None. 2451 * 2452 *---------------------------------------------------------------------- 2453 */ 2454 2455int 2456TreeItem_FromObj( 2457 TreeCtrl *tree, /* Widget info. */ 2458 Tcl_Obj *objPtr, /* Object to parse to an item. */ 2459 TreeItem *itemPtr, /* Returned item. */ 2460 int flags /* IFO_xxx flags */ 2461 ) 2462{ 2463 TreeItemList items; 2464 2465 if (TreeItemList_FromObj(tree, objPtr, &items, flags | IFO_NOT_MANY) != TCL_OK) 2466 return TCL_ERROR; 2467 /* May be NULL. */ 2468 (*itemPtr) = TreeItemList_Nth(&items, 0); 2469 TreeItemList_Free(&items); 2470 return TCL_OK; 2471} 2472 2473/* 2474 *---------------------------------------------------------------------- 2475 * 2476 * TreeItemForEach_Start -- 2477 * 2478 * Begin iterating over items. A command might accept two item 2479 * descriptions for a range of items, or a single item description 2480 * which may itself refer to multiple items. Either item 2481 * description could be ITEM_ALL. 2482 * 2483 * Results: 2484 * Returns the first item to iterate over. If an error occurs 2485 * then ItemForEach.error is set to 1. 2486 * 2487 * Side effects: 2488 * None. 2489 * 2490 *---------------------------------------------------------------------- 2491 */ 2492 2493TreeItem 2494TreeItemForEach_Start( 2495 TreeItemList *items, /* List of items. */ 2496 TreeItemList *item2s, /* List of items or NULL. */ 2497 ItemForEach *iter /* Returned info, pass to 2498 TreeItemForEach_Next. */ 2499 ) 2500{ 2501 TreeCtrl *tree = items->tree; 2502 TreeItem item, item2 = NULL; 2503 2504 item = TreeItemList_Nth(items, 0); 2505 if (item2s) 2506 item2 = TreeItemList_Nth(item2s, 0); 2507 2508 iter->tree = tree; 2509 iter->all = FALSE; 2510 iter->error = 0; 2511 iter->items = NULL; 2512 2513 if (IS_ALL(item) || IS_ALL(item2)) { 2514 Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&tree->itemHash, &iter->search); 2515 iter->all = TRUE; 2516 return iter->item = (TreeItem) Tcl_GetHashValue(hPtr); 2517 } 2518 2519 if (item2 != NULL) { 2520 if (TreeItem_FirstAndLast(tree, &item, &item2) == 0) { 2521 iter->error = 1; 2522 return NULL; 2523 } 2524 iter->last = item2; 2525 return iter->item = item; 2526 } 2527 2528 iter->items = items; 2529 iter->index = 0; 2530 return iter->item = item; 2531} 2532 2533/* 2534 *---------------------------------------------------------------------- 2535 * 2536 * TreeItemForEach_Next -- 2537 * 2538 * Returns the next item to iterate over. Keep calling this until 2539 * the result is NULL. 2540 * 2541 * Results: 2542 * Returns the next item to iterate over or NULL. 2543 * 2544 * Side effects: 2545 * None. 2546 * 2547 *---------------------------------------------------------------------- 2548 */ 2549 2550TreeItem 2551TreeItemForEach_Next( 2552 ItemForEach *iter /* Initialized by TreeItemForEach_Start. */ 2553 ) 2554{ 2555 TreeCtrl *tree = iter->tree; 2556 2557 if (iter->all) { 2558 Tcl_HashEntry *hPtr = Tcl_NextHashEntry(&iter->search); 2559 if (hPtr == NULL) 2560 return iter->item = NULL; 2561 return iter->item = (TreeItem) Tcl_GetHashValue(hPtr); 2562 } 2563 2564 if (iter->items != NULL) { 2565 if (iter->index >= TreeItemList_Count(iter->items)) 2566 return iter->item = NULL; 2567 return iter->item = TreeItemList_Nth(iter->items, ++iter->index); 2568 } 2569 2570 if (iter->item == iter->last) 2571 return iter->item = NULL; 2572 return iter->item = TreeItem_Next(tree, iter->item); 2573} 2574 2575/* 2576 *---------------------------------------------------------------------- 2577 * 2578 * Item_ToggleOpen -- 2579 * 2580 * Inverts the STATE_OPEN flag of an Item. 2581 * 2582 * Results: 2583 * Items may be displayed/undisplayed. 2584 * 2585 * Side effects: 2586 * Display changes. 2587 * 2588 *---------------------------------------------------------------------- 2589 */ 2590 2591static void 2592Item_ToggleOpen( 2593 TreeCtrl *tree, /* Widget info. */ 2594 TreeItem item, /* Item record. */ 2595 int stateOff, /* STATE_OPEN or 0 */ 2596 int stateOn /* STATE_OPEN or 0 */ 2597 ) 2598{ 2599 int mask; 2600 2601 mask = TreeItem_ChangeState(tree, item, stateOff, stateOn); 2602 2603 if (IS_ROOT(item) && !tree->showRoot) 2604 return; 2605 2606#if 0 2607 /* Don't affect display if we weren't visible */ 2608 if (!TreeItem_ReallyVisible(tree, item)) 2609 return; 2610 2611 /* Invalidate display info for this item, so it is redrawn later. */ 2612 Tree_InvalidateItemDInfo(tree, item, NULL); 2613#endif 2614 2615 if (item->numChildren > 0) { 2616 /* indexVis needs updating for all items after this one, if we 2617 * have any visible children */ 2618 tree->updateIndex = 1; 2619 Tree_DInfoChanged(tree, DINFO_REDO_RANGES); 2620 2621 /* Hiding/showing children may change the width of any column */ 2622 Tree_InvalidateColumnWidth(tree, NULL); 2623 } 2624 2625 /* If this item was previously onscreen, this call is repetitive. */ 2626 Tree_EventuallyRedraw(tree); 2627} 2628 2629/* 2630 *---------------------------------------------------------------------- 2631 * 2632 * TreeItem_OpenClose -- 2633 * 2634 * Inverts the STATE_OPEN flag of an Item. 2635 * 2636 * Results: 2637 * Items may be displayed/undisplayed. 2638 * 2639 * Side effects: 2640 * Display changes. <Expand> and <Collapse> events may be 2641 * generated. 2642 * 2643 *---------------------------------------------------------------------- 2644 */ 2645 2646void 2647TreeItem_OpenClose( 2648 TreeCtrl *tree, /* Widget info. */ 2649 TreeItem item, /* Item token. */ 2650 int mode /* -1: toggle 2651 * 0: close 2652 * 1: open */ 2653 ) 2654{ 2655 int stateOff = 0, stateOn = 0; 2656 2657 /* When processing a list of items, any <Expand> or <Collapse> event 2658 * may result in items being deleted. */ 2659 if (IS_DELETED(item)) return; 2660 2661 if (mode == -1) { 2662 if (item->state & STATE_OPEN) 2663 stateOff = STATE_OPEN; 2664 else 2665 stateOn = STATE_OPEN; 2666 } else if (!mode && (item->state & STATE_OPEN)) 2667 stateOff = STATE_OPEN; 2668 else if (mode && !(item->state & STATE_OPEN)) 2669 stateOn = STATE_OPEN; 2670 2671 if (stateOff != stateOn) { 2672 TreeNotify_OpenClose(tree, item, stateOn, TRUE); 2673 if (IS_DELETED(item)) return; 2674 Item_ToggleOpen(tree, item, stateOff, stateOn); 2675 TreeNotify_OpenClose(tree, item, stateOn, FALSE); 2676 } 2677} 2678 2679/* 2680 *---------------------------------------------------------------------- 2681 * 2682 * TreeItem_Delete -- 2683 * 2684 * Recursively frees resources associated with an Item and its 2685 * descendants. 2686 * 2687 * Results: 2688 * Items are removed from their parent and freed. 2689 * 2690 * Side effects: 2691 * Memory is freed. If the active item or selection-anchor item 2692 * is deleted, the root becomes the active/anchor item. 2693 * Display changes may occur. 2694 * 2695 *---------------------------------------------------------------------- 2696 */ 2697 2698void 2699TreeItem_Delete( 2700 TreeCtrl *tree, /* Widget info. */ 2701 TreeItem item /* Item token. */ 2702 ) 2703{ 2704 if (TreeItem_ReallyVisible(tree, item)) 2705 Tree_InvalidateColumnWidth(tree, NULL); 2706 2707 while (item->numChildren > 0) 2708 TreeItem_Delete(tree, item->firstChild); 2709 2710 TreeItem_RemoveFromParent(tree, item); 2711 TreeDisplay_ItemDeleted(tree, item); 2712 Tree_RemoveItem(tree, item); 2713 TreeItem_FreeResources(tree, item); 2714 if (tree->activeItem == item) { 2715 tree->activeItem = tree->root; 2716 TreeItem_ChangeState(tree, tree->activeItem, 0, STATE_ACTIVE); 2717 } 2718 if (tree->anchorItem == item) 2719 tree->anchorItem = tree->root; 2720 if (tree->debug.enable && tree->debug.data) 2721 Tree_Debug(tree); 2722} 2723 2724/* 2725 *---------------------------------------------------------------------- 2726 * 2727 * TreeItem_Deleted -- 2728 * 2729 * Return 1 if the given item is deleted. 2730 * 2731 * Results: 2732 * None. 2733 * 2734 * Side effects: 2735 * None. 2736 * 2737 *---------------------------------------------------------------------- 2738 */ 2739 2740int 2741TreeItem_Deleted( 2742 TreeCtrl *tree, /* Widget info. */ 2743 TreeItem item /* Item token. */ 2744 ) 2745{ 2746 return IS_DELETED(item); 2747} 2748 2749/* 2750 *---------------------------------------------------------------------- 2751 * 2752 * TreeItem_FirstAndLast -- 2753 * 2754 * Determine the order of two items and swap them if needed. 2755 * 2756 * Results: 2757 * If the items do not share a common ancestor, 0 is returned and 2758 * an error message is left in the interpreter result. 2759 * Otherwise the return value is the number of items in the 2760 * range between first and last. 2761 * 2762 * Side effects: 2763 * None. 2764 * 2765 *---------------------------------------------------------------------- 2766 */ 2767 2768int 2769TreeItem_FirstAndLast( 2770 TreeCtrl *tree, /* Widget info. */ 2771 TreeItem *first, /* Item token. */ 2772 TreeItem *last /* Item token. */ 2773 ) 2774{ 2775 int indexFirst, indexLast, index; 2776 2777 if (TreeItem_RootAncestor(tree, *first) != 2778 TreeItem_RootAncestor(tree, *last)) { 2779 FormatResult(tree->interp, 2780 "item %s%d and item %s%d don't share a common ancestor", 2781 tree->itemPrefix, TreeItem_GetID(tree, *first), 2782 tree->itemPrefix, TreeItem_GetID(tree, *last)); 2783 return 0; 2784 } 2785 TreeItem_ToIndex(tree, *first, &indexFirst, NULL); 2786 TreeItem_ToIndex(tree, *last, &indexLast, NULL); 2787 if (indexFirst > indexLast) { 2788 TreeItem item = *first; 2789 *first = *last; 2790 *last = item; 2791 2792 index = indexFirst; 2793 indexFirst = indexLast; 2794 indexLast = index; 2795 } 2796 return indexLast - indexFirst + 1; 2797} 2798 2799/* 2800 *---------------------------------------------------------------------- 2801 * 2802 * TreeItem_ListDescendants -- 2803 * 2804 * Appends descendants of an item to a list of items. 2805 * 2806 * Results: 2807 * List of items may grow. 2808 * 2809 * Side effects: 2810 * None. 2811 * 2812 *---------------------------------------------------------------------- 2813 */ 2814 2815void 2816TreeItem_ListDescendants( 2817 TreeCtrl *tree, /* Widget info. */ 2818 TreeItem item, /* Item token. */ 2819 TreeItemList *items /* List of items to append descendants to. */ 2820 ) 2821{ 2822 TreeItem last; 2823 2824 if (item->firstChild == NULL) 2825 return; 2826 last = item; 2827 while (last->lastChild != NULL) 2828 last = last->lastChild; 2829 item = item->firstChild; 2830 while (1) { 2831 TreeItemList_Append(items, item); 2832 if (item == last) 2833 break; 2834 item = TreeItem_Next(tree, item); 2835 } 2836} 2837 2838 2839/* 2840 *---------------------------------------------------------------------- 2841 * 2842 * TreeItem_UpdateDepth -- 2843 * 2844 * Recursively updates Item.depth of an Item and its 2845 * descendants. 2846 * 2847 * Results: 2848 * None. 2849 * 2850 * Side effects: 2851 * None. 2852 * 2853 *---------------------------------------------------------------------- 2854 */ 2855 2856void 2857TreeItem_UpdateDepth( 2858 TreeCtrl *tree, /* Widget info. */ 2859 TreeItem item /* Item token. */ 2860 ) 2861{ 2862 TreeItem child; 2863 2864 if (IS_ROOT(item)) 2865 return; 2866 if (item->parent != NULL) 2867 item->depth = item->parent->depth + 1; 2868 else 2869 item->depth = 0; 2870 child = item->firstChild; 2871 while (child != NULL) { 2872 TreeItem_UpdateDepth(tree, child); 2873 child = child->nextSibling; 2874 } 2875} 2876 2877/* 2878 *---------------------------------------------------------------------- 2879 * 2880 * TreeItem_AddToParent -- 2881 * 2882 * Called *after* an Item is added as a child to another Item. 2883 * 2884 * Results: 2885 * None. 2886 * 2887 * Side effects: 2888 * Display changes. 2889 * 2890 *---------------------------------------------------------------------- 2891 */ 2892 2893void 2894TreeItem_AddToParent( 2895 TreeCtrl *tree, /* Widget info. */ 2896 TreeItem item /* Item token. */ 2897 ) 2898{ 2899 TreeItem last, parent = item->parent; 2900 2901 /* If this is the new last child, redraw the lines of the previous 2902 * sibling and all of its descendants so the line from the previous 2903 * sibling reaches this item */ 2904 if ((item->prevSibling != NULL) && 2905 (item->nextSibling == NULL) && 2906 tree->showLines && (tree->columnTree != NULL)) { 2907 last = item->prevSibling; 2908 while (last->lastChild != NULL) 2909 last = last->lastChild; 2910 Tree_InvalidateItemDInfo(tree, tree->columnTree, 2911 item->prevSibling, last); 2912 } 2913 2914 /* Redraw the parent if the parent has "-button auto". */ 2915 if (IS_VISIBLE(item) && (parent->flags & ITEM_FLAG_BUTTON_AUTO) && 2916 tree->showButtons && (tree->columnTree != NULL)) { 2917 Tree_InvalidateItemDInfo(tree, tree->columnTree, parent, 2918 NULL); 2919 } 2920 2921 tree->updateIndex = 1; 2922 Tree_DInfoChanged(tree, DINFO_REDO_RANGES); 2923 2924 /* Tree_UpdateItemIndex() also recalcs depth, but in one of my demos 2925 * I retrieve item depth during list creation. Since Tree_UpdateItemIndex() 2926 * is slow I will keep depth up-to-date here. */ 2927 TreeItem_UpdateDepth(tree, item); 2928 2929 Tree_InvalidateColumnWidth(tree, NULL); 2930 2931 if (tree->debug.enable && tree->debug.data) 2932 Tree_Debug(tree); 2933} 2934 2935/* 2936 *---------------------------------------------------------------------- 2937 * 2938 * RemoveFromParentAux -- 2939 * 2940 * Recursively update Item.depth, Item.index and Item.indexVis. 2941 * 2942 * Results: 2943 * None. 2944 * 2945 * Side effects: 2946 * Display changes. 2947 * 2948 *---------------------------------------------------------------------- 2949 */ 2950 2951static void 2952RemoveFromParentAux( 2953 TreeCtrl *tree, /* Widget info. */ 2954 TreeItem item, /* Item being removed. */ 2955 int *index /* New value of Item.index. Is incremented. */ 2956 ) 2957{ 2958 TreeItem child; 2959 2960 /* Invalidate display info. Don't free it because we may just be 2961 * moving the item to a new parent. FIXME: if it is being moved, 2962 * it might not actually need to be redrawn (just copied) */ 2963 if (item->dInfo != NULL) 2964 Tree_InvalidateItemDInfo(tree, NULL, item, NULL); 2965 2966 if (item->parent != NULL) 2967 item->depth = item->parent->depth + 1; 2968 else 2969 item->depth = 0; 2970 item->index = (*index)++; 2971 item->indexVis = -1; 2972 child = item->firstChild; 2973 while (child != NULL) { 2974 RemoveFromParentAux(tree, child, index); 2975 child = child->nextSibling; 2976 } 2977} 2978 2979/* 2980 *---------------------------------------------------------------------- 2981 * 2982 * TreeItem_RemoveFromParent -- 2983 * 2984 * Remove an Item from its parent (if any). 2985 * 2986 * Results: 2987 * None. 2988 * 2989 * Side effects: 2990 * Display changes. 2991 * 2992 *---------------------------------------------------------------------- 2993 */ 2994 2995void 2996TreeItem_RemoveFromParent( 2997 TreeCtrl *tree, /* Widget info. */ 2998 TreeItem item /* Item token. */ 2999 ) 3000{ 3001 TreeItem parent = item->parent; 3002 TreeItem last; 3003 int index = 0; 3004 3005 if (parent == NULL) 3006 return; 3007 3008 /* If this is the last child, redraw the lines of the previous 3009 * sibling and all of its descendants because the line from 3010 * the previous sibling to us is now gone */ 3011 if ((item->prevSibling != NULL) && 3012 (item->nextSibling == NULL) && 3013 tree->showLines && (tree->columnTree != NULL)) { 3014 last = item->prevSibling; 3015 while (last->lastChild != NULL) 3016 last = last->lastChild; 3017 Tree_InvalidateItemDInfo(tree, tree->columnTree, 3018 item->prevSibling, last); 3019 } 3020 3021 /* Redraw the parent if the parent has "-button auto". */ 3022 if (IS_VISIBLE(item) && (parent->flags & ITEM_FLAG_BUTTON_AUTO) && 3023 tree->showButtons && (tree->columnTree != NULL)) { 3024 Tree_InvalidateItemDInfo(tree, tree->columnTree, parent, 3025 NULL); 3026 } 3027 3028 /* 3029 * Set a flag indicating that item indexes are out-of-date. This doesn't 3030 * cover the current item being removed. 3031 */ 3032 tree->updateIndex = 1; 3033 Tree_DInfoChanged(tree, DINFO_REDO_RANGES); 3034 3035 if (item->prevSibling) 3036 item->prevSibling->nextSibling = item->nextSibling; 3037 if (item->nextSibling) 3038 item->nextSibling->prevSibling = item->prevSibling; 3039 if (parent->firstChild == item) { 3040 parent->firstChild = item->nextSibling; 3041 if (!parent->firstChild) 3042 parent->lastChild = NULL; 3043 } 3044 if (parent->lastChild == item) 3045 parent->lastChild = item->prevSibling; 3046 item->prevSibling = item->nextSibling = NULL; 3047 item->parent = NULL; 3048 parent->numChildren--; 3049 3050 /* 3051 * Update Item.depth, Item.index and Item.indexVis for the item and its 3052 * descendants. An up-to-date Item.index is needed for some operations that 3053 * use a range of items, such as [item delete]. 3054 */ 3055 RemoveFromParentAux(tree, item, &index); 3056} 3057 3058/* 3059 *---------------------------------------------------------------------- 3060 * 3061 * TreeItem_RemoveColumns -- 3062 * 3063 * Free a range of Columns in an Item. 3064 * 3065 * Results: 3066 * None. 3067 * 3068 * Side effects: 3069 * Memory is deallocated. 3070 * 3071 *---------------------------------------------------------------------- 3072 */ 3073 3074void 3075TreeItem_RemoveColumns( 3076 TreeCtrl *tree, /* Widget info. */ 3077 TreeItem item, /* Item token. */ 3078 int first, /* 0-based column index at start of 3079 * the range. Must be <= last */ 3080 int last /* 0-based column index at end of 3081 * the range. Must be >= first */ 3082 ) 3083{ 3084 Column *column = item->columns; 3085 Column *prev = NULL, *next = NULL; 3086 int i = 0; 3087 3088 while (column != NULL) { 3089 next = column->next; 3090 if (i == first - 1) 3091 prev = column; 3092 else if (i >= first) 3093 Column_FreeResources(tree, column); 3094 if (i == last) 3095 break; 3096 ++i; 3097 column = next; 3098 } 3099 if (prev != NULL) 3100 prev->next = next; 3101 else if (first == 0) 3102 item->columns = next; 3103} 3104 3105/* 3106 *---------------------------------------------------------------------- 3107 * 3108 * TreeItem_RemoveAllColumns -- 3109 * 3110 * Free all the Columns in an Item. 3111 * 3112 * Results: 3113 * None. 3114 * 3115 * Side effects: 3116 * Memory is deallocated. 3117 * 3118 *---------------------------------------------------------------------- 3119 */ 3120 3121void TreeItem_RemoveAllColumns( 3122 TreeCtrl *tree, /* Widget info. */ 3123 TreeItem item /* Item token. */ 3124 ) 3125{ 3126 Column *column = item->columns; 3127 3128 while (column != NULL) { 3129 Column *next = column->next; 3130 Column_FreeResources(tree, column); 3131 column = next; 3132 } 3133 item->columns = NULL; 3134} 3135 3136/* 3137 *---------------------------------------------------------------------- 3138 * 3139 * Item_CreateColumn -- 3140 * 3141 * Allocate a Column record for an Item if it doesn't already 3142 * exist. 3143 * 3144 * Results: 3145 * Pointer to new or existing Column record. 3146 * 3147 * Side effects: 3148 * Any column records preceding the desired one are allocated 3149 * if they weren't already. Memory is allocated. 3150 * 3151 *---------------------------------------------------------------------- 3152 */ 3153 3154static Column * 3155Item_CreateColumn( 3156 TreeCtrl *tree, /* Widget info. */ 3157 TreeItem item, /* Item to contain the column. */ 3158 int columnIndex, /* 0-based index of new column. */ 3159 int *isNew /* May be NULL. Set to TRUE if the 3160 * column record was created. */ 3161 ) 3162{ 3163 Column *column; 3164 int i; 3165 3166 if (isNew != NULL) (*isNew) = FALSE; 3167 column = item->columns; 3168 if (column == NULL) { 3169 column = Column_Alloc(tree); 3170 item->columns = column; 3171 if (isNew != NULL) (*isNew) = TRUE; 3172 } 3173 for (i = 0; i < columnIndex; i++) { 3174 if (column->next == NULL) { 3175 column->next = Column_Alloc(tree); 3176 if (isNew != NULL) (*isNew) = TRUE; 3177 } 3178 column = column->next; 3179 } 3180 3181 return column; 3182} 3183 3184/* 3185 *---------------------------------------------------------------------- 3186 * 3187 * TreeItem_MoveColumn -- 3188 * 3189 * Rearranges an Item's list of Column records by moving one 3190 * in front of another. 3191 * 3192 * Results: 3193 * If the Column to be moved does not exist and the Column to place it 3194 * in front of does not exist, then nothing happens. If the Column is 3195 * to be moved past all currently allocated Columns, then new 3196 * Column records are allocated. 3197 * 3198 * Side effects: 3199 * Memory may be allocated. 3200 * 3201 *---------------------------------------------------------------------- 3202 */ 3203 3204void 3205TreeItem_MoveColumn( 3206 TreeCtrl *tree, /* Widget info. */ 3207 TreeItem item, /* Item token. */ 3208 int columnIndex, /* 0-based index of the column to move. */ 3209 int beforeIndex /* 0-based index of the second column to move 3210 * the first column to the left of. */ 3211 ) 3212{ 3213 Column *before = NULL, *move = NULL; 3214 Column *prevM = NULL, *prevB = NULL; 3215 Column *last = NULL, *prev, *walk; 3216 int index = 0; 3217 3218 prev = NULL; 3219 walk = item->columns; 3220 while (walk != NULL) { 3221 if (index == columnIndex) { 3222 prevM = prev; 3223 move = walk; 3224 } 3225 if (index == beforeIndex) { 3226 prevB = prev; 3227 before = walk; 3228 } 3229 prev = walk; 3230 if (walk->next == NULL) 3231 last = walk; 3232 index++; 3233 walk = walk->next; 3234 } 3235 3236 if (move == NULL && before == NULL) 3237 return; 3238 if (move == NULL) 3239 move = Column_Alloc(tree); 3240 else { 3241 if (before == NULL) { 3242 prevB = Item_CreateColumn(tree, item, beforeIndex - 1, NULL); 3243 last = prevB; 3244 } 3245 if (prevM == NULL) 3246 item->columns = move->next; 3247 else 3248 prevM->next = move->next; 3249 } 3250 if (before == NULL) { 3251 last->next = move; 3252 move->next = NULL; 3253 } else { 3254 if (prevB == NULL) 3255 item->columns = move; 3256 else 3257 prevB->next = move; 3258 move->next = before; 3259 } 3260} 3261 3262/* 3263 *---------------------------------------------------------------------- 3264 * 3265 * TreeItem_FreeResources -- 3266 * 3267 * Free memory etc assocated with an Item. 3268 * 3269 * Results: 3270 * None. 3271 * 3272 * Side effects: 3273 * Memory is deallocated. Display changes. 3274 * 3275 *---------------------------------------------------------------------- 3276 */ 3277 3278void 3279TreeItem_FreeResources( 3280 TreeCtrl *tree, /* Widget info. */ 3281 TreeItem item /* Item token. */ 3282 ) 3283{ 3284 Column *column; 3285 3286 column = item->columns; 3287 while (column != NULL) 3288 column = Column_FreeResources(tree, column); 3289 if (item->dInfo != NULL) 3290 Tree_FreeItemDInfo(tree, item, NULL); 3291 if (item->rInfo != NULL) 3292 Tree_FreeItemRInfo(tree, item); 3293 if (item->spans != NULL) 3294 ckfree((char *) item->spans); 3295 Tk_FreeConfigOptions((char *) item, tree->itemOptionTable, tree->tkwin); 3296 3297 /* Add the item record to the "preserved" list. It will be freed later. */ 3298 TreeItemList_Append(&tree->preserveItemList, item); 3299} 3300 3301/* 3302 *---------------------------------------------------------------------- 3303 * 3304 * TreeItem_Release -- 3305 * 3306 * Finally free an item record when it is no longer needed. 3307 * 3308 * Results: 3309 * None. 3310 * 3311 * Side effects: 3312 * Memory is deallocated. 3313 * 3314 *---------------------------------------------------------------------- 3315 */ 3316 3317void 3318TreeItem_Release( 3319 TreeCtrl *tree, /* Widget info. */ 3320 TreeItem item /* Item token. */ 3321 ) 3322{ 3323#ifdef ALLOC_HAX 3324 TreeAlloc_Free(tree->allocData, ItemUid, (char *) item, sizeof(TreeItem_)); 3325#else 3326 WFREE(item, TreeItem_); 3327#endif 3328} 3329 3330/* 3331 *---------------------------------------------------------------------- 3332 * 3333 * Item_UseHeight -- 3334 * 3335 * Return the height used by styles in an Item. 3336 * 3337 * Results: 3338 * Maximum height in pixels of the style in each visible Column. 3339 * 3340 * Side effects: 3341 * None. 3342 * 3343 *---------------------------------------------------------------------- 3344 */ 3345 3346static int 3347Item_HeightOfStyles( 3348 TreeCtrl *tree, /* Widget info. */ 3349 TreeItem item /* Item record. */ 3350 ) 3351{ 3352 Column *column = item->columns; 3353 TreeColumn treeColumn = tree->columns; 3354 StyleDrawArgs drawArgs; 3355 int height = 0; 3356 3357 drawArgs.tree = tree; 3358 3359 while (column != NULL) { 3360 if (TreeColumn_Visible(treeColumn) && (column->style != NULL)) { 3361 drawArgs.state = item->state | column->cstate; 3362 drawArgs.style = column->style; 3363 drawArgs.indent = (treeColumn == tree->columnTree) ? 3364 TreeItem_Indent(tree, item) : 0; 3365 if ((TreeColumn_FixedWidth(treeColumn) != -1) || 3366 TreeColumn_Squeeze(treeColumn)) { 3367 drawArgs.width = TreeColumn_UseWidth(treeColumn); 3368 } else 3369 drawArgs.width = -1; 3370 height = MAX(height, TreeStyle_UseHeight(&drawArgs)); 3371 } 3372 treeColumn = TreeColumn_Next(treeColumn); 3373 column = column->next; 3374 } 3375 3376 return height; 3377} 3378 3379/* 3380 *---------------------------------------------------------------------- 3381 * 3382 * TreeItem_Height -- 3383 * 3384 * Return the height of an Item. 3385 * 3386 * Results: 3387 * If the Item -height option is > 0, the result is the maximum 3388 * of the button height (if a button is displayed) and the -height 3389 * option. 3390 * If the TreeCtrl -itemheight option is > 0, the result is the maximum 3391 * of the button height (if a button is displayed) and the -itemheight 3392 * option. 3393 * Otherwise the result is the maximum of the button height (if a button 3394 * is displayed) AND the TreeCtrl -minitemheight AND the height of 3395 * the style in each visible Column. 3396 * 3397 * Side effects: 3398 * None. 3399 * 3400 *---------------------------------------------------------------------- 3401 */ 3402 3403int TreeItem_Height( 3404 TreeCtrl *tree, /* Widget info. */ 3405 TreeItem item /* Item token. */ 3406 ) 3407{ 3408 int buttonHeight = 0; 3409 int useHeight; 3410 3411 if (!IS_VISIBLE(item) || (IS_ROOT(item) && !tree->showRoot)) 3412 return 0; 3413 3414 /* Get requested height of the style in each column */ 3415 useHeight = Item_HeightOfStyles(tree, item); 3416 3417 /* Can't have less height than our button */ 3418 if (TreeItem_HasButton(tree, item)) { 3419 buttonHeight = Tree_ButtonHeight(tree, item->state); 3420 } 3421 3422 /* User specified a fixed height for this item */ 3423 if (item->fixedHeight > 0) 3424 return MAX(item->fixedHeight, buttonHeight); 3425 3426 /* Fixed height of all items */ 3427 if (tree->itemHeight > 0) 3428 return MAX(tree->itemHeight, buttonHeight); 3429 3430 /* Minimum height of all items */ 3431 if (tree->minItemHeight > 0) 3432 useHeight = MAX(useHeight, tree->minItemHeight); 3433 3434 /* No fixed height specified */ 3435 return MAX(useHeight, buttonHeight); 3436} 3437 3438/* 3439 *---------------------------------------------------------------------- 3440 * 3441 * TreeItem_InvalidateHeight -- 3442 * 3443 * Marks Item.neededHeight out-of-date. 3444 * NOTE: Item.neededHeight is unused. 3445 * 3446 * Results: 3447 * None. 3448 * 3449 * Side effects: 3450 * None. 3451 * 3452 *---------------------------------------------------------------------- 3453 */ 3454 3455void 3456TreeItem_InvalidateHeight( 3457 TreeCtrl *tree, /* Widget info. */ 3458 TreeItem item /* Item token. */ 3459 ) 3460{ 3461} 3462 3463/* 3464 *---------------------------------------------------------------------- 3465 * 3466 * Item_FindColumn -- 3467 * 3468 * Return a Column record given a zero-based index. 3469 * 3470 * Results: 3471 * The Column record or NULL. 3472 * 3473 * Side effects: 3474 * None. 3475 * 3476 *---------------------------------------------------------------------- 3477 */ 3478 3479static Column * 3480Item_FindColumn( 3481 TreeCtrl *tree, /* Widget info. */ 3482 TreeItem item, /* Item record. */ 3483 int columnIndex /* 0-based index of column to find. */ 3484 ) 3485{ 3486 Column *column; 3487 int i = 0; 3488 3489 column = item->columns; 3490 if (!column) 3491 return NULL; 3492 while (column != NULL && i < columnIndex) { 3493 column = column->next; 3494 i++; 3495 } 3496 return column; 3497} 3498 3499/* 3500 *---------------------------------------------------------------------- 3501 * 3502 * Item_FindColumnFromObj -- 3503 * 3504 * Return a Column record given a Tcl_Obj column description. 3505 * 3506 * Results: 3507 * TCL_OK or TCL_ERROR. 3508 * 3509 * Side effects: 3510 * None. 3511 * 3512 *---------------------------------------------------------------------- 3513 */ 3514 3515static int 3516Item_FindColumnFromObj( 3517 TreeCtrl *tree, /* Widget info. */ 3518 TreeItem item, /* Item record. */ 3519 Tcl_Obj *obj, /* Column description. */ 3520 Column **columnPtr, /* Returned column, or NULL. */ 3521 int *indexPtr /* May be NULL. Returned 0-based index of 3522 * the column. */ 3523 ) 3524{ 3525 TreeColumn treeColumn; 3526 int columnIndex; 3527 3528 if (TreeColumn_FromObj(tree, obj, &treeColumn, CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) 3529 return TCL_ERROR; 3530 columnIndex = TreeColumn_Index(treeColumn); 3531 (*columnPtr) = Item_FindColumn(tree, item, columnIndex); 3532 if (indexPtr != NULL) 3533 (*indexPtr) = columnIndex; 3534 return TCL_OK; 3535} 3536 3537/* 3538 *---------------------------------------------------------------------- 3539 * 3540 * TreeItem_FindColumn -- 3541 * 3542 * Return an item-column token given a zero-based index. 3543 * 3544 * Results: 3545 * The item-column token or NULL. 3546 * 3547 * Side effects: 3548 * None. 3549 * 3550 *---------------------------------------------------------------------- 3551 */ 3552 3553TreeItemColumn 3554TreeItem_FindColumn( 3555 TreeCtrl *tree, /* Widget info. */ 3556 TreeItem item, /* Item token. */ 3557 int columnIndex /* 0-based index of column to find. */ 3558 ) 3559{ 3560 return (TreeItemColumn) Item_FindColumn(tree, item, columnIndex); 3561} 3562 3563/* 3564 *---------------------------------------------------------------------- 3565 * 3566 * TreeItem_ColumnFromObj -- 3567 * 3568 * Return an item-column token given a Tcl_Obj column description. 3569 * 3570 * Results: 3571 * TCL_OK or TCL_ERROR. 3572 * 3573 * Side effects: 3574 * None. 3575 * 3576 *---------------------------------------------------------------------- 3577 */ 3578 3579int 3580TreeItem_ColumnFromObj( 3581 TreeCtrl *tree, /* Widget info. */ 3582 TreeItem item, /* Item token. */ 3583 Tcl_Obj *obj, /* Column description. */ 3584 TreeItemColumn *columnPtr, /* Returned column, or NULL. */ 3585 int *indexPtr /* May be NULL. Returned 0-based index of 3586 * the column. */ 3587 ) 3588{ 3589 return Item_FindColumnFromObj(tree, item, obj, (Column **) columnPtr, indexPtr); 3590} 3591 3592#if 0 3593/* 3594 *---------------------------------------------------------------------- 3595 * 3596 * Item_CreateColumnFromObj -- 3597 * 3598 * Return a Column record given a Tcl_Obj column description. 3599 * The Column record is allocated if it doesn't exist. 3600 * 3601 * Results: 3602 * TCL_OK or TCL_ERROR. 3603 * 3604 * Side effects: 3605 * Memory may be allocated. 3606 * 3607 *---------------------------------------------------------------------- 3608 */ 3609 3610static int 3611Item_CreateColumnFromObj( 3612 TreeCtrl *tree, /* Widget info. */ 3613 TreeItem item, /* Item record. */ 3614 Tcl_Obj *obj, /* Column description. */ 3615 Column **column, /* Returned column. */ 3616 int *indexPtr, /* May be NULL. Returned 0-based index of 3617 * the column. */ 3618 int *isNew /* May be NULL. Set to TRUE if the 3619 * column record was created. */ 3620 ) 3621{ 3622 TreeColumn treeColumn; 3623 int columnIndex; 3624 3625 if (TreeColumn_FromObj(tree, obj, &treeColumn, CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) 3626 return TCL_ERROR; 3627 columnIndex = TreeColumn_Index(treeColumn); 3628 (*column) = Item_CreateColumn(tree, item, columnIndex, isNew); 3629 if (indexPtr != NULL) 3630 (*indexPtr) = columnIndex; 3631 return TCL_OK; 3632} 3633#endif 3634 3635/* 3636 *---------------------------------------------------------------------- 3637 * 3638 * TreeItem_Indent -- 3639 * 3640 * Return the amount of indentation for the given item. This is 3641 * the width of the buttons/lines. 3642 * 3643 * Results: 3644 * Pixel value >= 0. 3645 * 3646 * Side effects: 3647 * None. 3648 * 3649 *---------------------------------------------------------------------- 3650 */ 3651 3652int TreeItem_Indent( 3653 TreeCtrl *tree, /* Widget info. */ 3654 TreeItem item /* Item token. */ 3655 ) 3656{ 3657 int depth; 3658 3659 if (IS_ROOT(item)) 3660 return (tree->showRoot && tree->showButtons && tree->showRootButton) 3661 ? tree->useIndent : 0; 3662 3663 Tree_UpdateItemIndex(tree); 3664 3665 depth = item->depth; 3666 if (tree->showRoot) 3667 { 3668 depth += 1; 3669 if (tree->showButtons && tree->showRootButton) 3670 depth += 1; 3671 } 3672 else if (tree->showButtons && tree->showRootChildButtons) 3673 depth += 1; 3674 else if (tree->showLines && tree->showRootLines) 3675 depth += 1; 3676 3677 return tree->useIndent * depth; 3678} 3679 3680/* 3681 *---------------------------------------------------------------------- 3682 * 3683 * ItemDrawBackground -- 3684 * 3685 * Draws part of the background area of an Item. The area is 3686 * erased to the -itembackground color of the tree column or the 3687 * TreeCtrl -background color. If the TreeCtrl -backgroundimage 3688 * option is specified then that image is tiled over the given area. 3689 * 3690 * Results: 3691 * None. 3692 * 3693 * Side effects: 3694 * Stuff is drawn in a drawable. 3695 * 3696 *---------------------------------------------------------------------- 3697 */ 3698 3699static void 3700ItemDrawBackground( 3701 TreeCtrl *tree, /* Widget info. */ 3702 TreeColumn treeColumn, /* Tree-column token. */ 3703 TreeItem item, /* Item record. */ 3704 Column *column, /* First column. */ 3705 TreeDrawable drawable, /* Where to draw. */ 3706 int x, int y, /* Area of the item to draw. */ 3707 int width, int height, /* ^ */ 3708 int index /* Used to select a color from the 3709 * tree-column's -itembackground option. */ 3710 ) 3711{ 3712 GC gc = None; 3713 3714 gc = TreeColumn_BackgroundGC(treeColumn, index); 3715 if (gc == None) 3716 gc = Tk_3DBorderGC(tree->tkwin, tree->border, TK_3D_FLAT_GC); 3717 /* 3718 * FIXME: If the background image is non-transparent, there is no 3719 * need to erase first. 3720 */ 3721 XFillRectangle(tree->display, drawable.drawable, gc, x, y, width, height); 3722 if (tree->backgroundImage != NULL) { 3723 Tree_DrawTiledImage(tree, drawable.drawable, tree->backgroundImage, x, y, 3724 x + width, y + height, 3725 tree->drawableXOrigin, tree->drawableYOrigin); 3726 } 3727} 3728 3729/* 3730 *---------------------------------------------------------------------- 3731 * 3732 * TreeItem_SpansInvalidate -- 3733 * 3734 * Invalidates the Item.spans field of one or all items. 3735 * 3736 * Results: 3737 * The item(s) are removed from the TreeCtrl.itemSpansHash to 3738 * indicate that the list of spans must be recalculated. 3739 * 3740 * Side effects: 3741 * None. 3742 * 3743 *---------------------------------------------------------------------- 3744 */ 3745 3746void 3747TreeItem_SpansInvalidate( 3748 TreeCtrl *tree, /* Widget info. */ 3749 TreeItem item /* Item token. NULL for all items. */ 3750 ) 3751{ 3752 Tcl_HashEntry *hPtr; 3753 Tcl_HashSearch search; 3754 int count = 0; 3755 3756 if (item == NULL) { 3757 hPtr = Tcl_FirstHashEntry(&tree->itemSpansHash, &search); 3758 while (hPtr != NULL) { 3759 item = (TreeItem) Tcl_GetHashKey(&tree->itemSpansHash, hPtr); 3760 item->flags &= ~ITEM_FLAG_SPANS_VALID; 3761 count++; 3762 hPtr = Tcl_NextHashEntry(&search); 3763 } 3764 if (count) { 3765 Tcl_DeleteHashTable(&tree->itemSpansHash); 3766 Tcl_InitHashTable(&tree->itemSpansHash, TCL_ONE_WORD_KEYS); 3767 } 3768 } else if (item->flags & ITEM_FLAG_SPANS_VALID) { 3769 hPtr = Tcl_FindHashEntry(&tree->itemSpansHash, (char *) item); 3770 Tcl_DeleteHashEntry(hPtr); 3771 item->flags &= ~ITEM_FLAG_SPANS_VALID; 3772 count++; 3773 } 3774 3775 if (count && tree->debug.enable && tree->debug.span) 3776 dbwin("TreeItem_SpansInvalidate forgot %d items\n", count); 3777} 3778 3779/* 3780 *---------------------------------------------------------------------- 3781 * 3782 * TreeItem_SpansRedo -- 3783 * 3784 * Updates the Item.spans field of an item. 3785 * 3786 * Results: 3787 * Item.spans is resized if needed to (at least) the current number 3788 * of tree columns. For tree column N, the index of the item 3789 * column displayed there is written to spans[N]. 3790 * 3791 * The return value is 1 if every span is 1, otherwise 0. 3792 * 3793 * Side effects: 3794 * Memory may be allocated. 3795 * 3796 *---------------------------------------------------------------------- 3797 */ 3798 3799int 3800TreeItem_SpansRedo( 3801 TreeCtrl *tree, /* Widget info. */ 3802 TreeItem item /* Item token. */ 3803 ) 3804{ 3805 TreeColumn treeColumn = tree->columns; 3806 Column *itemColumn = item->columns; 3807 int columnIndex = 0, spanner = 0, span = 1, simple = TRUE; 3808 int lock = TreeColumn_Lock(treeColumn); 3809 3810 if (tree->debug.enable && tree->debug.span) 3811 dbwin("TreeItem_SpansRedo item %d\n", item->id); 3812 3813 if (item->spans == NULL) { 3814 item->spans = (int *) ckalloc(sizeof(int) * tree->columnCount); 3815 item->spanAlloc = tree->columnCount; 3816 } else if (item->spanAlloc < tree->columnCount) { 3817 item->spans = (int *) ckrealloc((char *) item->spans, 3818 sizeof(int) * tree->columnCount); 3819 item->spanAlloc = tree->columnCount; 3820 } 3821 3822 while (treeColumn != NULL) { 3823 /* End current span if column lock changes. */ 3824 if (TreeColumn_Lock(treeColumn) != lock) { 3825 lock = TreeColumn_Lock(treeColumn); 3826 span = 1; 3827 } 3828 if (--span == 0) { 3829 if (TreeColumn_Visible(treeColumn)) 3830 span = itemColumn ? itemColumn->span : 1; 3831 else 3832 span = 1; 3833 if (span > 1) 3834 simple = FALSE; 3835 spanner = columnIndex; 3836 } 3837 item->spans[columnIndex] = spanner; 3838 columnIndex++; 3839 treeColumn = TreeColumn_Next(treeColumn); 3840 if (itemColumn != NULL) 3841 itemColumn = itemColumn->next; 3842 } 3843 3844 return simple; 3845} 3846 3847/* 3848 *---------------------------------------------------------------------- 3849 * 3850 * TreeItem_SpansRedoIfNeeded -- 3851 * 3852 * Updates the Item.spans field of an item if needed. 3853 * 3854 * Results: 3855 * If all spans are known to be 1, nothing is done. If the list of 3856 * spans is marked valid, nothing is done. Otherwise the list of 3857 * spans is recalculated; if any span is > 1 the item is added 3858 * to TreeCtrl.itemSpansHash. 3859 * 3860 * Side effects: 3861 * Memory may be allocated. 3862 * 3863 *---------------------------------------------------------------------- 3864 */ 3865 3866void 3867TreeItem_SpansRedoIfNeeded( 3868 TreeCtrl *tree, 3869 TreeItem item 3870 ) 3871{ 3872 /* All the spans are 1. */ 3873 if (item->flags & ITEM_FLAG_SPANS_SIMPLE) 3874 return; 3875 3876 /* Some spans > 1, but we calculated them already. */ 3877 if (item->flags & ITEM_FLAG_SPANS_VALID) 3878 return; 3879 3880 if (TreeItem_SpansRedo(tree, item)) { 3881 /* Reverted to all spans=1. */ 3882 item->flags |= ITEM_FLAG_SPANS_SIMPLE; 3883 } else { 3884 int isNew; 3885 Tcl_HashEntry *hPtr; 3886 3887 hPtr = Tcl_CreateHashEntry(&tree->itemSpansHash, (char *) item, &isNew); 3888 Tcl_SetHashValue(hPtr, (ClientData) item); 3889 item->flags |= ITEM_FLAG_SPANS_VALID; 3890 } 3891} 3892 3893/* 3894 *---------------------------------------------------------------------- 3895 * 3896 * TreeItem_GetSpans -- 3897 * 3898 * Returns the spans[] array for an item. 3899 * 3900 * Results: 3901 * If all spans are known to be 1, the result is NULL. Otherwise the 3902 * list of spans is returned. 3903 * 3904 * Side effects: 3905 * Memory may be allocated. 3906 * 3907 *---------------------------------------------------------------------- 3908 */ 3909 3910int * 3911TreeItem_GetSpans( 3912 TreeCtrl *tree, /* Widget info. */ 3913 TreeItem item /* Item token. */ 3914 ) 3915{ 3916 TreeItem_SpansRedoIfNeeded(tree, item); 3917 if (item->flags & ITEM_FLAG_SPANS_SIMPLE) 3918 return NULL; 3919 return item->spans; 3920} 3921 3922/* 3923 * The following structure holds information about which item column 3924 * is displayed at a given tree column. 3925 */ 3926typedef struct SpanInfo { 3927 TreeColumn treeColumn; /* Always non-null. */ 3928 TreeItemColumn itemColumn; /* May be null. */ 3929 int span; /* Number of tree-columns spanned. */ 3930 int width; /* Width of the span. */ 3931} SpanInfo; 3932 3933/* 3934 *---------------------------------------------------------------------- 3935 * 3936 * Item_GetSpans -- 3937 * 3938 * Fills an array of SpanInfo records, one per visible span. 3939 * 3940 * Results: 3941 * The return value is the number of SpanInfo records written. 3942 * 3943 * Side effects: 3944 * None. 3945 * 3946 *---------------------------------------------------------------------- 3947 */ 3948 3949static int 3950Item_GetSpans( 3951 TreeCtrl *tree, /* Widget info. */ 3952 TreeItem item, /* Item token. */ 3953 TreeColumn firstColumn, /* Which columns. */ 3954 SpanInfo spans[] /* Returned span records. */ 3955 ) 3956{ 3957 TreeColumn treeColumn = firstColumn; 3958 int columnIndex = TreeColumn_Index(firstColumn); 3959 Column *column = Item_FindColumn(tree, item, columnIndex); 3960 int spanCount = 0, span = 1; 3961 SpanInfo *spanPtr = NULL; 3962 3963 while (treeColumn != NULL) { 3964 if (TreeColumn_Lock(treeColumn) != TreeColumn_Lock(firstColumn)) 3965 break; 3966 if (--span == 0) { 3967 if (TreeColumn_Visible(treeColumn)) { 3968 span = column ? column->span : 1; 3969 if (spanPtr == NULL) 3970 spanPtr = spans; 3971 else 3972 spanPtr++; 3973 spanPtr->treeColumn = treeColumn; 3974 spanPtr->itemColumn = (TreeItemColumn) column; 3975 spanPtr->span = 0; 3976 spanPtr->width = 0; 3977 spanCount++; 3978 } else { 3979 span = 1; 3980 goto next; 3981 } 3982 } 3983 spanPtr->span++; 3984 spanPtr->width += TreeColumn_UseWidth(treeColumn); 3985next: 3986 ++columnIndex; 3987 treeColumn = TreeColumn_Next(treeColumn); 3988 if (column != NULL) 3989 column = column->next; 3990 } 3991 return spanCount; 3992} 3993 3994typedef int (*TreeItemWalkSpansProc)( 3995 TreeCtrl *tree, 3996 TreeItem item, 3997 SpanInfo *spanPtr, 3998 StyleDrawArgs *drawArgs, 3999 ClientData clientData 4000 ); 4001 4002/* 4003 *---------------------------------------------------------------------- 4004 * 4005 * TreeItem_WalkSpans -- 4006 * 4007 * Iterates over the spans of an item and calls a callback routine 4008 * for each span of non-zero width. This is used for drawing, 4009 * hit-testing and other purposes. 4010 * 4011 * Results: 4012 * None. 4013 * 4014 * Side effects: 4015 * None. 4016 * 4017 *---------------------------------------------------------------------- 4018 */ 4019 4020static void 4021TreeItem_WalkSpans( 4022 TreeCtrl *tree, /* Widget info. */ 4023 TreeItem item, /* Item token. */ 4024 int lock, /* Which columns. */ 4025 int x, int y, /* Drawable coordinates of the item. */ 4026 int width, int height, /* Total size of the item. */ 4027 TreeItemWalkSpansProc proc, /* Callback routine. */ 4028 ClientData clientData /* Data passed to callback routine. */ 4029 ) 4030{ 4031 int columnWidth, totalWidth; 4032 Column *itemColumn; 4033 StyleDrawArgs drawArgs; 4034 TreeColumn treeColumn = tree->columnLockNone; 4035 int spanCount, spanIndex, columnCount = tree->columnCountVis; 4036 SpanInfo staticSpans[STATIC_SIZE], *spans = staticSpans; 4037 int area = TREE_AREA_CONTENT; 4038 4039 switch (lock) { 4040 case COLUMN_LOCK_LEFT: 4041 treeColumn = tree->columnLockLeft; 4042 columnCount = tree->columnCountVisLeft; 4043 area = TREE_AREA_LEFT; 4044 break; 4045 case COLUMN_LOCK_NONE: 4046 break; 4047 case COLUMN_LOCK_RIGHT: 4048 treeColumn = tree->columnLockRight; 4049 columnCount = tree->columnCountVisRight; 4050 area = TREE_AREA_RIGHT; 4051 break; 4052 } 4053 4054 if (!Tree_AreaBbox(tree, area, &drawArgs.bounds[0], &drawArgs.bounds[1], 4055 &drawArgs.bounds[2], &drawArgs.bounds[3])) { 4056 drawArgs.bounds[0] = drawArgs.bounds[1] = 4057 drawArgs.bounds[2] = drawArgs.bounds[3] = 0; 4058 } 4059 4060 STATIC_ALLOC(spans, SpanInfo, columnCount); 4061 spanCount = Item_GetSpans(tree, item, treeColumn, spans); 4062 4063 drawArgs.tree = tree; 4064 drawArgs.td.drawable = None; 4065 4066 totalWidth = 0; 4067 for (spanIndex = 0; spanIndex < spanCount; spanIndex++) { 4068 treeColumn = spans[spanIndex].treeColumn; 4069 itemColumn = (Column *) spans[spanIndex].itemColumn; 4070 4071 /* If this is the single visible column, use the provided width which 4072 * may be different than the column's width. */ 4073 if ((tree->columnCountVis == 1) && (treeColumn == tree->columnVis)) { 4074 columnWidth = width; 4075 4076 /* More than one column is visible, or this is not the visible 4077 * column. */ 4078 } else { 4079 columnWidth = spans[spanIndex].width; 4080 } 4081 if (columnWidth <= 0) 4082 continue; 4083 4084 if (itemColumn != NULL) { 4085 drawArgs.state = item->state | itemColumn->cstate; 4086 drawArgs.style = itemColumn->style; /* may be NULL */ 4087 } else { 4088 drawArgs.state = item->state; 4089 drawArgs.style = NULL; 4090 } 4091 if (treeColumn == tree->columnTree) 4092 drawArgs.indent = TreeItem_Indent(tree, item); 4093 else 4094 drawArgs.indent = 0; 4095 drawArgs.x = x + totalWidth; 4096 drawArgs.y = y; 4097 drawArgs.width = columnWidth; 4098 drawArgs.height = height; 4099 drawArgs.justify = TreeColumn_ItemJustify(treeColumn); 4100 if ((*proc)(tree, item, &spans[spanIndex], &drawArgs, clientData)) 4101 break; 4102 4103 totalWidth += columnWidth; 4104 } 4105 4106 STATIC_FREE(spans, SpanInfo, columnCount); 4107} 4108 4109/* 4110 *---------------------------------------------------------------------- 4111 * 4112 * SpanWalkProc_Draw -- 4113 * 4114 * Callback routine to TreeItem_WalkSpans for TreeItem_Draw. 4115 * 4116 * Results: 4117 * None. 4118 * 4119 * Side effects: 4120 * Stuff is drawn in a drawable. 4121 * 4122 *---------------------------------------------------------------------- 4123 */ 4124 4125static int 4126SpanWalkProc_Draw( 4127 TreeCtrl *tree, 4128 TreeItem item, 4129 SpanInfo *spanPtr, 4130 StyleDrawArgs *drawArgs, 4131 ClientData clientData 4132 ) 4133{ 4134 TreeColumn treeColumn = spanPtr->treeColumn; 4135 Column *itemColumn = (Column *) spanPtr->itemColumn; 4136 int i, x; 4137 struct { 4138 TreeDrawable td; 4139 int minX; 4140 int maxX; 4141 int index; 4142 } *data = clientData; 4143 4144 /* Draw nothing if the entire span is out-of-bounds. */ 4145 if ((drawArgs->x >= data->maxX) || 4146 (drawArgs->x + drawArgs->width <= data->minX)) 4147 return 0; 4148 4149 drawArgs->td = data->td; 4150 4151 /* Draw background colors. */ 4152 if (spanPtr->span == 1) { 4153 /* Important point: use drawArgs->width since an item's width may 4154 * be totally different than tree->columnVis' width. */ 4155 ItemDrawBackground(tree, treeColumn, item, itemColumn, 4156 drawArgs->td, drawArgs->x, drawArgs->y, 4157 drawArgs->width, drawArgs->height, data->index); 4158 } else { 4159 x = drawArgs->x; 4160 for (i = 0; i < spanPtr->span; i++) { 4161 int columnWidth = TreeColumn_UseWidth(treeColumn); 4162 if ((columnWidth > 0) && (x < data->maxX) && 4163 (x + columnWidth > data->minX)) { 4164 ItemDrawBackground(tree, treeColumn, item, itemColumn, 4165 drawArgs->td, x, drawArgs->y, 4166 columnWidth, drawArgs->height, data->index); 4167 } 4168 x += columnWidth; 4169 treeColumn = TreeColumn_Next(treeColumn); 4170 } 4171 } 4172 4173 if (drawArgs->style != NULL) { 4174 StyleDrawArgs drawArgsCopy = *drawArgs; 4175 TreeStyle_Draw(&drawArgsCopy); 4176 } 4177 4178 if (spanPtr->treeColumn == tree->columnTree) { 4179 if (tree->showLines) 4180 TreeItem_DrawLines(tree, item, drawArgs->x, drawArgs->y, 4181 drawArgs->width, drawArgs->height, data->td); 4182 if (tree->showButtons) 4183 TreeItem_DrawButton(tree, item, drawArgs->x, drawArgs->y, 4184 drawArgs->width, drawArgs->height, data->td); 4185 } 4186 4187 /* Stop walking if we went past the right edge of the dirty area. */ 4188 return drawArgs->x + drawArgs->width >= data->maxX; 4189} 4190 4191/* 4192 *---------------------------------------------------------------------- 4193 * 4194 * TreeItem_Draw -- 4195 * 4196 * Draws part of an Item. 4197 * 4198 * Results: 4199 * None. 4200 * 4201 * Side effects: 4202 * Stuff is drawn in a drawable. 4203 * 4204 *---------------------------------------------------------------------- 4205 */ 4206 4207void 4208TreeItem_Draw( 4209 TreeCtrl *tree, /* Widget info. */ 4210 TreeItem item, /* Item token. */ 4211 int lock, /* Which columns. */ 4212 int x, int y, /* Drawable coordinates of the item. */ 4213 int width, int height, /* Total size of the item. */ 4214 TreeDrawable td, /* Where to draw. */ 4215 int minX, int maxX, /* Left/right edge that needs to be drawn. */ 4216 int index /* Used to select a color from a 4217 * tree-column's -itembackground option. */ 4218 ) 4219{ 4220 struct { 4221 TreeDrawable td; 4222 int minX; 4223 int maxX; 4224 int index; 4225 } clientData; 4226 4227 clientData.td = td; 4228 clientData.minX = minX; 4229 clientData.maxX = maxX; 4230 clientData.index = index; 4231 4232 TreeItem_WalkSpans(tree, item, lock, 4233 x, y, width, height, 4234 SpanWalkProc_Draw, (ClientData) &clientData); 4235} 4236 4237/* 4238 *---------------------------------------------------------------------- 4239 * 4240 * TreeItem_DrawLines -- 4241 * 4242 * Draws horizontal and vertical lines indicating parent-child 4243 * relationship in an item. 4244 * 4245 * Results: 4246 * None. 4247 * 4248 * Side effects: 4249 * Stuff is drawn in a drawable. 4250 * 4251 *---------------------------------------------------------------------- 4252 */ 4253 4254void 4255TreeItem_DrawLines( 4256 TreeCtrl *tree, /* Widget info. */ 4257 TreeItem item, /* Item token. */ 4258 int x, int y, /* Drawable coordinates of columnTree. */ 4259 int width, int height, /* Total size of columnTree. */ 4260 TreeDrawable td /* Where to draw. */ 4261 ) 4262{ 4263 TreeItem parent, walk; 4264 int indent, left, lineLeft, lineTop; 4265 int hasPrev, hasNext; 4266 int i, vert = 0; 4267 4268 indent = TreeItem_Indent(tree, item); 4269 4270 /* Left edge of button/line area */ 4271 left = x /* + tree->columnTreeLeft */ + indent - tree->useIndent; 4272 4273 /* Left edge of vertical line */ 4274 lineLeft = left + (tree->useIndent - tree->lineThickness) / 2; 4275 4276 /* Top edge of horizontal line */ 4277 lineTop = y + (height - tree->lineThickness) / 2; 4278 4279 /* NOTE: The next three checks do not call TreeItem_ReallyVisible() 4280 * since 'item' is ReallyVisible */ 4281 4282 /* Check for ReallyVisible previous sibling */ 4283 walk = item->prevSibling; 4284 while ((walk != NULL) && !IS_VISIBLE(walk)) 4285 walk = walk->prevSibling; 4286 hasPrev = (walk != NULL); 4287 4288 /* Check for ReallyVisible parent */ 4289 if ((item->parent != NULL) && (!IS_ROOT(item->parent) || tree->showRoot)) 4290 hasPrev = TRUE; 4291 4292 /* Check for ReallyVisible next sibling */ 4293 walk = item->nextSibling; 4294 while ((walk != NULL) && !IS_VISIBLE(walk)) 4295 walk = walk->nextSibling; 4296 hasNext = (walk != NULL); 4297 4298 /* Option: Don't connect children of root item */ 4299 if ((item->parent != NULL) && IS_ROOT(item->parent) && !tree->showRootLines) 4300 hasPrev = hasNext = FALSE; 4301 4302 /* Vertical line to parent and/or previous/next sibling */ 4303 if (hasPrev || hasNext) { 4304 int top = y, bottom = y + height; 4305 4306 if (!hasPrev) 4307 top = lineTop; 4308 if (!hasNext) 4309 bottom = lineTop + tree->lineThickness; 4310 4311 if (tree->lineStyle == LINE_STYLE_DOT) { 4312 for (i = 0; i < tree->lineThickness; i++) 4313 Tree_VDotLine(tree, td.drawable, tree->lineGC, 4314 lineLeft + i, 4315 top, 4316 bottom); 4317 } else 4318 XFillRectangle(tree->display, td.drawable, tree->lineGC, 4319 lineLeft, 4320 top, 4321 tree->lineThickness, 4322 bottom - top); 4323 4324 /* Don't overlap horizontal line */ 4325 vert = tree->lineThickness; 4326 } 4327 4328 /* Horizontal line to self */ 4329 if (hasPrev || hasNext) { 4330 if (tree->lineStyle == LINE_STYLE_DOT) { 4331 for (i = 0; i < tree->lineThickness; i++) 4332 Tree_HDotLine(tree, td.drawable, tree->lineGC, 4333 lineLeft + vert, 4334 lineTop + i, 4335 x /* + tree->columnTreeLeft */ + indent); 4336 } else 4337 XFillRectangle(tree->display, td.drawable, tree->lineGC, 4338 lineLeft + vert, 4339 lineTop, 4340 left + tree->useIndent - (lineLeft + vert), 4341 tree->lineThickness); 4342 } 4343 4344 /* Vertical lines from ancestors to their next siblings */ 4345 for (parent = item->parent; 4346 parent != NULL; 4347 parent = parent->parent) { 4348 lineLeft -= tree->useIndent; 4349 4350 /* Option: Don't connect children of root item */ 4351 if ((parent->parent != NULL) && IS_ROOT(parent->parent) && !tree->showRootLines) 4352 continue; 4353 4354 /* Check for ReallyVisible next sibling */ 4355 item = parent->nextSibling; 4356 while ((item != NULL) && !IS_VISIBLE(item)) 4357 item = item->nextSibling; 4358 4359 if (item != NULL) { 4360 if (tree->lineStyle == LINE_STYLE_DOT) { 4361 for (i = 0; i < tree->lineThickness; i++) 4362 Tree_VDotLine(tree, td.drawable, tree->lineGC, 4363 lineLeft + i, 4364 y, 4365 y + height); 4366 } else 4367 XFillRectangle(tree->display, td.drawable, tree->lineGC, 4368 lineLeft, 4369 y, 4370 tree->lineThickness, 4371 height); 4372 } 4373 } 4374} 4375 4376/* 4377 *---------------------------------------------------------------------- 4378 * 4379 * TreeItem_DrawButton -- 4380 * 4381 * Draws the button (if any) in an item. 4382 * 4383 * Results: 4384 * None. 4385 * 4386 * Side effects: 4387 * Stuff is drawn in a drawable. 4388 * 4389 *---------------------------------------------------------------------- 4390 */ 4391 4392void 4393TreeItem_DrawButton( 4394 TreeCtrl *tree, /* Widget info. */ 4395 TreeItem item, /* Item token. */ 4396 int x, int y, /* Drawable coordinates of columnTree. */ 4397 int width, int height, /* Total size of columnTree. */ 4398 TreeDrawable td /* Where to draw. */ 4399 ) 4400{ 4401 int indent, left, lineLeft, lineTop; 4402 int buttonLeft, buttonTop, w1; 4403 int macoffset = 0; 4404 Tk_Image image; 4405 Pixmap bitmap; 4406 4407 if (!TreeItem_HasButton(tree, item)) 4408 return; 4409 4410#if defined(MAC_TK_CARBON) 4411 /* QuickDraw on Mac is offset by one pixel in both x and y. */ 4412 macoffset = 1; 4413#endif 4414 4415 indent = TreeItem_Indent(tree, item); 4416 4417 /* Left edge of button/line area */ 4418 left = x /* + tree->columnTreeLeft */ + indent - tree->useIndent; 4419 4420 image = PerStateImage_ForState(tree, &tree->buttonImage, item->state, NULL); 4421 if (image != NULL) { 4422 int imgW, imgH; 4423 Tk_SizeOfImage(image, &imgW, &imgH); 4424 Tree_RedrawImage(image, 0, 0, imgW, imgH, td, 4425 left + (tree->useIndent - imgW) / 2, 4426 y + (height - imgH) / 2); 4427 return; 4428 } 4429 4430 bitmap = PerStateBitmap_ForState(tree, &tree->buttonBitmap, item->state, NULL); 4431 if (bitmap != None) { 4432 int bmpW, bmpH; 4433 int bx, by; 4434 Tk_SizeOfBitmap(tree->display, bitmap, &bmpW, &bmpH); 4435 bx = left + (tree->useIndent - bmpW) / 2; 4436 by = y + (height - bmpH) / 2; 4437 Tree_DrawBitmap(tree, bitmap, td.drawable, NULL, NULL, 4438 0, 0, (unsigned int) bmpW, (unsigned int) bmpH, 4439 bx, by); 4440 return; 4441 } 4442 4443 if (tree->useTheme) { 4444 int bw, bh; 4445 if (TreeTheme_GetButtonSize(tree, td.drawable, item->state & STATE_OPEN, 4446 &bw, &bh) == TCL_OK) { 4447 if (TreeTheme_DrawButton(tree, td.drawable, item->state & STATE_OPEN, 4448 left + (tree->useIndent - bw) / 2, y + (height - bh) / 2, 4449 bw, bh) == TCL_OK) { 4450 return; 4451 } 4452 } 4453 } 4454 4455 w1 = tree->buttonThickness / 2; 4456 4457 /* Left edge of vertical line */ 4458 /* Make sure this matches TreeItem_DrawLines() */ 4459 lineLeft = left + (tree->useIndent - tree->buttonThickness) / 2; 4460 4461 /* Top edge of horizontal line */ 4462 /* Make sure this matches TreeItem_DrawLines() */ 4463 lineTop = y + (height - tree->buttonThickness) / 2; 4464 4465 buttonLeft = left + (tree->useIndent - tree->buttonSize) / 2; 4466 buttonTop = y + (height - tree->buttonSize) / 2; 4467 4468 /* Erase button background */ 4469 XFillRectangle(tree->display, td.drawable, 4470 Tk_3DBorderGC(tree->tkwin, tree->border, TK_3D_FLAT_GC), 4471 buttonLeft + tree->buttonThickness, 4472 buttonTop + tree->buttonThickness, 4473 tree->buttonSize - tree->buttonThickness, 4474 tree->buttonSize - tree->buttonThickness); 4475 4476 /* Draw button outline */ 4477 XDrawRectangle(tree->display, td.drawable, tree->buttonGC, 4478 buttonLeft + w1, 4479 buttonTop + w1, 4480 tree->buttonSize - tree->buttonThickness + macoffset, 4481 tree->buttonSize - tree->buttonThickness + macoffset); 4482 4483 /* Horizontal '-' */ 4484 XFillRectangle(tree->display, td.drawable, tree->buttonGC, 4485 buttonLeft + tree->buttonThickness * 2, 4486 lineTop, 4487 tree->buttonSize - tree->buttonThickness * 4, 4488 tree->buttonThickness); 4489 4490 if (!(item->state & STATE_OPEN)) { 4491 /* Finish '+' */ 4492 XFillRectangle(tree->display, td.drawable, tree->buttonGC, 4493 lineLeft, 4494 buttonTop + tree->buttonThickness * 2, 4495 tree->buttonThickness, 4496 tree->buttonSize - tree->buttonThickness * 4); 4497 } 4498} 4499 4500/* 4501 *---------------------------------------------------------------------- 4502 * 4503 * SpanWalkProc_UpdateWindowPositions -- 4504 * 4505 * Callback routine to TreeItem_WalkSpans for 4506 * TreeItem_UpdateWindowPositions. 4507 * 4508 * Results: 4509 * None. 4510 * 4511 * Side effects: 4512 * Windows in window elements may be resized/repositioned. 4513 * 4514 *---------------------------------------------------------------------- 4515 */ 4516 4517static int 4518SpanWalkProc_UpdateWindowPositions( 4519 TreeCtrl *tree, 4520 TreeItem item, 4521 SpanInfo *spanPtr, 4522 StyleDrawArgs *drawArgs, 4523 ClientData clientData 4524 ) 4525{ 4526 StyleDrawArgs drawArgsCopy; 4527 int requests; 4528 4529 if ((drawArgs->x >= drawArgs->bounds[2]) || 4530 (drawArgs->x + drawArgs->width <= drawArgs->bounds[0]) || 4531 (drawArgs->style == NULL)) 4532 return 0; 4533 4534 TreeDisplay_GetReadyForTrouble(tree, &requests); 4535 4536 drawArgsCopy = *drawArgs; 4537 TreeStyle_UpdateWindowPositions(&drawArgsCopy); 4538 4539 if (TreeDisplay_WasThereTrouble(tree, requests)) 4540 return 1; 4541 4542 /* Stop walking if we went past the right edge of the display area. */ 4543 return drawArgs->x + drawArgs->width >= drawArgs->bounds[2]; 4544} 4545 4546/* 4547 *---------------------------------------------------------------------- 4548 * 4549 * TreeItem_UpdateWindowPositions -- 4550 * 4551 * Updates the geometry of any on-screen window elements. Called 4552 * by the display code when an item was possibly scrolled. 4553 * 4554 * Results: 4555 * None. 4556 * 4557 * Side effects: 4558 * Windows in window elements may be resized/repositioned. 4559 * 4560 *---------------------------------------------------------------------- 4561 */ 4562 4563void 4564TreeItem_UpdateWindowPositions( 4565 TreeCtrl *tree, /* Widget info. */ 4566 TreeItem item, /* Item token. */ 4567 int lock, /* Columns we care about. */ 4568 int x, int y, /* Window coordinates of the item. */ 4569 int width, int height /* Total size of the item. */ 4570 ) 4571{ 4572 TreeItem_WalkSpans(tree, item, lock, 4573 x, y, width, height, 4574 SpanWalkProc_UpdateWindowPositions, (ClientData) NULL); 4575} 4576 4577/* 4578 *---------------------------------------------------------------------- 4579 * 4580 * TreeItem_OnScreen -- 4581 * 4582 * Called by the display code when the item becomes visible 4583 * (i.e., actually displayed) or hidden. 4584 * 4585 * Results: 4586 * None. 4587 * 4588 * Side effects: 4589 * Windows in window elements may be mapped/unmapped. 4590 * 4591 *---------------------------------------------------------------------- 4592 */ 4593 4594void 4595TreeItem_OnScreen( 4596 TreeCtrl *tree, /* Widget info. */ 4597 TreeItem item, /* Item token. */ 4598 int onScreen /* TRUE if item is displayed. */ 4599 ) 4600{ 4601#if 0 4602 Column *column = item->columns; 4603 4604 while (column != NULL) { 4605 if (column->style != NULL) { 4606 TreeStyle_OnScreen(tree, column->style, onScreen); 4607 } 4608 column = column->next; 4609 } 4610#endif 4611} 4612 4613/* 4614 *---------------------------------------------------------------------- 4615 * 4616 * TreeItem_ReallyVisible -- 4617 * 4618 * Return whether the given Item could be displayed. 4619 * 4620 * Results: 4621 * TRUE if the item's -visible is TRUE, all of its ancestors' 4622 * -visible options are TRUE, and all of its ancestors are open. 4623 * 4624 * Side effects: 4625 * None. 4626 * 4627 *---------------------------------------------------------------------- 4628 */ 4629 4630int TreeItem_ReallyVisible( 4631 TreeCtrl *tree, /* Widget info. */ 4632 TreeItem item /* Item token. */ 4633 ) 4634{ 4635#if 0 4636 Tree_UpdateItemIndex(tree); 4637 return item->indexVis != -1; 4638#else 4639 TreeItem parent = item->parent; 4640 4641 if (!tree->updateIndex) 4642 return item->indexVis != -1; 4643 4644 if (!IS_VISIBLE(item)) 4645 return 0; 4646 if (parent == NULL) 4647 return IS_ROOT(item) ? tree->showRoot : 0; 4648 if (IS_ROOT(parent)) { 4649 if (!IS_VISIBLE(parent)) 4650 return 0; 4651 if (!tree->showRoot) 4652 return 1; 4653 if (!(parent->state & STATE_OPEN)) 4654 return 0; 4655 } 4656 if (!IS_VISIBLE(parent) || !(parent->state & STATE_OPEN)) 4657 return 0; 4658 return TreeItem_ReallyVisible(tree, parent); 4659#endif 4660} 4661 4662/* 4663 *---------------------------------------------------------------------- 4664 * 4665 * TreeItem_RootAncestor -- 4666 * 4667 * Return the toplevel ancestor of an Item. 4668 * 4669 * Results: 4670 * Returns the root, or an orphan ancestor, or the given Item. 4671 * 4672 * Side effects: 4673 * None. 4674 * 4675 *---------------------------------------------------------------------- 4676 */ 4677 4678TreeItem TreeItem_RootAncestor( 4679 TreeCtrl *tree, /* Widget info. */ 4680 TreeItem item /* Item token. */ 4681 ) 4682{ 4683 while (item->parent != NULL) 4684 item = item->parent; 4685 return item; 4686} 4687 4688/* 4689 *---------------------------------------------------------------------- 4690 * 4691 * TreeItem_IsAncestor -- 4692 * 4693 * Determine if one Item is the ancestor of another. 4694 * 4695 * Results: 4696 * TRUE if item1 is an ancestor of item2. 4697 * 4698 * Side effects: 4699 * None. 4700 * 4701 *---------------------------------------------------------------------- 4702 */ 4703 4704int 4705TreeItem_IsAncestor( 4706 TreeCtrl *tree, /* Widget info. */ 4707 TreeItem item1, /* Possible ancestor. */ 4708 TreeItem item2 /* Item to check ancestry of. */ 4709 ) 4710{ 4711 if (item1 == item2) 4712 return 0; 4713 while (item2 && item2 != item1) 4714 item2 = item2->parent; 4715 return item2 != NULL; 4716} 4717 4718/* 4719 *---------------------------------------------------------------------- 4720 * 4721 * TreeItem_ToObj -- 4722 * 4723 * Convert an Item to a Tcl_Obj. 4724 * 4725 * Results: 4726 * A new Tcl_Obj representing the Item. 4727 * 4728 * Side effects: 4729 * Memory is allocated. 4730 * 4731 *---------------------------------------------------------------------- 4732 */ 4733 4734Tcl_Obj *TreeItem_ToObj( 4735 TreeCtrl *tree, /* Widget info. */ 4736 TreeItem item /* Item token. */ 4737 ) 4738{ 4739 if (tree->itemPrefixLen) { 4740 char buf[100 + TCL_INTEGER_SPACE]; 4741 (void) sprintf(buf, "%s%d", tree->itemPrefix, item->id); 4742 return Tcl_NewStringObj(buf, -1); 4743 } 4744 return Tcl_NewIntObj(item->id); 4745} 4746 4747/* 4748 *---------------------------------------------------------------------- 4749 * 4750 * Item_Configure -- 4751 * 4752 * This procedure is called to process an objc/objv list, plus 4753 * the Tk option database, in order to configure (or reconfigure) 4754 * an Item. 4755 * 4756 * Results: 4757 * The return value is a standard Tcl result. If TCL_ERROR is 4758 * returned, then the interp's result contains an error message. 4759 * 4760 * Side effects: 4761 * Configuration information gets set for item; old resources get 4762 * freed, if there were any. 4763 * 4764 *---------------------------------------------------------------------- 4765 */ 4766 4767static int Item_Configure( 4768 TreeCtrl *tree, /* Widget info. */ 4769 TreeItem item, /* Item to configure. */ 4770 int objc, /* Number of arguments */ 4771 Tcl_Obj *CONST objv[] /* Array of arguments */ 4772 ) 4773{ 4774 Tk_SavedOptions savedOptions; 4775 int error; 4776 Tcl_Obj *errorResult = NULL; 4777 int mask; 4778 int lastVisible = IS_VISIBLE(item); 4779 int lastWrap = IS_WRAP(item); 4780 4781 for (error = 0; error <= 1; error++) { 4782 if (error == 0) { 4783 if (Tk_SetOptions(tree->interp, (char *) item, tree->itemOptionTable, 4784 objc, objv, tree->tkwin, &savedOptions, &mask) != TCL_OK) { 4785 mask = 0; 4786 continue; 4787 } 4788 4789 /* xxx */ 4790 4791 Tk_FreeSavedOptions(&savedOptions); 4792 break; 4793 } else { 4794 errorResult = Tcl_GetObjResult(tree->interp); 4795 Tcl_IncrRefCount(errorResult); 4796 Tk_RestoreSavedOptions(&savedOptions); 4797 4798 /* xxx */ 4799 4800 Tcl_SetObjResult(tree->interp, errorResult); 4801 Tcl_DecrRefCount(errorResult); 4802 return TCL_ERROR; 4803 } 4804 } 4805 4806 if (mask & ITEM_CONF_SIZE) { 4807 Tree_FreeItemDInfo(tree, item, NULL); 4808 Tree_DInfoChanged(tree, DINFO_REDO_RANGES); 4809 } 4810 4811 if (mask & ITEM_CONF_BUTTON) { 4812 if (tree->columnTree != NULL) 4813 Tree_InvalidateItemDInfo(tree, tree->columnTree, item, NULL); 4814 } 4815 4816 if ((mask & ITEM_CONF_VISIBLE) && (IS_VISIBLE(item) != lastVisible)) { 4817 4818 /* Changing the visibility of an item can change the width of 4819 * any column. This is due to column expansion (this item may 4820 * be the widest item in the column) and spans > 1. */ 4821 Tree_InvalidateColumnWidth(tree, NULL); 4822 4823 /* If this is the last child, redraw the lines of the previous 4824 * sibling and all of its descendants because the line from 4825 * the previous sibling to us is appearing/disappearing. */ 4826 if ((item->prevSibling != NULL) && 4827 (item->nextSibling == NULL) && 4828 tree->showLines && (tree->columnTree != NULL)) { 4829 TreeItem last = item->prevSibling; 4830 while (last->lastChild != NULL) 4831 last = last->lastChild; 4832 Tree_InvalidateItemDInfo(tree, tree->columnTree, 4833 item->prevSibling, 4834 last); 4835 } 4836 4837 /* Redraw the parent if the parent has "-button auto". */ 4838 if ((item->parent != NULL) && 4839 (item->parent->flags & ITEM_FLAG_BUTTON_AUTO) && 4840 tree->showButtons && (tree->columnTree != NULL)) { 4841 Tree_InvalidateItemDInfo(tree, tree->columnTree, item->parent, 4842 NULL); 4843 } 4844 4845 tree->updateIndex = 1; 4846 Tree_DInfoChanged(tree, DINFO_REDO_RANGES | DINFO_REDO_SELECTION); 4847 } 4848 4849 if ((mask & ITEM_CONF_WRAP) && (IS_WRAP(item) != lastWrap)) { 4850 tree->updateIndex = 1; 4851 Tree_InvalidateColumnWidth(tree, NULL); 4852 Tree_DInfoChanged(tree, DINFO_REDO_RANGES); 4853 } 4854 4855 return TCL_OK; 4856} 4857 4858/* 4859 *---------------------------------------------------------------------- 4860 * 4861 * ItemCreateCmd -- 4862 * 4863 * This procedure is invoked to process the [item create] widget 4864 * command. See the user documentation for details on what 4865 * it does. 4866 * 4867 * Results: 4868 * A standard Tcl result. 4869 * 4870 * Side effects: 4871 * See the user documentation. 4872 * 4873 *---------------------------------------------------------------------- 4874 */ 4875 4876static int 4877ItemCreateCmd( 4878 ClientData clientData, /* Widget info. */ 4879 Tcl_Interp *interp, /* Current interpreter. */ 4880 int objc, /* Number of arguments. */ 4881 Tcl_Obj *CONST objv[] /* Argument values. */ 4882 ) 4883{ 4884 TreeCtrl *tree = clientData; 4885 static CONST char *optionNames[] = { "-button", "-count", "-height", 4886 "-nextsibling", "-open", "-parent", "-prevsibling", "-returnid", 4887 "-tags", "-visible", "-wrap", 4888 (char *) NULL }; 4889 enum { OPT_BUTTON, OPT_COUNT, OPT_HEIGHT, OPT_NEXTSIBLING, 4890 OPT_OPEN, OPT_PARENT, OPT_PREVSIBLING, OPT_RETURNID, OPT_TAGS, 4891 OPT_VISIBLE, OPT_WRAP }; 4892 int index, i, count = 1, button = 0, returnId = 1, open = 1, visible = 1; 4893 int wrap = 0; 4894 int height = 0; 4895 TreeItem item, parent = NULL, prevSibling = NULL, nextSibling = NULL; 4896 TreeItem head = NULL, tail = NULL; 4897 Tcl_Obj *listObj = NULL, *tagsObj = NULL; 4898 TagInfo *tagInfo = NULL; 4899 TreeColumn treeColumn; 4900 4901 for (i = 3; i < objc; i += 2) { 4902 if (Tcl_GetIndexFromObj(interp, objv[i], optionNames, "option", 0, 4903 &index) != TCL_OK) { 4904 return TCL_ERROR; 4905 } 4906 if (i + 1 == objc) { 4907 FormatResult(interp, "missing value for \"%s\" option", 4908 optionNames[index]); 4909 return TCL_ERROR; 4910 } 4911 switch (index) { 4912 case OPT_BUTTON: { 4913 int length; 4914 char *s = Tcl_GetStringFromObj(objv[i + 1], &length); 4915 if (s[0] == 'a' && strncmp(s, "auto", length) == 0) { 4916 button = ITEM_FLAG_BUTTON_AUTO; 4917 } else { 4918 if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &button) != TCL_OK) { 4919 FormatResult(interp, "expected boolean or auto but got \"%s\"", s); 4920 return TCL_ERROR; 4921 } 4922 if (button) { 4923 button = ITEM_FLAG_BUTTON; 4924 } 4925 } 4926 break; 4927 } 4928 case OPT_COUNT: 4929 if (Tcl_GetIntFromObj(interp, objv[i + 1], &count) != TCL_OK) 4930 return TCL_ERROR; 4931 if (count <= 0) { 4932 FormatResult(interp, "bad count \"%d\": must be > 0", 4933 count); 4934 return TCL_ERROR; 4935 } 4936 break; 4937 case OPT_HEIGHT: 4938 if (Tk_GetPixelsFromObj(interp, tree->tkwin, objv[i + 1], 4939 &height) != TCL_OK) 4940 return TCL_ERROR; 4941 if (height < 0) { 4942 FormatResult(interp, "bad screen distance \"%s\": must be > 0", 4943 Tcl_GetString(objv[i + 1])); 4944 return TCL_ERROR; 4945 } 4946 break; 4947 case OPT_NEXTSIBLING: 4948 if (TreeItem_FromObj(tree, objv[i + 1], &nextSibling, 4949 IFO_NOT_NULL | IFO_NOT_ROOT | IFO_NOT_ORPHAN) != TCL_OK) { 4950 return TCL_ERROR; 4951 } 4952 parent = prevSibling = NULL; 4953 break; 4954 case OPT_OPEN: 4955 if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &open) 4956 != TCL_OK) { 4957 return TCL_ERROR; 4958 } 4959 break; 4960 case OPT_PARENT: 4961 if (TreeItem_FromObj(tree, objv[i + 1], &parent, IFO_NOT_NULL) != TCL_OK) { 4962 return TCL_ERROR; 4963 } 4964 prevSibling = nextSibling = NULL; 4965 break; 4966 case OPT_PREVSIBLING: 4967 if (TreeItem_FromObj(tree, objv[i + 1], &prevSibling, 4968 IFO_NOT_NULL | IFO_NOT_ROOT | IFO_NOT_ORPHAN) != TCL_OK) { 4969 return TCL_ERROR; 4970 } 4971 parent = nextSibling = NULL; 4972 break; 4973 case OPT_RETURNID: 4974 if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &returnId) 4975 != TCL_OK) { 4976 return TCL_ERROR; 4977 } 4978 break; 4979 case OPT_TAGS: 4980 tagsObj = objv[i + 1]; 4981 break; 4982 case OPT_VISIBLE: 4983 if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &visible) 4984 != TCL_OK) { 4985 return TCL_ERROR; 4986 } 4987 break; 4988 case OPT_WRAP: 4989 if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &wrap) 4990 != TCL_OK) { 4991 return TCL_ERROR; 4992 } 4993 break; 4994 } 4995 } 4996 4997 /* Do it here so I don't have to free it above if an error occurs. */ 4998 if (tagsObj != NULL) { 4999 if (TagInfo_FromObj(tree, tagsObj, &tagInfo) != TCL_OK) 5000 return TCL_ERROR; 5001 } 5002 5003 if (returnId) 5004 listObj = Tcl_NewListObj(0, NULL); 5005 5006 /* Don't allow non-deleted items to become children of a 5007 * deleted item. */ 5008 if ((parent && IS_DELETED(parent)) || 5009 (prevSibling && IS_DELETED(prevSibling->parent)) || 5010 (nextSibling && IS_DELETED(nextSibling->parent))) 5011 parent = prevSibling = nextSibling = NULL; 5012 5013 for (i = 0; i < count; i++) { 5014 item = Item_Alloc(tree); 5015 item->flags &= ~(ITEM_FLAG_BUTTON | ITEM_FLAG_BUTTON_AUTO); 5016 item->flags |= button; 5017 if (visible) item->flags |= ITEM_FLAG_VISIBLE; 5018 else item->flags &= ~ITEM_FLAG_VISIBLE; 5019 if (open) item->state |= STATE_OPEN; 5020 else item->state &= ~STATE_OPEN; 5021 if (wrap) item->flags |= ITEM_FLAG_WRAP; 5022 else item->flags &= ~ITEM_FLAG_WRAP; 5023 item->fixedHeight = height; 5024 5025 /* Apply each column's -itemstyle option. */ 5026 for (treeColumn = tree->columns; treeColumn != NULL; 5027 treeColumn = TreeColumn_Next(treeColumn)) { 5028 TreeStyle style = TreeColumn_ItemStyle(treeColumn); 5029 if (style != NULL) { 5030 Column *column = Item_CreateColumn(tree, item, 5031 TreeColumn_Index(treeColumn), NULL); 5032 column->style = TreeStyle_NewInstance(tree, style); 5033 } 5034 } 5035#ifdef DEPRECATED 5036 /* Apply default styles */ 5037 if (tree->defaultStyle.numStyles) { 5038 int i, n = MIN(tree->columnCount, tree->defaultStyle.numStyles); 5039 5040 for (i = 0; i < n; i++) { 5041 Column *column = Item_CreateColumn(tree, item, i, NULL); 5042 if (column->style != NULL) 5043 continue; 5044 if (tree->defaultStyle.styles[i] != NULL) { 5045 column->style = TreeStyle_NewInstance(tree, 5046 tree->defaultStyle.styles[i]); 5047 } 5048 } 5049 } 5050#endif /* DEPRECATED */ 5051 5052 if (tagInfo != NULL) { 5053 if (count == 1) { 5054 item->tagInfo = tagInfo; 5055 tagInfo = NULL; 5056 } else { 5057 item->tagInfo = TagInfo_Copy(tree, tagInfo); 5058 } 5059 } 5060 5061 /* Link the new items together as siblings */ 5062 if (parent || prevSibling || nextSibling) { 5063 if (head == NULL) 5064 head = item; 5065 if (tail != NULL) { 5066 tail->nextSibling = item; 5067 item->prevSibling = tail; 5068 } 5069 tail = item; 5070 } 5071 5072 if (returnId) 5073 Tcl_ListObjAppendElement(interp, listObj, TreeItem_ToObj(tree, 5074 item)); 5075 } 5076 5077 if (parent != NULL) { 5078 head->prevSibling = parent->lastChild; 5079 if (parent->lastChild != NULL) 5080 parent->lastChild->nextSibling = head; 5081 else 5082 parent->firstChild = head; 5083 parent->lastChild = tail; 5084 } else if (prevSibling != NULL) { 5085 parent = prevSibling->parent; 5086 if (prevSibling->nextSibling != NULL) 5087 prevSibling->nextSibling->prevSibling = tail; 5088 else 5089 parent->lastChild = tail; 5090 head->prevSibling = prevSibling; 5091 tail->nextSibling = prevSibling->nextSibling; 5092 prevSibling->nextSibling = head; 5093 } else if (nextSibling != NULL) { 5094 parent = nextSibling->parent; 5095 if (nextSibling->prevSibling != NULL) 5096 nextSibling->prevSibling->nextSibling = head; 5097 else 5098 parent->firstChild = head; 5099 head->prevSibling = nextSibling->prevSibling; 5100 tail->nextSibling = nextSibling; 5101 nextSibling->prevSibling = tail; 5102 } 5103 5104 if (parent != NULL) { 5105 for (item = head; item != NULL; item = item->nextSibling) { 5106 item->parent = parent; 5107 item->depth = parent->depth + 1; 5108 } 5109 parent->numChildren += count; 5110 TreeItem_AddToParent(tree, head); 5111 } 5112 5113 TagInfo_Free(tree, tagInfo); 5114 5115 if (returnId) 5116 Tcl_SetObjResult(interp, listObj); 5117 5118 return TCL_OK; 5119} 5120 5121/* 5122 *---------------------------------------------------------------------- 5123 * 5124 * NoStyleMsg -- 5125 * 5126 * Utility to set the interpreter result with a message indicating 5127 * a Column has no assigned style. 5128 * 5129 * Results: 5130 * None. 5131 * 5132 * Side effects: 5133 * Interpreter result is changed. 5134 * 5135 *---------------------------------------------------------------------- 5136 */ 5137 5138static void 5139NoStyleMsg( 5140 TreeCtrl *tree, /* Widget info. */ 5141 TreeItem item, /* Item record. */ 5142 int columnIndex /* 0-based index of the column that 5143 * has no style. */ 5144 ) 5145{ 5146 FormatResult(tree->interp, 5147 "item %s%d column %s%d has no style", 5148 tree->itemPrefix, item->id, 5149 tree->columnPrefix, 5150 TreeColumn_GetID(Tree_FindColumn(tree, columnIndex))); 5151} 5152 5153/* 5154 *---------------------------------------------------------------------- 5155 * 5156 * ItemElementCmd -- 5157 * 5158 * This procedure is invoked to process the [item element] widget 5159 * command. See the user documentation for details on what 5160 * it does. 5161 * 5162 * Results: 5163 * A standard Tcl result. 5164 * 5165 * Side effects: 5166 * See the user documentation. 5167 * 5168 *---------------------------------------------------------------------- 5169 */ 5170 5171static int 5172ItemElementCmd( 5173 ClientData clientData, /* Widget info. */ 5174 Tcl_Interp *interp, /* Current interpreter. */ 5175 int objc, /* Number of arguments. */ 5176 Tcl_Obj *CONST objv[] /* Argument values. */ 5177 ) 5178{ 5179 TreeCtrl *tree = clientData; 5180 static CONST char *commandNames[] = { 5181#ifdef DEPRECATED 5182 "actual", 5183#endif 5184 "cget", "configure", 5185 "perstate", (char *) NULL }; 5186 enum { 5187#ifdef DEPRECATED 5188 COMMAND_ACTUAL, 5189#endif 5190 COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_PERSTATE }; 5191 int index; 5192 int columnIndex; 5193 Column *column; 5194 TreeItemList itemList; 5195 TreeItem item; 5196 int flags = IFO_NOT_NULL; 5197 int result = TCL_OK; 5198 5199 if (objc < 7) { 5200 Tcl_WrongNumArgs(interp, 3, objv, "command item column element ?arg ...?"); 5201 return TCL_ERROR; 5202 } 5203 5204 if (Tcl_GetIndexFromObj(interp, objv[3], commandNames, "command", 0, 5205 &index) != TCL_OK) 5206 return TCL_ERROR; 5207 5208 /* 5209 * [configure] without an option-value pair can operate on a single item 5210 * only. [cget] and [perstate] only operate on a single item. 5211 */ 5212 if ((index != COMMAND_CONFIGURE) || (objc < 9)) 5213 flags |= IFO_NOT_MANY; 5214 5215 if (TreeItemList_FromObj(tree, objv[4], &itemList, flags) != TCL_OK) 5216 return TCL_ERROR; 5217 item = TreeItemList_Nth(&itemList, 0); 5218 5219 switch (index) { 5220 /* T item element perstate I C E option ?stateList? */ 5221#ifdef DEPRECATED 5222 case COMMAND_ACTUAL: 5223#endif 5224 case COMMAND_PERSTATE: { 5225 int state; 5226 5227 if (objc < 8 || objc > 9) { 5228 Tcl_WrongNumArgs(tree->interp, 4, objv, 5229 "item column element option ?stateList?"); 5230 result = TCL_ERROR; 5231 break; 5232 } 5233 if (Item_FindColumnFromObj(tree, item, objv[5], &column, 5234 &columnIndex) != TCL_OK) { 5235 result = TCL_ERROR; 5236 break; 5237 } 5238 if ((column == NULL) || (column->style == NULL)) { 5239 NoStyleMsg(tree, item, columnIndex); 5240 result = TCL_ERROR; 5241 break; 5242 } 5243 state = item->state | column->cstate; 5244 if (objc == 9) { 5245 int states[3]; 5246 5247 if (Tree_StateFromListObj(tree, objv[8], states, 5248 SFO_NOT_OFF | SFO_NOT_TOGGLE) != TCL_OK) { 5249 result = TCL_ERROR; 5250 break; 5251 } 5252 state = states[STATE_OP_ON]; 5253 } 5254 result = TreeStyle_ElementActual(tree, column->style, 5255 state, objv[6], objv[7]); 5256 break; 5257 } 5258 5259 /* T item element cget I C E option */ 5260 case COMMAND_CGET: { 5261 if (objc != 8) { 5262 Tcl_WrongNumArgs(tree->interp, 4, objv, 5263 "item column element option"); 5264 result = TCL_ERROR; 5265 break; 5266 } 5267 if (Item_FindColumnFromObj(tree, item, objv[5], &column, 5268 &columnIndex) != TCL_OK) { 5269 result = TCL_ERROR; 5270 break; 5271 } 5272 if ((column == NULL) || (column->style == NULL)) { 5273 NoStyleMsg(tree, item, columnIndex); 5274 result = TCL_ERROR; 5275 break; 5276 } 5277 result = TreeStyle_ElementCget(tree, item, 5278 (TreeItemColumn) column, column->style, objv[6], objv[7]); 5279 break; 5280 } 5281 5282 /* T item element configure I C E ... */ 5283 case COMMAND_CONFIGURE: { 5284 struct columnObj { 5285 TreeColumnList columns; 5286 int isColumn; 5287 int numArgs; 5288 } staticCO[STATIC_SIZE], *co = staticCO; 5289 int i, index, indexElem, prevColumn; 5290 ItemForEach iter; 5291 5292 /* If no option-value pair is given, we can't specify more than 5293 * one column. */ 5294 flags = CFO_NOT_NULL | CFO_NOT_TAIL; 5295 if (objc < 9) 5296 flags |= CFO_NOT_MANY; 5297 5298 STATIC_ALLOC(co, struct columnObj, objc); 5299 for (i = 5; i < objc; i++) { 5300 co[i].isColumn = FALSE; 5301 co[i].numArgs = -1; 5302 } 5303 indexElem = 6; 5304 5305 /* Get the first column(s) */ 5306 i = indexElem - 1; 5307 if (TreeColumnList_FromObj(tree, objv[i], &co[i].columns, 5308 flags) != TCL_OK) { 5309 result = TCL_ERROR; 5310 break; 5311 } 5312 co[i].isColumn = TRUE; 5313 prevColumn = i; 5314 5315 while (1) { 5316 int numArgs = 0; 5317 char breakChar = '\0'; 5318 5319 /* Look for a + or , */ 5320 for (index = indexElem + 1; index < objc; index++) { 5321 if (numArgs % 2 == 0) { 5322 int length; 5323 char *s = Tcl_GetStringFromObj(objv[index], &length); 5324 5325 if ((length == 1) && ((s[0] == '+') || (s[0] == ','))) { 5326 breakChar = s[0]; 5327 break; 5328 } 5329 } 5330 numArgs++; 5331 } 5332 5333 /* Require at least one option-value pair if more than one 5334 * element is specified. */ 5335 if ((breakChar || indexElem != 6) && (numArgs < 2)) { 5336 FormatResult(interp, 5337 "missing option-value pair after element \"%s\"", 5338 Tcl_GetString(objv[indexElem])); 5339 result = TCL_ERROR; 5340 goto doneCONF; 5341 } 5342 5343 co[indexElem].numArgs = numArgs; 5344 5345 if (!breakChar) 5346 break; 5347 5348 if (index == objc - 1) { 5349 FormatResult(interp, "missing %s after \"%c\"", 5350 (breakChar == '+') ? "element name" : "column", 5351 breakChar); 5352 result = TCL_ERROR; 5353 goto doneCONF; 5354 } 5355 5356 /* + indicates start of another element */ 5357 if (breakChar == '+') { 5358 indexElem = index + 1; 5359 } 5360 5361 /* , indicates start of another column */ 5362 else if (breakChar == ',') { 5363 co[prevColumn].numArgs = index - prevColumn; 5364 5365 if (TreeColumnList_FromObj(tree, objv[index + 1], 5366 &co[index + 1].columns, CFO_NOT_NULL | 5367 CFO_NOT_TAIL) != TCL_OK) { 5368 result = TCL_ERROR; 5369 goto doneCONF; 5370 } 5371 co[index + 1].isColumn = TRUE; 5372 prevColumn = index + 1; 5373 5374 indexElem = index + 2; 5375 if (indexElem == objc) { 5376 FormatResult(interp, 5377 "missing element name after column \"%s\"", 5378 Tcl_GetString(objv[index + 1])); 5379 result = TCL_ERROR; 5380 goto doneCONF; 5381 } 5382 } 5383 } 5384 co[prevColumn].numArgs = index - prevColumn; 5385 5386 ITEM_FOR_EACH(item, &itemList, NULL, &iter) { 5387 /* T item element configure I C E option value \ 5388 * + E option value , C E option value */ 5389 int iMask = 0; 5390 5391 /* co[index].numArgs is the number of arguments from the C 5392 * to the next separator (but not including that separator). */ 5393 for (index = 5; index < objc; index += co[index].numArgs + 1) { 5394 ColumnForEach citer; 5395 TreeColumn treeColumn; 5396 5397if (!co[index].isColumn) panic("isColumn == FALSE"); 5398 5399 COLUMN_FOR_EACH(treeColumn, &co[index].columns, NULL, &citer) { 5400 int columnIndex, cMask = 0; 5401 5402 columnIndex = TreeColumn_Index(treeColumn); 5403 column = Item_FindColumn(tree, item, columnIndex); 5404 if ((column == NULL) || (column->style == NULL)) { 5405 NoStyleMsg(tree, item, columnIndex); 5406 result = TCL_ERROR; 5407 break; 5408 } 5409 5410 indexElem = index + 1; 5411 5412 /* Do each element in this column */ 5413 while (1) { 5414 int eMask, index2; 5415 5416if (co[indexElem].numArgs == -1) panic("indexElem=%d (%s) objc=%d numArgs == -1", indexElem, Tcl_GetString(objv[indexElem]), objc); 5417 result = TreeStyle_ElementConfigure(tree, item, 5418 (TreeItemColumn) column, column->style, objv[indexElem], 5419 co[indexElem].numArgs, (Tcl_Obj **) objv + indexElem + 1, &eMask); 5420 if (result != TCL_OK) 5421 break; 5422 5423 cMask |= eMask; 5424 5425 /* co[indexElem].numArgs is the number of 5426 * option-value arguments after the element. */ 5427 index2 = indexElem + co[indexElem].numArgs; 5428 if (index2 == objc - 1) 5429 break; 5430 5431 /* Skip the '+' or ',' */ 5432 index2 += 2; 5433 5434 if (co[index2].isColumn) 5435 break; 5436 5437 indexElem = index2; 5438 } 5439 5440 if (cMask & CS_LAYOUT) { 5441 TreeItemColumn_InvalidateSize(tree, (TreeItemColumn) column); 5442 Tree_InvalidateColumnWidth(tree, treeColumn); 5443 } else if (cMask & CS_DISPLAY) { 5444 Tree_InvalidateItemDInfo(tree, treeColumn, item, NULL); 5445 } 5446 iMask |= cMask; 5447 if (result != TCL_OK) 5448 break; 5449 } 5450 if (result != TCL_OK) 5451 break; 5452 } 5453 if (iMask & CS_LAYOUT) { 5454 TreeItem_InvalidateHeight(tree, item); 5455 Tree_FreeItemDInfo(tree, item, NULL); 5456 Tree_DInfoChanged(tree, DINFO_REDO_RANGES); 5457 } else if (iMask & CS_DISPLAY) { 5458 } 5459 if (result != TCL_OK) 5460 break; 5461 } 5462doneCONF: 5463 for (i = 5; i < objc; i++) { 5464 if (co[i].isColumn) 5465 TreeColumnList_Free(&co[i].columns); 5466 } 5467 STATIC_FREE(co, struct columnObj, objc); 5468 break; 5469 } 5470 } 5471 5472 TreeItemList_Free(&itemList); 5473 return result; 5474} 5475 5476/* 5477 *---------------------------------------------------------------------- 5478 * 5479 * ItemStyleCmd -- 5480 * 5481 * This procedure is invoked to process the [item style] widget 5482 * command. See the user documentation for details on what 5483 * it does. 5484 * 5485 * Results: 5486 * A standard Tcl result. 5487 * 5488 * Side effects: 5489 * See the user documentation. 5490 * 5491 *---------------------------------------------------------------------- 5492 */ 5493 5494static int 5495ItemStyleCmd( 5496 ClientData clientData, /* Widget info. */ 5497 Tcl_Interp *interp, /* Current interpreter. */ 5498 int objc, /* Number of arguments. */ 5499 Tcl_Obj *CONST objv[] /* Argument values. */ 5500 ) 5501{ 5502 TreeCtrl *tree = clientData; 5503 static CONST char *commandNames[] = { "elements", "map", "set", (char *) NULL }; 5504 enum { COMMAND_ELEMENTS, COMMAND_MAP, COMMAND_SET }; 5505 int index; 5506 TreeItemList itemList; 5507 TreeItem item; 5508 int flags = IFO_NOT_NULL; 5509 int result = TCL_OK; 5510 5511 if (objc < 5) { 5512 Tcl_WrongNumArgs(interp, 3, objv, "command item ?arg ...?"); 5513 return TCL_ERROR; 5514 } 5515 5516 if (Tcl_GetIndexFromObj(interp, objv[3], commandNames, "command", 0, 5517 &index) != TCL_OK) { 5518 return TCL_ERROR; 5519 } 5520 5521 /* [style elements] only works on a single item. 5522 * [style set] only works on a single item without a column-style pair. */ 5523 if ((index == COMMAND_ELEMENTS) || (index == COMMAND_SET && objc < 7)) 5524 flags |= IFO_NOT_MANY; 5525 if (TreeItemList_FromObj(tree, objv[4], &itemList, flags) != TCL_OK) { 5526 return TCL_ERROR; 5527 } 5528 item = TreeItemList_Nth(&itemList, 0); 5529 5530 switch (index) { 5531 /* T item style elements I C */ 5532 case COMMAND_ELEMENTS: { 5533 Column *column; 5534 int columnIndex; 5535 5536 if (objc != 6) { 5537 Tcl_WrongNumArgs(interp, 4, objv, "item column"); 5538 result = TCL_ERROR; 5539 break; 5540 } 5541 if (Item_FindColumnFromObj(tree, item, objv[5], &column, 5542 &columnIndex) != TCL_OK) { 5543 result = TCL_ERROR; 5544 break; 5545 } 5546 if ((column == NULL) || (column->style == NULL)) { 5547 NoStyleMsg(tree, item, columnIndex); 5548 result = TCL_ERROR; 5549 break; 5550 } 5551 TreeStyle_ListElements(tree, column->style); 5552 break; 5553 } 5554 5555 /* T item style map I C S map */ 5556 case COMMAND_MAP: { 5557 TreeStyle style; 5558 TreeColumnList columns; 5559 TreeColumn treeColumn; 5560 Column *column; 5561 int columnIndex; 5562 int objcM; 5563 Tcl_Obj **objvM; 5564 ItemForEach iter; 5565 ColumnForEach citer; 5566 5567 if (objc != 8) { 5568 Tcl_WrongNumArgs(interp, 4, objv, "item column style map"); 5569 return TCL_ERROR; 5570 } 5571 if (TreeColumnList_FromObj(tree, objv[5], &columns, 5572 CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) { 5573 result = TCL_ERROR; 5574 break; 5575 } 5576 if (TreeStyle_FromObj(tree, objv[6], &style) != TCL_OK) { 5577 result = TCL_ERROR; 5578 goto doneMAP; 5579 } 5580 if (Tcl_ListObjGetElements(interp, objv[7], &objcM, &objvM) 5581 != TCL_OK) { 5582 result = TCL_ERROR; 5583 goto doneMAP; 5584 } 5585 if (objcM & 1) { 5586 FormatResult(interp, "list must contain even number of elements"); 5587 result = TCL_ERROR; 5588 goto doneMAP; 5589 } 5590 ITEM_FOR_EACH(item, &itemList, NULL, &iter) { 5591 COLUMN_FOR_EACH(treeColumn, &columns, NULL, &citer) { 5592 columnIndex = TreeColumn_Index(treeColumn); 5593 column = Item_CreateColumn(tree, item, columnIndex, NULL); 5594 if (column->style != NULL) { 5595 if (TreeStyle_Remap(tree, column->style, style, objcM, 5596 objvM) != TCL_OK) { 5597 result = TCL_ERROR; 5598 break; 5599 } 5600 } else { 5601 column->style = TreeStyle_NewInstance(tree, style); 5602 } 5603 TreeItemColumn_InvalidateSize(tree, (TreeItemColumn) column); 5604 Tree_InvalidateColumnWidth(tree, treeColumn); 5605 } 5606 TreeItem_InvalidateHeight(tree, item); 5607 Tree_FreeItemDInfo(tree, item, NULL); 5608 if (result != TCL_OK) 5609 break; 5610 } 5611 Tree_DInfoChanged(tree, DINFO_REDO_RANGES); 5612doneMAP: 5613 TreeColumnList_Free(&columns); 5614 break; 5615 } 5616 5617 /* T item style set I ?C? ?S? ?C S ...?*/ 5618 case COMMAND_SET: { 5619 struct columnStyle { 5620 TreeColumnList columns; 5621 TreeStyle style; 5622 }; 5623 struct columnStyle staticCS[STATIC_SIZE], *cs = staticCS; 5624 TreeColumn treeColumn; 5625 Column *column; 5626 int i, count = 0, length, changed = FALSE, changedI; 5627 ItemForEach iter; 5628 ColumnForEach citer; 5629 5630 if (objc < 5) { 5631 Tcl_WrongNumArgs(interp, 4, objv, "item ?column? ?style? ?column style ...?"); 5632 return TCL_ERROR; 5633 } 5634 /* Return list of styles. */ 5635 if (objc == 5) { 5636 Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); 5637 treeColumn = tree->columns; 5638 column = item->columns; 5639 while (treeColumn != NULL) { 5640 if ((column != NULL) && (column->style != NULL)) 5641 Tcl_ListObjAppendElement(interp, listObj, 5642 TreeStyle_ToObj(TreeStyle_GetMaster( 5643 tree, column->style))); 5644 else 5645 Tcl_ListObjAppendElement(interp, listObj, 5646 Tcl_NewObj()); 5647 treeColumn = TreeColumn_Next(treeColumn); 5648 if (column != NULL) 5649 column = column->next; 5650 } 5651 Tcl_SetObjResult(interp, listObj); 5652 break; 5653 } 5654 /* Return style in one column. */ 5655 if (objc == 6) { 5656 if (Item_FindColumnFromObj(tree, item, objv[5], &column, NULL) != TCL_OK) 5657 return TCL_ERROR; 5658 if ((column != NULL) && (column->style != NULL)) 5659 Tcl_SetObjResult(interp, TreeStyle_ToObj( 5660 TreeStyle_GetMaster(tree, column->style))); 5661 break; 5662 } 5663 /* Get column/style pairs. */ 5664 STATIC_ALLOC(cs, struct columnStyle, objc / 2); 5665 for (i = 5; i < objc; i += 2) { 5666 if (TreeColumnList_FromObj(tree, objv[i], &cs[count].columns, 5667 CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) { 5668 result = TCL_ERROR; 5669 goto doneSET; 5670 } 5671 if (i + 1 == objc) { 5672 FormatResult(interp, "missing style for column \"%s\"", 5673 Tcl_GetString(objv[i])); 5674 result = TCL_ERROR; 5675 goto doneSET; 5676 } 5677 (void) Tcl_GetStringFromObj(objv[i + 1], &length); 5678 if (length == 0) { 5679 cs[count].style = NULL; 5680 } else { 5681 if (TreeStyle_FromObj(tree, objv[i + 1], &cs[count].style) 5682 != TCL_OK) { 5683 result = TCL_ERROR; 5684 goto doneSET; 5685 } 5686 } 5687 count++; 5688 } 5689 ITEM_FOR_EACH(item, &itemList, NULL, &iter) { 5690 changedI = FALSE; 5691 for (i = 0; i < count; i++) { 5692 COLUMN_FOR_EACH(treeColumn, &cs[i].columns, NULL, &citer) { 5693 if (cs[i].style == NULL) { 5694 column = Item_FindColumn(tree, item, 5695 TreeColumn_Index(treeColumn)); 5696 if (column == NULL || column->style == NULL) 5697 continue; 5698 TreeItemColumn_ForgetStyle(tree, 5699 (TreeItemColumn) column); 5700 } else { 5701 column = Item_CreateColumn(tree, item, 5702 TreeColumn_Index(treeColumn), NULL); 5703 if (column->style != NULL) { 5704 if (TreeStyle_GetMaster(tree, column->style) 5705 == cs[i].style) 5706 continue; 5707 TreeItemColumn_ForgetStyle(tree, 5708 (TreeItemColumn) column); 5709 } 5710 column->style = TreeStyle_NewInstance(tree, 5711 cs[i].style); 5712 } 5713 TreeItemColumn_InvalidateSize(tree, 5714 (TreeItemColumn) column); 5715 Tree_InvalidateColumnWidth(tree, treeColumn); 5716 changedI = TRUE; 5717 } 5718 if (changedI) { 5719 TreeItem_InvalidateHeight(tree, item); 5720 Tree_FreeItemDInfo(tree, item, NULL); 5721 changed = TRUE; 5722 } 5723 } 5724 } 5725 if (changed) 5726 Tree_DInfoChanged(tree, DINFO_REDO_RANGES); 5727doneSET: 5728 for (i = 0; i < count; i++) { 5729 TreeColumnList_Free(&cs[i].columns); 5730 } 5731 STATIC_FREE(cs, struct columnStyle, objc / 2); 5732 break; 5733 } 5734 } 5735 5736 TreeItemList_Free(&itemList); 5737 return result; 5738} 5739 5740/* Quicksort is not a "stable" sorting algorithm, but it can become a 5741 * stable sort by using the pre-sort order of two items as a tie-breaker 5742 * for items that would otherwise be considered equal. */ 5743#define STABLE_SORT 5744 5745/* one per column per SortItem */ 5746struct SortItem1 5747{ 5748 long longValue; 5749 double doubleValue; 5750 char *string; 5751}; 5752 5753/* one per Item */ 5754struct SortItem 5755{ 5756 TreeItem item; 5757 struct SortItem1 *item1; 5758 Tcl_Obj *obj; /* TreeItem_ToObj() */ 5759#ifdef STABLE_SORT 5760 int index; /* The pre-sort order of the item */ 5761#endif 5762}; 5763 5764typedef struct SortData SortData; 5765 5766/* Used to process -element option */ 5767struct SortElement 5768{ 5769 TreeStyle style; 5770 TreeElement elem; 5771 int elemIndex; 5772}; 5773 5774/* One per TreeColumn */ 5775struct SortColumn 5776{ 5777 int (*proc)(SortData *, struct SortItem *, struct SortItem *, int); 5778 int sortBy; 5779 int column; 5780 int order; 5781 Tcl_Obj *command; 5782 struct SortElement elems[20]; 5783 int elemCount; 5784}; 5785 5786/* Data for sort as a whole */ 5787struct SortData 5788{ 5789 TreeCtrl *tree; 5790 struct SortItem *items; 5791 struct SortItem1 *item1s; /* SortItem.item1 points in here */ 5792#define MAX_SORT_COLUMNS 40 5793 struct SortColumn columns[MAX_SORT_COLUMNS]; 5794 int columnCount; /* max number of columns to compare */ 5795 int result; 5796}; 5797 5798/* from Tcl 8.4.0 */ 5799static int 5800DictionaryCompare( 5801 char *left, 5802 char *right 5803 ) 5804{ 5805 Tcl_UniChar uniLeft, uniRight, uniLeftLower, uniRightLower; 5806 int diff, zeros; 5807 int secondaryDiff = 0; 5808 5809 while (1) { 5810 if (isdigit(UCHAR(*right)) && isdigit(UCHAR(*left))) { /* INTL: digit */ 5811 /* 5812 * There are decimal numbers embedded in the two 5813 * strings. Compare them as numbers, rather than 5814 * strings. If one number has more leading zeros than 5815 * the other, the number with more leading zeros sorts 5816 * later, but only as a secondary choice. 5817 */ 5818 5819 zeros = 0; 5820 while ((*right == '0') && (isdigit(UCHAR(right[1])))) { 5821 right++; 5822 zeros--; 5823 } 5824 while ((*left == '0') && (isdigit(UCHAR(left[1])))) { 5825 left++; 5826 zeros++; 5827 } 5828 if (secondaryDiff == 0) { 5829 secondaryDiff = zeros; 5830 } 5831 5832 /* 5833 * The code below compares the numbers in the two 5834 * strings without ever converting them to integers. It 5835 * does this by first comparing the lengths of the 5836 * numbers and then comparing the digit values. 5837 */ 5838 5839 diff = 0; 5840 while (1) { 5841 if (diff == 0) { 5842 diff = UCHAR(*left) - UCHAR(*right); 5843 } 5844 right++; 5845 left++; 5846 if (!isdigit(UCHAR(*right))) { /* INTL: digit */ 5847 if (isdigit(UCHAR(*left))) { /* INTL: digit */ 5848 return 1; 5849 } else { 5850 /* 5851 * The two numbers have the same length. See 5852 * if their values are different. 5853 */ 5854 5855 if (diff != 0) { 5856 return diff; 5857 } 5858 break; 5859 } 5860 } else if (!isdigit(UCHAR(*left))) { /* INTL: digit */ 5861 return -1; 5862 } 5863 } 5864 continue; 5865 } 5866 5867 /* 5868 * Convert character to Unicode for comparison purposes. If either 5869 * string is at the terminating null, do a byte-wise comparison and 5870 * bail out immediately. 5871 */ 5872 5873 if ((*left != '\0') && (*right != '\0')) { 5874 left += Tcl_UtfToUniChar(left, &uniLeft); 5875 right += Tcl_UtfToUniChar(right, &uniRight); 5876 /* 5877 * Convert both chars to lower for the comparison, because 5878 * dictionary sorts are case insensitve. Covert to lower, not 5879 * upper, so chars between Z and a will sort before A (where most 5880 * other interesting punctuations occur) 5881 */ 5882 uniLeftLower = Tcl_UniCharToLower(uniLeft); 5883 uniRightLower = Tcl_UniCharToLower(uniRight); 5884 } else { 5885 diff = UCHAR(*left) - UCHAR(*right); 5886 break; 5887 } 5888 5889 diff = uniLeftLower - uniRightLower; 5890 if (diff) { 5891 return diff; 5892 } else if (secondaryDiff == 0) { 5893 if (Tcl_UniCharIsUpper(uniLeft) && 5894 Tcl_UniCharIsLower(uniRight)) { 5895 secondaryDiff = -1; 5896 } else if (Tcl_UniCharIsUpper(uniRight) && 5897 Tcl_UniCharIsLower(uniLeft)) { 5898 secondaryDiff = 1; 5899 } 5900 } 5901 } 5902 if (diff == 0) { 5903 diff = secondaryDiff; 5904 } 5905 return diff; 5906} 5907 5908static int 5909CompareAscii( 5910 SortData *sortData, 5911 struct SortItem *a, 5912 struct SortItem *b, 5913 int n /* Column index. */ 5914 ) 5915{ 5916 char *left = a->item1[n].string; 5917 char *right = b->item1[n].string; 5918 5919 /* make sure to handle case where no string value has been set */ 5920 if (left == NULL) { 5921 return ((right == NULL) ? 0 : (0 - UCHAR(*right))); 5922 } else if (right == NULL) { 5923 return UCHAR(*left); 5924 } else { 5925 return strcmp(left, right); 5926 } 5927} 5928 5929static int 5930CompareDict( 5931 SortData *sortData, 5932 struct SortItem *a, 5933 struct SortItem *b, 5934 int n /* Column index. */ 5935 ) 5936{ 5937 char *left = a->item1[n].string; 5938 char *right = b->item1[n].string; 5939 5940 /* make sure to handle case where no string value has been set */ 5941 if (left == NULL) { 5942 return ((right == NULL) ? 0 : (0 - UCHAR(*right))); 5943 } else if (right == NULL) { 5944 return UCHAR(*left); 5945 } else { 5946 return DictionaryCompare(left, right); 5947 } 5948} 5949 5950static int 5951CompareDouble( 5952 SortData *sortData, 5953 struct SortItem *a, 5954 struct SortItem *b, 5955 int n /* Column index. */ 5956 ) 5957{ 5958 return (a->item1[n].doubleValue < b->item1[n].doubleValue) ? -1 : 5959 ((a->item1[n].doubleValue == b->item1[n].doubleValue) ? 0 : 1); 5960} 5961 5962static int 5963CompareLong( 5964 SortData *sortData, 5965 struct SortItem *a, 5966 struct SortItem *b, 5967 int n /* Column index. */ 5968 ) 5969{ 5970 return (a->item1[n].longValue < b->item1[n].longValue) ? -1 : 5971 ((a->item1[n].longValue == b->item1[n].longValue) ? 0 : 1); 5972} 5973 5974static int 5975CompareCmd( 5976 SortData *sortData, 5977 struct SortItem *a, 5978 struct SortItem *b, 5979 int n /* Column index. */ 5980 ) 5981{ 5982 Tcl_Interp *interp = sortData->tree->interp; 5983 Tcl_Obj **objv, *paramObjv[2]; 5984 int objc, v; 5985 5986 paramObjv[0] = a->obj; 5987 paramObjv[1] = b->obj; 5988 5989 Tcl_ListObjLength(interp, sortData->columns[n].command, &objc); 5990 Tcl_ListObjReplace(interp, sortData->columns[n].command, objc - 2, 5991 2, 2, paramObjv); 5992 Tcl_ListObjGetElements(interp, sortData->columns[n].command, 5993 &objc, &objv); 5994 5995 sortData->result = Tcl_EvalObjv(interp, objc, objv, 0); 5996 5997 if (sortData->result != TCL_OK) { 5998 Tcl_AddErrorInfo(interp, "\n (evaluating item sort -command)"); 5999 return 0; 6000 } 6001 6002 sortData->result = Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &v); 6003 if (sortData->result != TCL_OK) { 6004 Tcl_ResetResult(interp); 6005 Tcl_AppendToObj(Tcl_GetObjResult(interp), 6006 "-command returned non-numeric result", -1); 6007 return 0; 6008 } 6009 6010 return v; 6011} 6012 6013static int 6014CompareProc( 6015 SortData *sortData, 6016 struct SortItem *a, 6017 struct SortItem *b 6018 ) 6019{ 6020 int i, v; 6021 6022 if (a->item == b->item) 6023 return 0; 6024 6025 for (i = 0; i < sortData->columnCount; i++) { 6026 v = (*sortData->columns[i].proc)(sortData, a, b, i); 6027 6028 /* -command returned error */ 6029 if (sortData->result != TCL_OK) 6030 return 0; 6031 6032 if (v != 0) { 6033 if (i && (sortData->columns[i].order != sortData->columns[0].order)) 6034 v *= -1; 6035 return v; 6036 } 6037 } 6038#ifdef STABLE_SORT 6039 return ((a->index < b->index) == sortData->columns[0].order) ? -1 : 1; 6040#else 6041 return 0; 6042#endif 6043} 6044 6045/* BEGIN custom quicksort() */ 6046 6047static int 6048find_pivot( 6049 SortData *sortData, 6050 struct SortItem *left, 6051 struct SortItem *right, 6052 struct SortItem *pivot 6053 ) 6054{ 6055 struct SortItem *a, *b, *c, *p, *tmp; 6056 int v; 6057 6058 a = left; 6059 b = (left + (right - left) / 2); 6060 c = right; 6061 6062 /* Arrange a <= b <= c. */ 6063 v = CompareProc(sortData, a, b); 6064 if (sortData->result != TCL_OK) 6065 return 0; 6066 if (v > 0) { tmp = a; a = b; b = tmp; } 6067 6068 v = CompareProc(sortData, a, c); 6069 if (sortData->result != TCL_OK) 6070 return 0; 6071 if (v > 0) { tmp = a; a = c; c = tmp; } 6072 6073 v = CompareProc(sortData, b, c); 6074 if (sortData->result != TCL_OK) 6075 return 0; 6076 if (v > 0) { tmp = b; b = c; c = tmp; } 6077 6078 /* if (a < b) pivot = b */ 6079 v = CompareProc(sortData, a, b); 6080 if (sortData->result != TCL_OK) 6081 return 0; 6082 if (v < 0) { 6083 (*pivot) = *b; 6084 return 1; 6085 } 6086 6087 /* if (b < c) pivot = c */ 6088 v = CompareProc(sortData, b, c); 6089 if (sortData->result != TCL_OK) 6090 return 0; 6091 if (v < 0) { 6092 (*pivot) = *c; 6093 return 1; 6094 } 6095 6096 for (p = left + 1; p <= right; p++) { 6097 int v = CompareProc(sortData, p, left); 6098 if (sortData->result != TCL_OK) 6099 return 0; 6100 if (v != 0) { 6101 (*pivot) = (v < 0) ? *left : *p; 6102 return 1; 6103 } 6104 } 6105 return 0; 6106} 6107 6108/* If the user provides a -command which does not properly compare two 6109 * elements, quicksort may go into an infinite loop or access illegal memory. 6110 * This #define indicates parts of the code which are not part of a normal 6111 * quicksort, but are present to detect the aforementioned bugs. */ 6112#define BUGGY_COMMAND 6113 6114static struct SortItem * 6115partition( 6116 SortData *sortData, 6117 struct SortItem *left, 6118 struct SortItem *right, 6119 struct SortItem *pivot 6120 ) 6121{ 6122 int v; 6123#ifdef BUGGY_COMMAND 6124 struct SortItem *min = left, *max = right; 6125#endif 6126 6127 while (left <= right) { 6128 /* 6129 while (*left < *pivot) 6130 ++left; 6131 */ 6132 while (1) { 6133 v = CompareProc(sortData, left, pivot); 6134 if (sortData->result != TCL_OK) 6135 return NULL; 6136 if (v >= 0) 6137 break; 6138#ifdef BUGGY_COMMAND 6139 /* If -command always returns < 0, 'left' becomes invalid */ 6140 if (left == max) 6141 goto buggy; 6142#endif 6143 left++; 6144 } 6145 /* 6146 while (*right >= *pivot) 6147 --right; 6148 */ 6149 while (1) { 6150 v = CompareProc(sortData, right, pivot); 6151 if (sortData->result != TCL_OK) 6152 return NULL; 6153 if (v < 0) 6154 break; 6155#ifdef BUGGY_COMMAND 6156 /* If -command always returns >= 0, 'right' becomes invalid */ 6157 if (right == min) 6158 goto buggy; 6159#endif 6160 right--; 6161 } 6162 if (left < right) { 6163 struct SortItem tmp = *left; 6164 *left = *right; 6165 *right = tmp; 6166 left++; 6167 right--; 6168 } 6169 } 6170 return left; 6171#ifdef BUGGY_COMMAND 6172 buggy: 6173 FormatResult(sortData->tree->interp, "buggy item sort -command detected"); 6174 sortData->result = TCL_ERROR; 6175 return NULL; 6176#endif 6177} 6178 6179static void 6180quicksort( 6181 SortData *sortData, 6182 struct SortItem *left, 6183 struct SortItem *right 6184 ) 6185{ 6186 struct SortItem *p, pivot; 6187 6188 if (sortData->result != TCL_OK) 6189 return; 6190 6191 if (left == right) 6192 return; 6193 6194 /* FIXME: switch to insertion sort or similar when the number of 6195 * elements is small. */ 6196 6197 if (find_pivot(sortData, left, right, &pivot) == 1) { 6198 p = partition(sortData, left, right, &pivot); 6199 quicksort(sortData, left, p - 1); 6200 quicksort(sortData, p, right); 6201 } 6202} 6203 6204/* END custom quicksort() */ 6205 6206/* 6207 *---------------------------------------------------------------------- 6208 * 6209 * ItemSortCmd -- 6210 * 6211 * This procedure is invoked to process the [item sort] widget 6212 * command. See the user documentation for details on what 6213 * it does. 6214 * 6215 * Results: 6216 * A standard Tcl result. 6217 * 6218 * Side effects: 6219 * See the user documentation. 6220 * 6221 *---------------------------------------------------------------------- 6222 */ 6223 6224static int 6225ItemSortCmd( 6226 ClientData clientData, /* Widget info. */ 6227 Tcl_Interp *interp, /* Current interpreter. */ 6228 int objc, /* Number of arguments. */ 6229 Tcl_Obj *CONST objv[] /* Argument values. */ 6230 ) 6231{ 6232 TreeCtrl *tree = clientData; 6233 TreeItem item, first, last, walk, lastChild; 6234 Column *column; 6235 int i, j, count, elemIndex, index, indexF = 0, indexL = 0; 6236 int sawColumn = FALSE, sawCmd = FALSE; 6237 static int (*sortProc[5])(SortData *, struct SortItem *, struct SortItem *, int) = 6238 { CompareAscii, CompareDict, CompareDouble, CompareLong, CompareCmd }; 6239 SortData sortData; 6240 TreeColumn treeColumn; 6241 struct SortElement *elemPtr; 6242 int notReally = FALSE; 6243 int result = TCL_OK; 6244 6245 if (objc < 4) { 6246 Tcl_WrongNumArgs(interp, 3, objv, "item ?option ...?"); 6247 return TCL_ERROR; 6248 } 6249 6250 if (TreeItem_FromObj(tree, objv[3], &item, IFO_NOT_NULL) != TCL_OK) 6251 return TCL_ERROR; 6252 6253 /* If the item has no children, then nothing is done and no error 6254 * is generated. */ 6255 if (item->numChildren < 1) 6256 return TCL_OK; 6257 6258 /* Defaults: sort ascii strings in column 0 only */ 6259 sortData.tree = tree; 6260 sortData.columnCount = 1; 6261 sortData.columns[0].column = 0; 6262 sortData.columns[0].sortBy = SORT_ASCII; 6263 sortData.columns[0].order = 1; 6264 sortData.columns[0].elemCount = 0; 6265 sortData.result = TCL_OK; 6266 6267 first = item->firstChild; 6268 last = item->lastChild; 6269 6270 for (i = 4; i < objc; ) { 6271 static CONST char *optionName[] = { "-ascii", "-column", "-command", 6272 "-decreasing", "-dictionary", "-element", "-first", "-increasing", 6273 "-integer", "-last", "-notreally", "-real", NULL }; 6274 int numArgs[] = { 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 1, 1 }; 6275 enum { OPT_ASCII, OPT_COLUMN, OPT_COMMAND, OPT_DECREASING, OPT_DICT, 6276 OPT_ELEMENT, OPT_FIRST, OPT_INCREASING, OPT_INTEGER, OPT_LAST, 6277 OPT_NOT_REALLY, OPT_REAL }; 6278 6279 if (Tcl_GetIndexFromObj(interp, objv[i], optionName, "option", 0, 6280 &index) != TCL_OK) 6281 return TCL_ERROR; 6282 if (objc - i < numArgs[index]) { 6283 FormatResult(interp, "missing value for \"%s\" option", 6284 optionName[index]); 6285 return TCL_ERROR; 6286 } 6287 switch (index) { 6288 case OPT_ASCII: 6289 sortData.columns[sortData.columnCount - 1].sortBy = SORT_ASCII; 6290 break; 6291 case OPT_COLUMN: 6292 if (TreeColumn_FromObj(tree, objv[i + 1], &treeColumn, 6293 CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) 6294 return TCL_ERROR; 6295 /* The first -column we see is the first column we compare */ 6296 if (sawColumn) { 6297 if (sortData.columnCount + 1 > MAX_SORT_COLUMNS) { 6298 FormatResult(interp, 6299 "can't compare more than %d columns", 6300 MAX_SORT_COLUMNS); 6301 return TCL_ERROR; 6302 } 6303 sortData.columnCount++; 6304 /* Defaults for this column */ 6305 sortData.columns[sortData.columnCount - 1].sortBy = SORT_ASCII; 6306 sortData.columns[sortData.columnCount - 1].order = 1; 6307 sortData.columns[sortData.columnCount - 1].elemCount = 0; 6308 } 6309 sortData.columns[sortData.columnCount - 1].column = TreeColumn_Index(treeColumn); 6310 sawColumn = TRUE; 6311 break; 6312 case OPT_COMMAND: 6313 sortData.columns[sortData.columnCount - 1].command = objv[i + 1]; 6314 sortData.columns[sortData.columnCount - 1].sortBy = SORT_COMMAND; 6315 sawCmd = TRUE; 6316 break; 6317 case OPT_DECREASING: 6318 sortData.columns[sortData.columnCount - 1].order = 0; 6319 break; 6320 case OPT_DICT: 6321 sortData.columns[sortData.columnCount - 1].sortBy = SORT_DICT; 6322 break; 6323 case OPT_ELEMENT: { 6324 int listObjc; 6325 Tcl_Obj **listObjv; 6326 6327 if (Tcl_ListObjGetElements(interp, objv[i + 1], &listObjc, 6328 &listObjv) != TCL_OK) 6329 return TCL_ERROR; 6330 elemPtr = sortData.columns[sortData.columnCount - 1].elems; 6331 sortData.columns[sortData.columnCount - 1].elemCount = 0; 6332 if (listObjc == 0) { 6333 } else if (listObjc == 1) { 6334 if (TreeElement_FromObj(tree, listObjv[0], &elemPtr->elem) 6335 != TCL_OK) { 6336 Tcl_AddErrorInfo(interp, 6337 "\n (processing -element option)"); 6338 return TCL_ERROR; 6339 } 6340 if (!TreeElement_IsType(tree, elemPtr->elem, "text")) { 6341 FormatResult(interp, 6342 "element %s is not of type \"text\"", 6343 Tcl_GetString(listObjv[0])); 6344 Tcl_AddErrorInfo(interp, 6345 "\n (processing -element option)"); 6346 return TCL_ERROR; 6347 } 6348 elemPtr->style = NULL; 6349 elemPtr->elemIndex = -1; 6350 sortData.columns[sortData.columnCount - 1].elemCount++; 6351 } else { 6352 if (listObjc & 1) { 6353 FormatResult(interp, 6354 "list must have even number of elements"); 6355 Tcl_AddErrorInfo(interp, 6356 "\n (processing -element option)"); 6357 return TCL_ERROR; 6358 } 6359 for (j = 0; j < listObjc; j += 2) { 6360 if ((TreeStyle_FromObj(tree, listObjv[j], 6361 &elemPtr->style) != TCL_OK) || 6362 (TreeElement_FromObj(tree, listObjv[j + 1], 6363 &elemPtr->elem) != TCL_OK) || 6364 (TreeStyle_FindElement(tree, elemPtr->style, 6365 elemPtr->elem, &elemPtr->elemIndex) != TCL_OK)) { 6366 Tcl_AddErrorInfo(interp, 6367 "\n (processing -element option)"); 6368 return TCL_ERROR; 6369 } 6370 if (!TreeElement_IsType(tree, elemPtr->elem, "text")) { 6371 FormatResult(interp, 6372 "element %s is not of type \"text\"", 6373 Tcl_GetString(listObjv[j + 1])); 6374 Tcl_AddErrorInfo(interp, 6375 "\n (processing -element option)"); 6376 return TCL_ERROR; 6377 } 6378 sortData.columns[sortData.columnCount - 1].elemCount++; 6379 elemPtr++; 6380 } 6381 } 6382 break; 6383 } 6384 case OPT_FIRST: 6385 if (TreeItem_FromObj(tree, objv[i + 1], &first, IFO_NOT_NULL) != TCL_OK) 6386 return TCL_ERROR; 6387 if (first->parent != item) { 6388 FormatResult(interp, 6389 "item %s%d is not a child of item %s%d", 6390 tree->itemPrefix, first->id, tree->itemPrefix, item->id); 6391 return TCL_ERROR; 6392 } 6393 break; 6394 case OPT_INCREASING: 6395 sortData.columns[sortData.columnCount - 1].order = 1; 6396 break; 6397 case OPT_INTEGER: 6398 sortData.columns[sortData.columnCount - 1].sortBy = SORT_LONG; 6399 break; 6400 case OPT_LAST: 6401 if (TreeItem_FromObj(tree, objv[i + 1], &last, IFO_NOT_NULL) != TCL_OK) 6402 return TCL_ERROR; 6403 if (last->parent != item) { 6404 FormatResult(interp, 6405 "item %s%d is not a child of item %s%d", 6406 tree->itemPrefix, last->id, tree->itemPrefix, item->id); 6407 return TCL_ERROR; 6408 } 6409 break; 6410 case OPT_NOT_REALLY: 6411 notReally = TRUE; 6412 break; 6413 case OPT_REAL: 6414 sortData.columns[sortData.columnCount - 1].sortBy = SORT_DOUBLE; 6415 break; 6416 } 6417 i += numArgs[index]; 6418 } 6419 6420 /* If there are no columns, we cannot perform a sort unless -command 6421 * is specified. */ 6422 if ((tree->columnCount < 1) && (sortData.columns[0].sortBy != SORT_COMMAND)) { 6423 FormatResult(interp, "there are no columns"); 6424 return TCL_ERROR; 6425 } 6426 6427 /* If there is only one item to sort, then return early. */ 6428 if (first == last) { 6429 if (notReally) 6430 Tcl_SetObjResult(interp, TreeItem_ToObj(tree, first)); 6431 return TCL_OK; 6432 } 6433 6434 for (i = 0; i < sortData.columnCount; i++) { 6435 6436 /* Initialize the sort procedure for this column. */ 6437 sortData.columns[i].proc = sortProc[sortData.columns[i].sortBy]; 6438 6439 /* Append two dummy args to the -command argument. These two dummy 6440 * args are replaced by the 2 item ids being compared. See 6441 * CompareCmd(). */ 6442 if (sortData.columns[i].sortBy == SORT_COMMAND) { 6443 Tcl_Obj *obj = Tcl_DuplicateObj(sortData.columns[i].command); 6444 Tcl_Obj *obj2 = Tcl_NewObj(); 6445 Tcl_IncrRefCount(obj); 6446 if (Tcl_ListObjAppendElement(interp, obj, obj2) != TCL_OK) { 6447 Tcl_DecrRefCount(obj); 6448 Tcl_IncrRefCount(obj2); 6449 Tcl_DecrRefCount(obj2); 6450 6451 for (j = 0; j < i; j++) { 6452 if (sortData.columns[j].sortBy == SORT_COMMAND) { 6453 Tcl_DecrRefCount(sortData.columns[j].command); 6454 } 6455 } 6456 6457 return TCL_ERROR; 6458 } 6459 (void) Tcl_ListObjAppendElement(interp, obj, obj2); 6460 sortData.columns[i].command = obj; 6461 } 6462 } 6463 6464 index = 0; 6465 walk = item->firstChild; 6466 while (walk != NULL) { 6467 if (walk == first) 6468 indexF = index; 6469 if (walk == last) 6470 indexL = index; 6471 index++; 6472 walk = walk->nextSibling; 6473 } 6474 if (indexF > indexL) { 6475 walk = last; 6476 last = first; 6477 first = walk; 6478 6479 index = indexL; 6480 indexL = indexF; 6481 indexF = index; 6482 } 6483 count = indexL - indexF + 1; 6484 6485 sortData.item1s = (struct SortItem1 *) ckalloc(sizeof(struct SortItem1) * count * sortData.columnCount); 6486 sortData.items = (struct SortItem *) ckalloc(sizeof(struct SortItem) * count); 6487 for (i = 0; i < count; i++) { 6488 sortData.items[i].item1 = sortData.item1s + i * sortData.columnCount; 6489 sortData.items[i].obj = NULL; 6490 } 6491 6492 index = 0; 6493 walk = first; 6494 while (walk != last->nextSibling) { 6495 struct SortItem *sortItem = &sortData.items[index]; 6496 6497 sortItem->item = walk; 6498#ifdef STABLE_SORT 6499 sortItem->index = index; 6500#endif 6501 if (sawCmd) { 6502 Tcl_Obj *obj = TreeItem_ToObj(tree, walk); 6503 Tcl_IncrRefCount(obj); 6504 sortData.items[index].obj = obj; 6505 } 6506 for (i = 0; i < sortData.columnCount; i++) { 6507 struct SortItem1 *sortItem1 = sortItem->item1 + i; 6508 6509 if (sortData.columns[i].sortBy == SORT_COMMAND) 6510 continue; 6511 6512 column = Item_FindColumn(tree, walk, sortData.columns[i].column); 6513 if ((column == NULL) || (column->style == NULL)) { 6514 NoStyleMsg(tree, walk, sortData.columns[i].column); 6515 result = TCL_ERROR; 6516 goto done; 6517 } 6518 6519 /* -element was empty. Find the first text element in the style */ 6520 if (sortData.columns[i].elemCount == 0) 6521 elemIndex = -1; 6522 6523 /* -element was element name. Find the element in the style */ 6524 else if ((sortData.columns[i].elemCount == 1) && 6525 (sortData.columns[i].elems[0].style == NULL)) { 6526 if (TreeStyle_FindElement(tree, column->style, 6527 sortData.columns[i].elems[0].elem, &elemIndex) != TCL_OK) { 6528 result = TCL_ERROR; 6529 goto done; 6530 } 6531 } 6532 6533 /* -element was style/element pair list */ 6534 else { 6535 TreeStyle masterStyle = TreeStyle_GetMaster(tree, column->style); 6536 6537 /* If the item style does not match any in the -element list, 6538 * we will use the first text element in the item style. */ 6539 elemIndex = -1; 6540 6541 /* Match a style from the -element list. Look in reverse order 6542 * to handle duplicates. */ 6543 for (j = sortData.columns[i].elemCount - 1; j >= 0; j--) { 6544 if (sortData.columns[i].elems[j].style == masterStyle) { 6545 elemIndex = sortData.columns[i].elems[j].elemIndex; 6546 break; 6547 } 6548 } 6549 } 6550 if (TreeStyle_GetSortData(tree, column->style, elemIndex, 6551 sortData.columns[i].sortBy, 6552 &sortItem1->longValue, 6553 &sortItem1->doubleValue, 6554 &sortItem1->string) != TCL_OK) { 6555 char msg[128]; 6556 sprintf(msg, "\n (preparing to sort item %s%d column %s%d)", 6557 tree->itemPrefix, walk->id, 6558 tree->columnPrefix, TreeColumn_GetID( 6559 Tree_FindColumn(tree, sortData.columns[i].column))); 6560 Tcl_AddErrorInfo(interp, msg); 6561 result = TCL_ERROR; 6562 goto done; 6563 } 6564 } 6565 index++; 6566 walk = walk->nextSibling; 6567 } 6568 6569 quicksort(&sortData, sortData.items, sortData.items + count - 1); 6570 6571 if (sortData.result != TCL_OK) { 6572 result = sortData.result; 6573 goto done; 6574 } 6575 6576 if (sawCmd) 6577 Tcl_ResetResult(interp); 6578 6579 if (notReally) { 6580 Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); 6581 Tcl_Obj *itemObj; 6582 6583 /* Smallest to largest */ 6584 if (sortData.columns[0].order == 1) { 6585 for (i = 0; i < count; i++) { 6586 itemObj = sortData.items[i].obj; 6587 if (itemObj == NULL) 6588 itemObj = TreeItem_ToObj(tree, 6589 sortData.items[i].item); 6590 Tcl_ListObjAppendElement(interp, listObj, itemObj); 6591 } 6592 } 6593 6594 /* Largest to smallest */ 6595 else { 6596 for (i = count - 1; i >= 0; i--) { 6597 itemObj = sortData.items[i].obj; 6598 if (itemObj == NULL) 6599 itemObj = TreeItem_ToObj(tree, 6600 sortData.items[i].item); 6601 Tcl_ListObjAppendElement(interp, listObj, itemObj); 6602 } 6603 } 6604 6605 Tcl_SetObjResult(interp, listObj); 6606 goto done; 6607 } 6608 first = first->prevSibling; 6609 last = last->nextSibling; 6610 6611 /* Smallest to largest */ 6612 if (sortData.columns[0].order == 1) { 6613 for (i = 0; i < count - 1; i++) { 6614 sortData.items[i].item->nextSibling = sortData.items[i + 1].item; 6615 sortData.items[i + 1].item->prevSibling = sortData.items[i].item; 6616 } 6617 indexF = 0; 6618 indexL = count - 1; 6619 } 6620 6621 /* Largest to smallest */ 6622 else { 6623 for (i = count - 1; i > 0; i--) { 6624 sortData.items[i].item->nextSibling = sortData.items[i - 1].item; 6625 sortData.items[i - 1].item->prevSibling = sortData.items[i].item; 6626 } 6627 indexF = count - 1; 6628 indexL = 0; 6629 } 6630 6631 lastChild = item->lastChild; 6632 6633 sortData.items[indexF].item->prevSibling = first; 6634 if (first) 6635 first->nextSibling = sortData.items[indexF].item; 6636 else 6637 item->firstChild = sortData.items[indexF].item; 6638 6639 sortData.items[indexL].item->nextSibling = last; 6640 if (last) 6641 last->prevSibling = sortData.items[indexL].item; 6642 else 6643 item->lastChild = sortData.items[indexL].item; 6644 6645 /* Redraw the lines of the old/new lastchild */ 6646 if ((item->lastChild != lastChild) && tree->showLines && (tree->columnTree != NULL)) { 6647 if (lastChild->dInfo != NULL) 6648 Tree_InvalidateItemDInfo(tree, tree->columnTree, 6649 lastChild, 6650 NULL); 6651 if (item->lastChild->dInfo != NULL) 6652 Tree_InvalidateItemDInfo(tree, tree->columnTree, 6653 item->lastChild, 6654 NULL); 6655 } 6656 6657 tree->updateIndex = 1; 6658 Tree_DInfoChanged(tree, DINFO_REDO_RANGES); 6659 6660 done: 6661 for (i = 0; i < count; i++) { 6662 if (sortData.items[i].obj != NULL) { 6663 Tcl_DecrRefCount(sortData.items[i].obj); 6664 } 6665 } 6666 for (i = 0; i < sortData.columnCount; i++) { 6667 if (sortData.columns[i].sortBy == SORT_COMMAND) { 6668 Tcl_DecrRefCount(sortData.columns[i].command); 6669 } 6670 } 6671 ckfree((char *) sortData.item1s); 6672 ckfree((char *) sortData.items); 6673 6674 if (tree->debug.enable && tree->debug.data) { 6675 Tree_Debug(tree); 6676 } 6677 6678 return result; 6679} 6680 6681/* 6682 *---------------------------------------------------------------------- 6683 * 6684 * TreeItemList_Sort -- 6685 * 6686 * Sorts a list of items. 6687 * 6688 * Results: 6689 * None. 6690 * 6691 * Side effects: 6692 * None. 6693 * 6694 *---------------------------------------------------------------------- 6695 */ 6696 6697static int 6698TILSCompare( 6699 CONST VOID *first_, 6700 CONST VOID *second_ 6701 ) 6702{ 6703 TreeItem first = *(TreeItem *) first_; 6704 TreeItem second = *(TreeItem *) second_; 6705 6706 return first->index - second->index; 6707} 6708 6709void 6710TreeItemList_Sort( 6711 TreeItemList *items 6712 ) 6713{ 6714 Tree_UpdateItemIndex(items->tree); 6715 6716 /* TkTable uses this, but mentions possible lack of thread-safety. */ 6717 qsort((VOID *) TreeItemList_Items(items), 6718 (size_t) TreeItemList_Count(items), 6719 sizeof(TreeItem), 6720 TILSCompare); 6721} 6722 6723/* 6724 *---------------------------------------------------------------------- 6725 * 6726 * ItemStateCmd -- 6727 * 6728 * This procedure is invoked to process the [item state] widget 6729 * command. See the user documentation for details on what 6730 * it does. 6731 * 6732 * Results: 6733 * A standard Tcl result. 6734 * 6735 * Side effects: 6736 * See the user documentation. 6737 * 6738 *---------------------------------------------------------------------- 6739 */ 6740 6741static int 6742ItemStateCmd( 6743 ClientData clientData, /* Widget info. */ 6744 Tcl_Interp *interp, /* Current interpreter. */ 6745 int objc, /* Number of arguments. */ 6746 Tcl_Obj *CONST objv[] /* Argument values. */ 6747 ) 6748{ 6749 TreeCtrl *tree = clientData; 6750 static CONST char *commandNames[] = { 6751 "forcolumn", "get", "set", (char *) NULL 6752 }; 6753 enum { 6754 COMMAND_FORCOLUMN, COMMAND_GET, COMMAND_SET 6755 }; 6756 int index; 6757 TreeItem item; 6758 6759 if (objc < 5) { 6760 Tcl_WrongNumArgs(interp, 3, objv, "command item ?arg ...?"); 6761 return TCL_ERROR; 6762 } 6763 6764 if (Tcl_GetIndexFromObj(interp, objv[3], commandNames, "command", 0, 6765 &index) != TCL_OK) 6766 return TCL_ERROR; 6767 6768 switch (index) { 6769 /* T item state forcolumn I C ?stateList? */ 6770 case COMMAND_FORCOLUMN: { 6771 TreeItemList itemList; 6772 TreeColumnList columns; 6773 TreeColumn treeColumn; 6774 Tcl_Obj *listObj; 6775 Column *column; 6776 int columnIndex; 6777 int i, states[3], stateOn, stateOff; 6778 ItemForEach iter; 6779 ColumnForEach citer; 6780 int flags = IFO_NOT_NULL; 6781 int result = TCL_OK; 6782 6783 if (objc < 6 || objc > 7) { 6784 Tcl_WrongNumArgs(interp, 4, objv, "item column ?stateList?"); 6785 return TCL_ERROR; 6786 } 6787 /* Without a stateList only one item is accepted. */ 6788 if (objc == 6) 6789 flags |= IFO_NOT_MANY; 6790 if (TreeItemList_FromObj(tree, objv[4], &itemList, flags) 6791 != TCL_OK) 6792 return TCL_ERROR; 6793 TreeColumnList_Init(tree, &columns, 0); 6794 if (objc == 6) { 6795 item = TreeItemList_Nth(&itemList, 0); 6796 if (Item_FindColumnFromObj(tree, item, objv[5], &column, 6797 &columnIndex) != TCL_OK) { 6798 result = TCL_ERROR; 6799 goto doneFORC; 6800 } 6801 if ((column == NULL) || !column->cstate) 6802 goto doneFORC; 6803 listObj = Tcl_NewListObj(0, NULL); 6804 for (i = 0; i < 32; i++) { 6805 if (tree->stateNames[i] == NULL) 6806 continue; 6807 if (column->cstate & (1L << i)) { 6808 Tcl_ListObjAppendElement(interp, listObj, 6809 Tcl_NewStringObj(tree->stateNames[i], -1)); 6810 } 6811 } 6812 Tcl_SetObjResult(interp, listObj); 6813 goto doneFORC; 6814 } 6815 if (TreeColumnList_FromObj(tree, objv[5], &columns, 6816 CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) { 6817 result = TCL_ERROR; 6818 goto doneFORC; 6819 } 6820 if (Tree_StateFromListObj(tree, objv[6], states, SFO_NOT_STATIC) != TCL_OK) { 6821 result = TCL_ERROR; 6822 goto doneFORC; 6823 } 6824 if ((states[0] | states[1] | states[2]) == 0) 6825 goto doneFORC; 6826 ITEM_FOR_EACH(item, &itemList, NULL, &iter) { 6827 COLUMN_FOR_EACH(treeColumn, &columns, NULL, &citer) { 6828 columnIndex = TreeColumn_Index(treeColumn); 6829 column = Item_CreateColumn(tree, item, columnIndex, NULL); 6830 stateOn = states[STATE_OP_ON]; 6831 stateOff = states[STATE_OP_OFF]; 6832 stateOn |= ~column->cstate & states[STATE_OP_TOGGLE]; 6833 stateOff |= column->cstate & states[STATE_OP_TOGGLE]; 6834 Column_ChangeState(tree, item, column, treeColumn, 6835 stateOff, stateOn); 6836 } 6837 } 6838doneFORC: 6839 TreeColumnList_Free(&columns); 6840 TreeItemList_Free(&itemList); 6841 return result; 6842 } 6843 6844 /* T item state get I ?state? */ 6845 case COMMAND_GET: { 6846 Tcl_Obj *listObj; 6847 int i, states[3]; 6848 6849 if (objc > 6) { 6850 Tcl_WrongNumArgs(interp, 5, objv, "?state?"); 6851 return TCL_ERROR; 6852 } 6853 if (TreeItem_FromObj(tree, objv[4], &item, IFO_NOT_NULL) != TCL_OK) 6854 return TCL_ERROR; 6855 if (objc == 6) { 6856 states[STATE_OP_ON] = 0; 6857 if (Tree_StateFromObj(tree, objv[5], states, NULL, 6858 SFO_NOT_OFF | SFO_NOT_TOGGLE) != TCL_OK) 6859 return TCL_ERROR; 6860 Tcl_SetObjResult(interp, 6861 Tcl_NewBooleanObj((item->state & states[STATE_OP_ON]) != 0)); 6862 break; 6863 } 6864 listObj = Tcl_NewListObj(0, NULL); 6865 for (i = 0; i < 32; i++) { 6866 if (tree->stateNames[i] == NULL) 6867 continue; 6868 if (item->state & (1L << i)) { 6869 Tcl_ListObjAppendElement(interp, listObj, 6870 Tcl_NewStringObj(tree->stateNames[i], -1)); 6871 } 6872 } 6873 Tcl_SetObjResult(interp, listObj); 6874 break; 6875 } 6876 6877 /* T item state set I ?I? {state ...} */ 6878 case COMMAND_SET: { 6879 TreeItemList itemList, item2List; 6880 int states[3], stateOn, stateOff; 6881 ItemForEach iter; 6882 int result = TCL_OK; 6883 6884 if (objc < 6 || objc > 7) { 6885 Tcl_WrongNumArgs(interp, 5, objv, "?last? stateList"); 6886 return TCL_ERROR; 6887 } 6888 if (TreeItemList_FromObj(tree, objv[4], &itemList, IFO_NOT_NULL) != TCL_OK) 6889 return TCL_ERROR; 6890 if (objc == 6) { 6891 TreeItemList_Init(tree, &item2List, 0); 6892 } 6893 if (objc == 7) { 6894 if (TreeItemList_FromObj(tree, objv[5], &item2List, IFO_NOT_NULL) != TCL_OK) { 6895 result = TCL_ERROR; 6896 goto doneSET; 6897 } 6898 } 6899 if (Tree_StateFromListObj(tree, objv[objc - 1], states, 6900 SFO_NOT_STATIC) != TCL_OK) { 6901 result = TCL_ERROR; 6902 goto doneSET; 6903 } 6904 if ((states[0] | states[1] | states[2]) == 0) 6905 goto doneSET; 6906 ITEM_FOR_EACH(item, &itemList, &item2List, &iter) { 6907 stateOn = states[STATE_OP_ON]; 6908 stateOff = states[STATE_OP_OFF]; 6909 stateOn |= ~item->state & states[STATE_OP_TOGGLE]; 6910 stateOff |= item->state & states[STATE_OP_TOGGLE]; 6911 TreeItem_ChangeState(tree, item, stateOff, stateOn); 6912 } 6913 if (iter.error) 6914 result = TCL_ERROR; 6915doneSET: 6916 TreeItemList_Free(&itemList); 6917 TreeItemList_Free(&item2List); 6918 return result; 6919 } 6920 } 6921 6922 return TCL_OK; 6923} 6924 6925/* 6926 *---------------------------------------------------------------------- 6927 * 6928 * ItemTagCmd -- 6929 * 6930 * This procedure is invoked to process the [item tag] widget 6931 * command. See the user documentation for details on what 6932 * it does. 6933 * 6934 * Results: 6935 * A standard Tcl result. 6936 * 6937 * Side effects: 6938 * See the user documentation. 6939 * 6940 *---------------------------------------------------------------------- 6941 */ 6942 6943static int 6944ItemTagCmd( 6945 ClientData clientData, /* Widget info. */ 6946 Tcl_Interp *interp, /* Current interpreter. */ 6947 int objc, /* Number of arguments. */ 6948 Tcl_Obj *CONST objv[] /* Argument values. */ 6949 ) 6950{ 6951 TreeCtrl *tree = clientData; 6952 static CONST char *commandNames[] = { 6953 "add", "expr", "names", "remove", (char *) NULL 6954 }; 6955 enum { 6956 COMMAND_ADD, COMMAND_EXPR, COMMAND_NAMES, COMMAND_REMOVE 6957 }; 6958 int index; 6959 ItemForEach iter; 6960 TreeItemList items; 6961 TreeItem item; 6962 int result = TCL_OK; 6963 6964 if (objc < 4) { 6965 Tcl_WrongNumArgs(interp, 3, objv, "command ?arg arg ...?"); 6966 return TCL_ERROR; 6967 } 6968 6969 if (Tcl_GetIndexFromObj(interp, objv[3], commandNames, "command", 0, 6970 &index) != TCL_OK) { 6971 return TCL_ERROR; 6972 } 6973 6974 switch (index) { 6975 /* T item tag add I tagList */ 6976 case COMMAND_ADD: { 6977 int i, numTags; 6978 Tcl_Obj **listObjv; 6979 Tk_Uid staticTags[STATIC_SIZE], *tags = staticTags; 6980 6981 if (objc != 6) { 6982 Tcl_WrongNumArgs(interp, 4, objv, "item tagList"); 6983 return TCL_ERROR; 6984 } 6985 if (TreeItemList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) { 6986 return TCL_ERROR; 6987 } 6988 if (Tcl_ListObjGetElements(interp, objv[5], &numTags, &listObjv) != TCL_OK) { 6989 result = TCL_ERROR; 6990 break; 6991 } 6992 STATIC_ALLOC(tags, Tk_Uid, numTags); 6993 for (i = 0; i < numTags; i++) { 6994 tags[i] = Tk_GetUid(Tcl_GetString(listObjv[i])); 6995 } 6996 ITEM_FOR_EACH(item, &items, NULL, &iter) { 6997 item->tagInfo = TagInfo_Add(tree, item->tagInfo, tags, numTags); 6998 } 6999 STATIC_FREE(tags, Tk_Uid, numTags); 7000 break; 7001 } 7002 7003 /* T item tag expr I tagExpr */ 7004 case COMMAND_EXPR: { 7005 TagExpr expr; 7006 int ok = TRUE; 7007 7008 if (objc != 6) { 7009 Tcl_WrongNumArgs(interp, 4, objv, "item tagExpr"); 7010 return TCL_ERROR; 7011 } 7012 if (TreeItemList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) { 7013 return TCL_ERROR; 7014 } 7015 if (TagExpr_Init(tree, objv[5], &expr) != TCL_OK) { 7016 result = TCL_ERROR; 7017 break; 7018 } 7019 ITEM_FOR_EACH(item, &items, NULL, &iter) { 7020 if (!TagExpr_Eval(&expr, item->tagInfo)) { 7021 ok = FALSE; 7022 break; 7023 } 7024 } 7025 TagExpr_Free(&expr); 7026 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(ok)); 7027 break; 7028 } 7029 7030 /* T item tag names I */ 7031 case COMMAND_NAMES: { 7032 Tcl_Obj *listObj; 7033 Tk_Uid *tags = NULL; 7034 int i, tagSpace, numTags = 0; 7035 7036 if (objc != 5) { 7037 Tcl_WrongNumArgs(interp, 4, objv, "item"); 7038 return TCL_ERROR; 7039 } 7040 if (TreeItemList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) { 7041 return TCL_ERROR; 7042 } 7043 ITEM_FOR_EACH(item, &items, NULL, &iter) { 7044 tags = TagInfo_Names(tree, item->tagInfo, tags, &numTags, &tagSpace); 7045 } 7046 if (numTags) { 7047 listObj = Tcl_NewListObj(0, NULL); 7048 for (i = 0; i < numTags; i++) { 7049 Tcl_ListObjAppendElement(NULL, listObj, 7050 Tcl_NewStringObj((char *) tags[i], -1)); 7051 } 7052 Tcl_SetObjResult(interp, listObj); 7053 ckfree((char *) tags); 7054 } 7055 break; 7056 } 7057 7058 /* T item tag remove I tagList */ 7059 case COMMAND_REMOVE: { 7060 int i, numTags; 7061 Tcl_Obj **listObjv; 7062 Tk_Uid staticTags[STATIC_SIZE], *tags = staticTags; 7063 7064 if (objc != 6) { 7065 Tcl_WrongNumArgs(interp, 4, objv, "item tagList"); 7066 return TCL_ERROR; 7067 } 7068 if (TreeItemList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) { 7069 return TCL_ERROR; 7070 } 7071 if (Tcl_ListObjGetElements(interp, objv[5], &numTags, &listObjv) != TCL_OK) { 7072 result = TCL_ERROR; 7073 break; 7074 } 7075 STATIC_ALLOC(tags, Tk_Uid, numTags); 7076 for (i = 0; i < numTags; i++) { 7077 tags[i] = Tk_GetUid(Tcl_GetString(listObjv[i])); 7078 } 7079 ITEM_FOR_EACH(item, &items, NULL, &iter) { 7080 item->tagInfo = TagInfo_Remove(tree, item->tagInfo, tags, numTags); 7081 } 7082 STATIC_FREE(tags, Tk_Uid, numTags); 7083 break; 7084 } 7085 } 7086 7087 TreeItemList_Free(&items); 7088 return result; 7089} 7090 7091#ifdef SELECTION_VISIBLE 7092 7093/* 7094 *---------------------------------------------------------------------- 7095 * 7096 * Tree_DeselectHidden -- 7097 * 7098 * Removes any selected items which are no longer ReallyVisible() 7099 * from the selection. 7100 * 7101 * Results: 7102 * None. 7103 * 7104 * Side effects: 7105 * <Selection> event may be generated. 7106 * 7107 *---------------------------------------------------------------------- 7108 */ 7109 7110/* 7111 * FIXME: optimize all calls to this routine. 7112 * Optionally call Tree_DInfoChanged(tree, DINFO_REDO_SELECTION) instead. 7113 */ 7114void 7115Tree_DeselectHidden( 7116 TreeCtrl *tree /* Widget info. */ 7117 ) 7118{ 7119 TreeItemList items; 7120 Tcl_HashEntry *hPtr; 7121 Tcl_HashSearch search; 7122 TreeItem item; 7123 int i; 7124 7125 if (tree->selectCount < 1) 7126 return; 7127 7128 /* This call is slow for large lists. */ 7129 Tree_UpdateItemIndex(tree); 7130 7131 TreeItemList_Init(tree, &items, tree->selectCount); 7132 7133 hPtr = Tcl_FirstHashEntry(&tree->selection, &search); 7134 while (hPtr != NULL) { 7135 item = (TreeItem) Tcl_GetHashKey(&tree->selection, hPtr); 7136 if (!TreeItem_ReallyVisible(tree, item)) 7137 TreeItemList_Append(&items, item); 7138 hPtr = Tcl_NextHashEntry(&search); 7139 } 7140 for (i = 0; i < TreeItemList_Count(&items); i++) 7141 Tree_RemoveFromSelection(tree, TreeItemList_Nth(&items, i)); 7142 if (TreeItemList_Count(&items)) { 7143 TreeNotify_Selection(tree, NULL, &items); 7144 } 7145 TreeItemList_Free(&items); 7146} 7147 7148#endif /* SELECTION_VISIBLE */ 7149 7150/* 7151 *---------------------------------------------------------------------- 7152 * 7153 * TreeItemCmd -- 7154 * 7155 * This procedure is invoked to process the [item] widget 7156 * command. See the user documentation for details on what 7157 * it does. 7158 * 7159 * Results: 7160 * A standard Tcl result. 7161 * 7162 * Side effects: 7163 * See the user documentation. 7164 * 7165 *---------------------------------------------------------------------- 7166 */ 7167 7168int 7169TreeItemCmd( 7170 ClientData clientData, /* Widget info. */ 7171 Tcl_Interp *interp, /* Current interpreter. */ 7172 int objc, /* Number of arguments. */ 7173 Tcl_Obj *CONST objv[] /* Argument values. */ 7174 ) 7175{ 7176 TreeCtrl *tree = clientData; 7177 enum { 7178 COMMAND_ANCESTORS, 7179 COMMAND_BBOX, 7180 COMMAND_CGET, 7181 COMMAND_CHILDREN, 7182 COMMAND_COLLAPSE, 7183 COMMAND_COMPARE, 7184#ifdef DEPRECATED 7185 COMMAND_COMPLEX, 7186#endif 7187 COMMAND_CONFIGURE, 7188 COMMAND_COUNT, 7189 COMMAND_CREATE, 7190 COMMAND_DELETE, 7191 COMMAND_DESCENDANTS, 7192 COMMAND_DUMP, 7193 COMMAND_ELEMENT, 7194 COMMAND_ENABLED, 7195 COMMAND_EXPAND, 7196 COMMAND_FIRSTCHILD, 7197 COMMAND_ID, 7198 COMMAND_IMAGE, 7199 COMMAND_ISANCESTOR, 7200 COMMAND_ISOPEN, 7201 COMMAND_LASTCHILD, 7202 COMMAND_NEXTSIBLING, 7203 COMMAND_NUMCHILDREN, 7204 COMMAND_ORDER, 7205 COMMAND_PARENT, 7206 COMMAND_PREVSIBLING, 7207 COMMAND_RANGE, 7208 COMMAND_REMOVE, 7209 COMMAND_RNC, 7210 COMMAND_SORT, 7211 COMMAND_SPAN, 7212 COMMAND_STATE, 7213 COMMAND_STYLE, 7214 COMMAND_TAG, 7215 COMMAND_TEXT, 7216 COMMAND_TOGGLE 7217 }; 7218 7219/* AF_xxx must not conflict with IFO_xxx. */ 7220#define AF_NOT_ANCESTOR 0x00010000 /* item can't be ancestor of other item */ 7221#define AF_NOT_EQUAL 0x00020000 /* second item can't be same as first */ 7222#define AF_SAMEROOT 0x00040000 /* both items must be descendants of a 7223 * common ancestor */ 7224#define AF_NOT_ITEM 0x00080000 /* arg is not an Item */ 7225#define AF_NOT_DELETED 0x00100000 /* item can't be deleted */ 7226 7227 struct { 7228 char *cmdName; 7229 int minArgs; 7230 int maxArgs; 7231 int flags; /* AF_xxx | IFO_xxx for 1st arg. */ 7232 int flags2; /* AF_xxx | IFO_xxx for 2nd arg. */ 7233 int flags3; /* AF_xxx | IFO_xxx for 3rd arg. */ 7234 char *argString; 7235 Tcl_ObjCmdProc *proc; 7236 } argInfo[] = { 7237 { "ancestors", 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0, "item", NULL }, 7238 { "bbox", 1, 3, IFO_NOT_MANY | IFO_NOT_NULL, AF_NOT_ITEM, AF_NOT_ITEM, 7239 "item ?column? ?element?", NULL }, 7240 { "cget", 2, 2, IFO_NOT_MANY | IFO_NOT_NULL, AF_NOT_ITEM, 0, 7241 "item option", NULL }, 7242 { "children", 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0, 7243 "item", NULL }, 7244 { "collapse", 1, 2, IFO_NOT_NULL, AF_NOT_ITEM, 0, 7245 "item ?-recurse?", NULL}, 7246 { "compare", 3, 3, IFO_NOT_MANY | IFO_NOT_NULL, AF_NOT_ITEM, 7247 IFO_NOT_MANY | IFO_NOT_NULL | AF_SAMEROOT, "item1 op item2", 7248 NULL }, 7249#ifdef DEPRECATED 7250 { "complex", 2, 100000, IFO_NOT_MANY | IFO_NOT_NULL, AF_NOT_ITEM, 7251 AF_NOT_ITEM, "item list ...", NULL }, 7252#endif 7253 { "configure", 1, 100000, IFO_NOT_NULL, AF_NOT_ITEM, AF_NOT_ITEM, 7254 "item ?option? ?value? ?option value ...?", NULL }, 7255 { "count", 0, 1, 0, 0, 0, "?itemDesc?" , NULL}, 7256 { "create", 0, 0, 0, 0, 0, NULL, ItemCreateCmd }, 7257 { "delete", 1, 2, IFO_NOT_NULL, IFO_NOT_NULL | AF_SAMEROOT, 0, 7258 "first ?last?", NULL }, 7259 { "descendants", 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0, "item", 7260 NULL }, 7261 { "dump", 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0, "item", NULL }, 7262 { "element", 0, 0, 0, 0, 0, NULL, ItemElementCmd }, 7263 { "enabled", 1, 2, IFO_NOT_NULL, AF_NOT_ITEM, 0, "item ?boolean?", 7264 NULL }, 7265 { "expand", 1, 2, IFO_NOT_NULL, AF_NOT_ITEM, 0, "item ?-recurse?", 7266 NULL}, 7267 { "firstchild", 1, 2, IFO_NOT_MANY | IFO_NOT_NULL | AF_NOT_DELETED, 7268 IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT | AF_NOT_ANCESTOR | 7269 AF_NOT_EQUAL | AF_NOT_DELETED, 0, "item ?newFirstChild?", 7270 NULL }, 7271 { "id", 1, 1, 0, 0, 0, "item", NULL }, 7272 { "image", 1, 100000, IFO_NOT_NULL, AF_NOT_ITEM, AF_NOT_ITEM, 7273 "item ?column? ?image? ?column image ...?", NULL }, 7274 { "isancestor", 2, 2, IFO_NOT_MANY | IFO_NOT_NULL, IFO_NOT_MANY | 7275 IFO_NOT_NULL, 0, "item item2", NULL }, 7276 { "isopen", 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0, "item", NULL }, 7277 { "lastchild", 1, 2, IFO_NOT_MANY | IFO_NOT_NULL | AF_NOT_DELETED, 7278 IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT | AF_NOT_ANCESTOR | 7279 AF_NOT_EQUAL | AF_NOT_DELETED, 0, "item ?newLastChild?", NULL }, 7280 { "nextsibling", 1, 2, IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT | 7281 IFO_NOT_ORPHAN, IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT | 7282 AF_NOT_ANCESTOR | AF_NOT_EQUAL, 0, "item ?newNextSibling?", 7283 NULL }, 7284 { "numchildren", 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0, "item", 7285 NULL }, 7286 { "order", 1, 2, IFO_NOT_MANY | IFO_NOT_NULL, AF_NOT_ITEM, 0, 7287 "item ?-visible?", NULL }, 7288 { "parent", 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0, "item", NULL }, 7289 { "prevsibling", 1, 2, IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT | 7290 IFO_NOT_ORPHAN, IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT | 7291 AF_NOT_ANCESTOR | AF_NOT_EQUAL, 0, "item ?newPrevSibling?", 7292 NULL }, 7293 { "range", 2, 2, IFO_NOT_MANY | IFO_NOT_NULL, IFO_NOT_MANY | 7294 IFO_NOT_NULL | AF_SAMEROOT, 0, "first last", NULL }, 7295 { "remove", 1, 1, IFO_NOT_NULL | IFO_NOT_ROOT, 0, 0, "item", NULL }, 7296 { "rnc", 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0, "item", NULL }, 7297 { "sort", 0, 0, 0, 0, 0, NULL, ItemSortCmd }, 7298 { "span", 1, 100000, IFO_NOT_NULL, AF_NOT_ITEM, AF_NOT_ITEM, 7299 "item ?column? ?span? ?column span ...?", NULL }, 7300 { "state", 0, 0, 0, 0, 0, NULL, ItemStateCmd }, 7301 { "style", 0, 0, 0, 0, 0, NULL, ItemStyleCmd }, 7302 { "tag", 0, 0, 0, 0, 0, NULL, ItemTagCmd }, 7303 { "text", 1, 100000, IFO_NOT_NULL, AF_NOT_ITEM, AF_NOT_ITEM, 7304 "item ?column? ?text? ?column text ...?", NULL }, 7305 { "toggle", 1, 2, IFO_NOT_NULL, AF_NOT_ITEM, 0, "item ?-recurse?", 7306 NULL}, 7307 { NULL } 7308 }; 7309 int index; 7310 int numArgs = objc - 3; 7311 TreeItemList itemList, item2List; 7312 TreeItem item = NULL, item2 = NULL, child; 7313 ItemForEach iter; 7314 int result = TCL_OK; 7315 7316 if (objc < 3) { 7317 Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg ...?"); 7318 return TCL_ERROR; 7319 } 7320 7321 if (Tcl_GetIndexFromObjStruct(interp, objv[2], argInfo, sizeof(argInfo[0]), 7322 "command", 0, &index) != TCL_OK) { 7323 return TCL_ERROR; 7324 } 7325 7326 if (argInfo[index].proc != NULL) 7327 return argInfo[index].proc(clientData, interp, objc, objv); 7328 7329 if ((numArgs < argInfo[index].minArgs) || 7330 (numArgs > argInfo[index].maxArgs)) { 7331 Tcl_WrongNumArgs(interp, 3, objv, argInfo[index].argString); 7332 return TCL_ERROR; 7333 } 7334 7335 TreeItemList_Init(tree, &itemList, 0); 7336 TreeItemList_Init(tree, &item2List, 0); 7337 7338 if ((numArgs >= 1) && !(argInfo[index].flags & AF_NOT_ITEM)) { 7339 if (TreeItemList_FromObj(tree, objv[3], &itemList, 7340 argInfo[index].flags & 0xFFFF) != TCL_OK) { 7341 goto errorExit; 7342 } 7343 item = TreeItemList_Nth(&itemList, 0); /* May be NULL. */ 7344 if ((argInfo[index].flags & AF_NOT_DELETED) && IS_DELETED(item)) { 7345 FormatResult(interp, "item %s%d is being deleted", 7346 tree->itemPrefix, item->id); 7347 goto errorExit; 7348 } 7349 } 7350 if (((numArgs >= 2) && !(argInfo[index].flags2 & AF_NOT_ITEM)) || 7351 ((numArgs >= 3) && !(argInfo[index].flags3 & AF_NOT_ITEM))) { 7352 int flags, obji; 7353 if (argInfo[index].flags2 & AF_NOT_ITEM) { 7354 obji = 5; 7355 flags = argInfo[index].flags3; 7356 } else { 7357 obji = 4; 7358 flags = argInfo[index].flags2; 7359 } 7360 if (TreeItemList_FromObj(tree, objv[obji], &item2List, 7361 flags & 0xFFFF) != TCL_OK) { 7362 goto errorExit; 7363 } 7364 ITEM_FOR_EACH(item2, &item2List, NULL, &iter) { 7365 if ((flags & AF_NOT_DELETED) && IS_DELETED(item2)) { 7366 FormatResult(interp, "item %s%d is being deleted", 7367 tree->itemPrefix, item2->id); 7368 goto errorExit; 7369 } 7370 if ((flags & AF_NOT_EQUAL) && (item == item2)) { 7371 FormatResult(interp, "item %s%d same as second item", tree->itemPrefix, 7372 item->id); 7373 goto errorExit; 7374 } 7375 if ((argInfo[index].flags & AF_NOT_ANCESTOR) && 7376 TreeItem_IsAncestor(tree, item, item2)) { 7377 FormatResult(interp, "item %s%d is ancestor of item %s%d", 7378 tree->itemPrefix, item->id, tree->itemPrefix, item2->id); 7379 goto errorExit; 7380 } 7381 if ((flags & AF_NOT_ANCESTOR) && 7382 TreeItem_IsAncestor(tree, item2, item)) { 7383 FormatResult(interp, "item %s%d is ancestor of item %s%d", 7384 tree->itemPrefix, item2->id, tree->itemPrefix, item->id); 7385 goto errorExit; 7386 } 7387 if ((flags & AF_SAMEROOT) && 7388 TreeItem_RootAncestor(tree, item) != 7389 TreeItem_RootAncestor(tree, item2)) { 7390 FormatResult(interp, 7391 "item %s%d and item %s%d don't share a common ancestor", 7392 tree->itemPrefix, item->id, tree->itemPrefix, item2->id); 7393 goto errorExit; 7394 } 7395 } 7396 item2 = TreeItemList_Nth(&item2List, 0); /* May be NULL. */ 7397 } 7398 7399 switch (index) { 7400 case COMMAND_ANCESTORS: { 7401 Tcl_Obj *listObj; 7402 TreeItem parent = item->parent; 7403 7404 if (parent == NULL) 7405 break; /* empty list */ 7406 listObj = Tcl_NewListObj(0, NULL); 7407 while (parent != NULL) { 7408 Tcl_ListObjAppendElement(interp, listObj, 7409 TreeItem_ToObj(tree, parent)); 7410 parent = parent->parent; 7411 } 7412 Tcl_SetObjResult(interp, listObj); 7413 break; 7414 } 7415 /* T item bbox I ?C? ?E? */ 7416 case COMMAND_BBOX: { 7417 int x, y, w, h; 7418 int count; 7419 TreeColumn treeColumn; 7420 TreeRectangle rect; 7421 7422 if (objc == 4) { 7423 if (Tree_ItemBbox(tree, item, COLUMN_LOCK_NONE, &x, &y, &w, &h) < 0) 7424 break; 7425 } else { 7426 if (TreeColumn_FromObj(tree, objv[4], &treeColumn, 7427 CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) 7428 goto errorExit; 7429 7430 /* Bounds of a column. */ 7431 if (objc == 5) { 7432 objc = 0; 7433 objv = NULL; 7434 7435 /* Single element in a column. */ 7436 } else { 7437 objc -= 5; 7438 objv += 5; 7439 } 7440 7441 count = TreeItem_GetRects(tree, item, treeColumn, 7442 objc, objv, &rect); 7443 if (count == 0) 7444 break; 7445 if (count == -1) 7446 goto errorExit; 7447 x = rect.x; 7448 y = rect.y; 7449 w = rect.width; 7450 h = rect.height; 7451 } 7452 FormatResult(interp, "%d %d %d %d", 7453 x - tree->xOrigin, 7454 y - tree->yOrigin, 7455 x - tree->xOrigin + w, 7456 y - tree->yOrigin + h); 7457 break; 7458 } 7459 case COMMAND_CGET: { 7460 Tcl_Obj *resultObjPtr; 7461 7462 resultObjPtr = Tk_GetOptionValue(interp, (char *) item, 7463 tree->itemOptionTable, objv[4], tree->tkwin); 7464 if (resultObjPtr == NULL) 7465 goto errorExit; 7466 Tcl_SetObjResult(interp, resultObjPtr); 7467 break; 7468 } 7469 case COMMAND_CHILDREN: { 7470 if (item->numChildren != 0) { 7471 Tcl_Obj *listObj; 7472 7473 listObj = Tcl_NewListObj(0, NULL); 7474 child = item->firstChild; 7475 while (child != NULL) { 7476 Tcl_ListObjAppendElement(interp, listObj, 7477 TreeItem_ToObj(tree, child)); 7478 child = child->nextSibling; 7479 } 7480 Tcl_SetObjResult(interp, listObj); 7481 } 7482 break; 7483 } 7484 case COMMAND_COLLAPSE: 7485 case COMMAND_EXPAND: 7486 case COMMAND_TOGGLE: { 7487 int recurse = 0; 7488 int mode = 0; /* lint */ 7489 int i, count; 7490 TreeItemList items; 7491 7492 if (numArgs == 2) { 7493 int len; 7494 char *s = Tcl_GetStringFromObj(objv[4], &len); 7495 if (strncmp(s, "-recurse", len)) { 7496 FormatResult(interp, "bad option \"%s\": must be -recurse", 7497 s); 7498 goto errorExit; 7499 } 7500 recurse = 1; 7501 } 7502 switch (index) { 7503 case COMMAND_COLLAPSE: 7504 mode = 0; 7505 break; 7506 case COMMAND_EXPAND: 7507 mode = 1; 7508 break; 7509 case COMMAND_TOGGLE: 7510 mode = -1; 7511 break; 7512 } 7513 TreeItemList_Init(tree, &items, 0); 7514 ITEM_FOR_EACH(item, &itemList, NULL, &iter) { 7515 TreeItemList_Append(&items, item); 7516 if (!iter.all && recurse) { 7517 TreeItem_ListDescendants(tree, item, &items); 7518 } 7519 } 7520 count = TreeItemList_Count(&items); 7521 for (i = 0; i < count; i++) { 7522 item = TreeItemList_Nth(&items, i); 7523 TreeItem_OpenClose(tree, item, mode); 7524 } 7525 TreeItemList_Free(&items); 7526#ifdef SELECTION_VISIBLE 7527 Tree_DeselectHidden(tree); 7528#endif 7529 break; 7530 } 7531 /* T item compare I op I */ 7532 case COMMAND_COMPARE: { 7533 static CONST char *opName[] = { "<", "<=", "==", ">=", ">", "!=", NULL }; 7534 int op, compare = 0, index1, index2; 7535 7536 if (Tcl_GetIndexFromObj(interp, objv[4], opName, "comparison operator", 0, 7537 &op) != TCL_OK) 7538 goto errorExit; 7539 TreeItem_ToIndex(tree, item, &index1, NULL); 7540 TreeItem_ToIndex(tree, item2, &index2, NULL); 7541 switch (op) { 7542 case 0: compare = index1 < index2; break; 7543 case 1: compare = index1 <= index2; break; 7544 case 2: compare = index1 == index2; break; 7545 case 3: compare = index1 >= index2; break; 7546 case 4: compare = index1 > index2; break; 7547 case 5: compare = index1 != index2; break; 7548 } 7549 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(compare)); 7550 break; 7551 } 7552 7553#ifdef DEPRECATED 7554 case COMMAND_COMPLEX: { 7555 int i, j, columnIndex; 7556 int objc1, objc2; 7557 Tcl_Obj **objv1, **objv2; 7558 TreeColumn treeColumn = tree->columns; 7559 Column *column; 7560 int eMask, cMask, iMask = 0; 7561 7562 if (objc <= 4) 7563 break; 7564 columnIndex = 0; 7565 for (i = 4; i < objc; i++, columnIndex++, 7566 treeColumn = TreeColumn_Next(treeColumn)) { 7567 if (treeColumn == NULL) { 7568 FormatResult(interp, "column #%d doesn't exist", 7569 columnIndex); 7570 result = TCL_ERROR; 7571 goto doneComplex; 7572 } 7573 column = Item_FindColumn(tree, item, columnIndex); 7574 if (column == NULL) { 7575 FormatResult(interp, "item %s%d doesn't have column %s%d", 7576 tree->itemPrefix, item->id, 7577 tree->columnPrefix, TreeColumn_GetID(treeColumn)); 7578 result = TCL_ERROR; 7579 goto doneComplex; 7580 } 7581 /* List of element-configs per column */ 7582 if (Tcl_ListObjGetElements(interp, objv[i], 7583 &objc1, &objv1) != TCL_OK) { 7584 result = TCL_ERROR; 7585 goto doneComplex; 7586 } 7587 if (objc1 == 0) 7588 continue; 7589 if (column->style == NULL) { 7590 FormatResult(interp, "item %s%d column %s%d has no style", 7591 tree->itemPrefix, item->id, 7592 tree->columnPrefix, TreeColumn_GetID(treeColumn)); 7593 result = TCL_ERROR; 7594 goto doneComplex; 7595 } 7596 cMask = 0; 7597 for (j = 0; j < objc1; j++) { 7598 /* elem option value... */ 7599 if (Tcl_ListObjGetElements(interp, objv1[j], 7600 &objc2, &objv2) != TCL_OK) { 7601 result = TCL_ERROR; 7602 goto doneComplex; 7603 } 7604 if (objc2 < 3) { 7605 FormatResult(interp, 7606 "wrong # args: should be \"element option value ...\""); 7607 result = TCL_ERROR; 7608 goto doneComplex; 7609 } 7610 if (TreeStyle_ElementConfigure(tree, item, 7611 (TreeItemColumn) column, column->style, 7612 objv2[0], objc2 - 1, objv2 + 1, &eMask) != TCL_OK) { 7613 result = TCL_ERROR; 7614 goto doneComplex; 7615 } 7616 cMask |= eMask; 7617 iMask |= eMask; 7618 } 7619 if (cMask & CS_LAYOUT) 7620 TreeItemColumn_InvalidateSize(tree, (TreeItemColumn) column); 7621 } 7622 doneComplex: 7623 if (iMask & CS_DISPLAY) 7624 Tree_InvalidateItemDInfo(tree, NULL, item, NULL); 7625 if (iMask & CS_LAYOUT) { 7626 Tree_InvalidateColumnWidth(tree, NULL); 7627 TreeItem_InvalidateHeight(tree, item); 7628 Tree_FreeItemDInfo(tree, item, NULL); 7629 Tree_DInfoChanged(tree, DINFO_REDO_RANGES); 7630 } 7631 break; 7632 } 7633#endif /* DEPRECATED*/ 7634 7635 /* T item configure I ?option? ?value? ?option value ...? */ 7636 case COMMAND_CONFIGURE: { 7637 if (objc <= 5) { 7638 Tcl_Obj *resultObjPtr; 7639 7640 if (IS_ALL(item) || (TreeItemList_Count(&itemList) > 1)) { 7641 FormatResult(interp, "can't specify > 1 item for this command"); 7642 goto errorExit; 7643 } 7644 resultObjPtr = Tk_GetOptionInfo(interp, (char *) item, 7645 tree->itemOptionTable, 7646 (objc == 4) ? (Tcl_Obj *) NULL : objv[4], 7647 tree->tkwin); 7648 if (resultObjPtr == NULL) 7649 goto errorExit; 7650 Tcl_SetObjResult(interp, resultObjPtr); 7651 break; 7652 } 7653 ITEM_FOR_EACH(item, &itemList, NULL, &iter) { 7654 result = Item_Configure(tree, item, objc - 4, objv + 4); 7655 if (result != TCL_OK) 7656 break; 7657 } 7658 break; 7659 } 7660 case COMMAND_COUNT: { 7661 int count = tree->itemCount; 7662 7663 if (objc == 4) { 7664 count = 0; 7665 ITEM_FOR_EACH(item, &itemList, &item2List, &iter) { 7666 count++; 7667 } 7668 } 7669 Tcl_SetObjResult(interp, Tcl_NewIntObj(count)); 7670 break; 7671 } 7672 case COMMAND_DELETE: { 7673 TreeItemList deleted, selected; 7674 int i, count; 7675 7676 /* The root is never deleted */ 7677 if (tree->itemCount == 1) 7678 break; 7679 7680 if (objc == 5) { 7681 if (item == NULL || item2 == NULL) { 7682 FormatResult(interp, "invalid range \"%s\" to \"%s\"", 7683 Tcl_GetString(objv[3]), Tcl_GetString(objv[4])); 7684 goto errorExit; 7685 } 7686 } 7687 7688 /* 7689 * ITEM_FLAG_DELETED prevents us from adding the same item 7690 * twice to the 'deleted' list. It also prevents nested 7691 * calls to this command (through binding scripts) deleting 7692 * the same item twice. 7693 */ 7694 7695 TreeItemList_Init(tree, &deleted, tree->itemCount - 1); 7696 TreeItemList_Init(tree, &selected, tree->selectCount); 7697 7698 ITEM_FOR_EACH(item, &itemList, &item2List, &iter) { 7699 if (IS_ROOT(item)) 7700 continue; 7701 if (IS_DELETED(item)) 7702 continue; 7703 item->flags |= ITEM_FLAG_DELETED; 7704 TreeItemList_Append(&deleted, item); 7705 if (TreeItem_GetSelected(tree, item)) 7706 TreeItemList_Append(&selected, item); 7707 if (iter.all) 7708 continue; 7709 /* Check every descendant. */ 7710 if (item->firstChild == NULL) 7711 continue; 7712 item2 = item; 7713 while (item2->lastChild != NULL) 7714 item2 = item2->lastChild; 7715 item = item->firstChild; 7716 while (1) { 7717 if (IS_DELETED(item)) { 7718 /* Skip all descendants (they are already flagged). */ 7719 while (item->lastChild != NULL) 7720 item = item->lastChild; 7721 } else { 7722 item->flags |= ITEM_FLAG_DELETED; 7723 TreeItemList_Append(&deleted, item); 7724 if (TreeItem_GetSelected(tree, item)) 7725 TreeItemList_Append(&selected, item); 7726 } 7727 if (item == item2) 7728 break; 7729 item = TreeItem_Next(tree, item); 7730 } 7731 } 7732 7733 count = TreeItemList_Count(&selected); 7734 if (count) { 7735 for (i = 0; i < count; i++) { 7736 item = TreeItemList_Nth(&selected, i); 7737 Tree_RemoveFromSelection(tree, item); 7738 } 7739 /* Generate <Selection> event for selected items being deleted. */ 7740 TreeNotify_Selection(tree, NULL, &selected); 7741 } 7742 7743 count = TreeItemList_Count(&deleted); 7744 if (count) { 7745 /* Generate <ItemDelete> event for items being deleted. */ 7746 TreeNotify_ItemDeleted(tree, &deleted); 7747 7748 /* Remove every item from its parent. Needed because items 7749 * are deleted recursively. */ 7750 for (i = 0; i < count; i++) { 7751 item = TreeItemList_Nth(&deleted, i); 7752 TreeItem_RemoveFromParent(tree, item); 7753 } 7754 7755 /* Delete the items. The item record will be freed when no 7756 * longer in use; however, the item cannot be referred to 7757 * by commands from this point on. */ 7758 for (i = 0; i < count; i++) { 7759 item = TreeItemList_Nth(&deleted, i); 7760 TreeItem_Delete(tree, item); 7761 } 7762 } 7763 7764 TreeItemList_Free(&selected); 7765 TreeItemList_Free(&deleted); 7766 break; 7767 } 7768 case COMMAND_DESCENDANTS: { 7769 Tcl_Obj *listObj; 7770 7771 if (item->firstChild == NULL) 7772 break; 7773 item2 = item; 7774 while (item2->lastChild != NULL) 7775 item2 = item2->lastChild; 7776 item = item->firstChild; 7777 listObj = Tcl_NewListObj(0, NULL); 7778 while (1) { 7779 Tcl_ListObjAppendElement(interp, listObj, 7780 TreeItem_ToObj(tree, item)); 7781 if (item == item2) 7782 break; 7783 item = TreeItem_Next(tree, item); 7784 } 7785 Tcl_SetObjResult(interp, listObj); 7786 break; 7787 } 7788 case COMMAND_DUMP: { 7789 Tree_UpdateItemIndex(tree); 7790 FormatResult(interp, "index %d indexVis %d", 7791 item->index, item->indexVis); 7792 break; 7793 } 7794 /* T item enabled I ?boolean? */ 7795 case COMMAND_ENABLED: { 7796 int enabled; 7797 TreeItemList newD; 7798 int stateOff, stateOn; 7799 7800 if (objc == 4) { 7801 if (IS_ALL(item) || (TreeItemList_Count(&itemList) > 1)) { 7802 FormatResult(interp, "can't specify > 1 item for this command"); 7803 goto errorExit; 7804 } 7805 Tcl_SetObjResult(interp, 7806 Tcl_NewBooleanObj(item->state & STATE_ENABLED)); 7807 break; 7808 } 7809 if (Tcl_GetBooleanFromObj(interp, objv[4], &enabled) != TCL_OK) 7810 goto errorExit; 7811 stateOff = enabled ? 0 : STATE_ENABLED; 7812 stateOn = enabled ? STATE_ENABLED : 0; 7813 TreeItemList_Init(tree, &newD, tree->selectCount); 7814 ITEM_FOR_EACH(item, &itemList, NULL, &iter) { 7815 if (enabled != TreeItem_GetEnabled(tree, item)) { 7816 TreeItem_ChangeState(tree, item, stateOff, stateOn); 7817 /* Disabled items cannot be selected. */ 7818 if (!enabled && TreeItem_GetSelected(tree, item)) { 7819 Tree_RemoveFromSelection(tree, item); 7820 TreeItemList_Append(&newD, item); 7821 } 7822 } 7823 } 7824 if (TreeItemList_Count(&newD)) 7825 TreeNotify_Selection(tree, NULL, &newD); 7826 TreeItemList_Free(&newD); 7827 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(enabled)); 7828 break; 7829 } 7830 case COMMAND_FIRSTCHILD: { 7831 if (item2 != NULL && item2 != item->firstChild) { 7832 TreeItem_RemoveFromParent(tree, item2); 7833 item2->nextSibling = item->firstChild; 7834 if (item->firstChild != NULL) 7835 item->firstChild->prevSibling = item2; 7836 else 7837 item->lastChild = item2; 7838 item->firstChild = item2; 7839 item2->parent = item; 7840 item->numChildren++; 7841 TreeItem_AddToParent(tree, item2); 7842#ifdef SELECTION_VISIBLE 7843 Tree_DeselectHidden(tree); 7844#endif 7845 } 7846 if (item->firstChild != NULL) 7847 Tcl_SetObjResult(interp, TreeItem_ToObj(tree, item->firstChild)); 7848 break; 7849 } 7850 /* T item id I */ 7851 case COMMAND_ID: { 7852 Tcl_Obj *listObj; 7853 7854 listObj = Tcl_NewListObj(0, NULL); 7855 ITEM_FOR_EACH(item, &itemList, NULL, &iter) { 7856 Tcl_ListObjAppendElement(interp, listObj, 7857 TreeItem_ToObj(tree, item)); 7858 } 7859 Tcl_SetObjResult(interp, listObj); 7860 break; 7861 } 7862 case COMMAND_ISANCESTOR: { 7863 Tcl_SetObjResult(interp, Tcl_NewBooleanObj( 7864 TreeItem_IsAncestor(tree, item, item2))); 7865 break; 7866 } 7867 case COMMAND_ISOPEN: { 7868 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(item->state & STATE_OPEN)); 7869 break; 7870 } 7871 case COMMAND_LASTCHILD: { 7872 /* Don't allow non-deleted items to become children of a 7873 * deleted item. */ 7874 if (item2 != NULL && item2 != item->lastChild) { 7875 TreeItem_RemoveFromParent(tree, item2); 7876 item2->prevSibling = item->lastChild; 7877 if (item->lastChild != NULL) 7878 item->lastChild->nextSibling = item2; 7879 else 7880 item->firstChild = item2; 7881 item->lastChild = item2; 7882 item2->parent = item; 7883 item->numChildren++; 7884 TreeItem_AddToParent(tree, item2); 7885#ifdef SELECTION_VISIBLE 7886 Tree_DeselectHidden(tree); 7887#endif 7888 } 7889 if (item->lastChild != NULL) 7890 Tcl_SetObjResult(interp, TreeItem_ToObj(tree, item->lastChild)); 7891 break; 7892 } 7893 case COMMAND_NEXTSIBLING: { 7894 if (item2 != NULL && item2 != item->nextSibling) { 7895 TreeItem_RemoveFromParent(tree, item2); 7896 item2->prevSibling = item; 7897 if (item->nextSibling != NULL) { 7898 item->nextSibling->prevSibling = item2; 7899 item2->nextSibling = item->nextSibling; 7900 } else 7901 item->parent->lastChild = item2; 7902 item->nextSibling = item2; 7903 item2->parent = item->parent; 7904 item->parent->numChildren++; 7905 TreeItem_AddToParent(tree, item2); 7906#ifdef SELECTION_VISIBLE 7907 Tree_DeselectHidden(tree); 7908#endif 7909 } 7910 if (item->nextSibling != NULL) 7911 Tcl_SetObjResult(interp, TreeItem_ToObj(tree, item->nextSibling)); 7912 break; 7913 } 7914 case COMMAND_NUMCHILDREN: { 7915 Tcl_SetObjResult(interp, Tcl_NewIntObj(item->numChildren)); 7916 break; 7917 } 7918 /* T item order I ?-visible? */ 7919 case COMMAND_ORDER: { 7920 int visible = FALSE; 7921 if (objc == 5) { 7922 int len; 7923 char *s = Tcl_GetStringFromObj(objv[4], &len); 7924 if ((s[0] == '-') && (strncmp(s, "-visible", len) == 0)) 7925 visible = TRUE; 7926 else { 7927 FormatResult(interp, "bad switch \"%s\": must be -visible", 7928 s); 7929 goto errorExit; 7930 } 7931 } 7932 Tree_UpdateItemIndex(tree); 7933 Tcl_SetObjResult(interp, 7934 Tcl_NewIntObj(visible ? item->indexVis : item->index)); 7935 break; 7936 } 7937 /* T item range I I */ 7938 case COMMAND_RANGE: { 7939 TreeItem itemFirst = item; 7940 TreeItem itemLast = item2; 7941 Tcl_Obj *listObj; 7942 7943 if (itemFirst == itemLast) { 7944 Tcl_SetObjResult(interp, TreeItem_ToObj(tree, itemFirst)); 7945 break; 7946 } 7947 (void) TreeItem_FirstAndLast(tree, &itemFirst, &itemLast); 7948 listObj = Tcl_NewListObj(0, NULL); 7949 while (itemFirst != NULL) { 7950 Tcl_ListObjAppendElement(interp, listObj, 7951 TreeItem_ToObj(tree, itemFirst)); 7952 if (itemFirst == itemLast) 7953 break; 7954 itemFirst = TreeItem_Next(tree, itemFirst); 7955 } 7956 Tcl_SetObjResult(interp, listObj); 7957 break; 7958 } 7959 case COMMAND_PARENT: { 7960 if (item->parent != NULL) 7961 Tcl_SetObjResult(interp, TreeItem_ToObj(tree, item->parent)); 7962 break; 7963 } 7964 case COMMAND_PREVSIBLING: { 7965 if (item2 != NULL && item2 != item->prevSibling) { 7966 TreeItem_RemoveFromParent(tree, item2); 7967 item2->nextSibling = item; 7968 if (item->prevSibling != NULL) { 7969 item->prevSibling->nextSibling = item2; 7970 item2->prevSibling = item->prevSibling; 7971 } else 7972 item->parent->firstChild = item2; 7973 item->prevSibling = item2; 7974 item2->parent = item->parent; 7975 item->parent->numChildren++; 7976 TreeItem_AddToParent(tree, item2); 7977#ifdef SELECTION_VISIBLE 7978 Tree_DeselectHidden(tree); 7979#endif 7980 } 7981 if (item->prevSibling != NULL) 7982 Tcl_SetObjResult(interp, TreeItem_ToObj(tree, item->prevSibling)); 7983 break; 7984 } 7985 case COMMAND_REMOVE: { 7986 int removed = FALSE; 7987 7988 ITEM_FOR_EACH(item, &itemList, NULL, &iter) { 7989 if (item->parent != NULL) { 7990 TreeItem_RemoveFromParent(tree, item); 7991 Tree_FreeItemDInfo(tree, item, NULL); 7992 removed = TRUE; 7993 } 7994 } 7995 if (!removed) 7996 break; 7997 if (tree->debug.enable && tree->debug.data) 7998 Tree_Debug(tree); 7999 Tree_InvalidateColumnWidth(tree, NULL); 8000#ifdef SELECTION_VISIBLE 8001 Tree_DeselectHidden(tree); 8002#endif 8003 break; 8004 } 8005 case COMMAND_RNC: { 8006 int row,col; 8007 8008 if (Tree_ItemToRNC(tree, item, &row, &col) == TCL_OK) 8009 FormatResult(interp, "%d %d", row, col); 8010 break; 8011 } 8012 8013 /* T item span I ?C? ?span? ?C span ...? */ 8014 case COMMAND_SPAN: { 8015 TreeColumn treeColumn = tree->columns; 8016 Column *column; 8017 Tcl_Obj *listObj; 8018 struct columnSpan { 8019 TreeColumnList columns; 8020 int span; 8021 } staticCS[STATIC_SIZE], *cs = staticCS; 8022 int i, count = 0, span, changed = FALSE; 8023 ColumnForEach citer; 8024 8025 if ((objc < 6) && (IS_ALL(item) || 8026 (TreeItemList_Count(&itemList) > 1))) { 8027 FormatResult(interp, "can't specify > 1 item for this command"); 8028 goto errorExit; 8029 } 8030 if (objc == 4) { 8031 listObj = Tcl_NewListObj(0, NULL); 8032 column = item->columns; 8033 while (treeColumn != NULL) { 8034 Tcl_ListObjAppendElement(interp, listObj, 8035 Tcl_NewIntObj(column ? column->span : 1)); 8036 treeColumn = TreeColumn_Next(treeColumn); 8037 if (column != NULL) 8038 column = column->next; 8039 } 8040 Tcl_SetObjResult(interp, listObj); 8041 break; 8042 } 8043 if (objc == 5) { 8044 if (Item_FindColumnFromObj(tree, item, objv[4], &column, NULL) != TCL_OK) { 8045 goto errorExit; 8046 } 8047 Tcl_SetObjResult(interp, Tcl_NewIntObj(column ? column->span : 1)); 8048 break; 8049 } 8050 if ((objc - 4) & 1) { 8051 FormatResult(interp, "missing argument after column \"%s\"", 8052 Tcl_GetString(objv[objc - 1])); 8053 goto errorExit; 8054 } 8055 /* Gather column/span pairs. */ 8056 STATIC_ALLOC(cs, struct columnSpan, objc / 2); 8057 for (i = 4; i < objc; i += 2) { 8058 if (TreeColumnList_FromObj(tree, objv[i], &cs[count].columns, 8059 CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) { 8060 result = TCL_ERROR; 8061 goto doneSPAN; 8062 } 8063 if (Tcl_GetIntFromObj(interp, objv[i + 1], &span) != TCL_OK) { 8064 result = TCL_ERROR; 8065 goto doneSPAN; 8066 } 8067 if (span <= 0) { 8068 FormatResult(interp, "bad span \"%d\": must be > 0", span); 8069 result = TCL_ERROR; 8070 goto doneSPAN; 8071 } 8072 cs[count].span = span; 8073 count++; 8074 } 8075 ITEM_FOR_EACH(item, &itemList, NULL, &iter) { 8076 int changedI = FALSE; 8077 for (i = 0; i < count; i++) { 8078 COLUMN_FOR_EACH(treeColumn, &cs[i].columns, NULL, &citer) { 8079 column = Item_CreateColumn(tree, item, 8080 TreeColumn_Index(treeColumn), NULL); 8081 if (column->span != cs[i].span) { 8082 if (cs[i].span > 1) { 8083 item->flags &= ~ITEM_FLAG_SPANS_SIMPLE; 8084 } 8085 TreeItem_SpansInvalidate(tree, item); 8086 column->span = cs[i].span; 8087 TreeItemColumn_InvalidateSize(tree, 8088 (TreeItemColumn) column); 8089 changedI = TRUE; 8090 Tree_InvalidateColumnWidth(tree, treeColumn); 8091 } 8092 } 8093 } 8094 if (changedI) { 8095 TreeItem_InvalidateHeight(tree, item); 8096 Tree_FreeItemDInfo(tree, item, NULL); 8097 changed = TRUE; 8098 } 8099 } 8100 if (changed) 8101 Tree_DInfoChanged(tree, DINFO_REDO_RANGES); 8102doneSPAN: 8103 for (i = 0; i < count; i++) { 8104 TreeColumnList_Free(&cs[i].columns); 8105 } 8106 STATIC_FREE(cs, struct columnSpan, objc / 2); 8107 break; 8108 } 8109 8110 /* T item image I ?C? ?image? ?C image ...? */ 8111 case COMMAND_IMAGE: 8112 /* T item text I ?C? ?text? ?C text ...? */ 8113 case COMMAND_TEXT: { 8114 TreeColumn treeColumn = tree->columns; 8115 Column *column; 8116 Tcl_Obj *objPtr; 8117 int isImage = (index == COMMAND_IMAGE); 8118 struct columnObj { 8119 TreeColumnList columns; 8120 Tcl_Obj *obj; 8121 } staticCO[STATIC_SIZE], *co = staticCO; 8122 int i, count = 0, changed = FALSE, columnIndex; 8123 ColumnForEach citer; 8124 8125 if ((objc < 6) && (IS_ALL(item) || 8126 (TreeItemList_Count(&itemList) > 1))) { 8127 FormatResult(interp, "can't specify > 1 item for this command"); 8128 goto errorExit; 8129 } 8130 if (objc == 4) { 8131 Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); 8132 column = item->columns; 8133 while (treeColumn != NULL) { 8134 if ((column != NULL) && (column->style != NULL)) 8135 objPtr = isImage ? 8136 TreeStyle_GetImage(tree, column->style) : 8137 TreeStyle_GetText(tree, column->style); 8138 else 8139 objPtr = NULL; 8140 if (objPtr == NULL) 8141 objPtr = Tcl_NewObj(); 8142 Tcl_ListObjAppendElement(interp, listObj, objPtr); 8143 treeColumn = TreeColumn_Next(treeColumn); 8144 if (column != NULL) 8145 column = column->next; 8146 } 8147 Tcl_SetObjResult(interp, listObj); 8148 break; 8149 } 8150 if (objc == 5) { 8151 if (Item_FindColumnFromObj(tree, item, objv[4], &column, NULL) != TCL_OK) { 8152 goto errorExit; 8153 } 8154 if ((column != NULL) && (column->style != NULL)) { 8155 objPtr = isImage ? 8156 TreeStyle_GetImage(tree, column->style) : 8157 TreeStyle_GetText(tree, column->style); 8158 if (objPtr != NULL) 8159 Tcl_SetObjResult(interp, objPtr); 8160 } 8161 break; 8162 } 8163 if ((objc - 4) & 1) { 8164 FormatResult(interp, "missing argument after column \"%s\"", 8165 Tcl_GetString(objv[objc - 1])); 8166 goto errorExit; 8167 } 8168 /* Gather column/obj pairs. */ 8169 STATIC_ALLOC(co, struct columnObj, objc / 2); 8170 for (i = 4; i < objc; i += 2) { 8171 if (TreeColumnList_FromObj(tree, objv[i], &co[count].columns, 8172 CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) { 8173 result = TCL_ERROR; 8174 goto doneTEXT; 8175 } 8176 co[count].obj = objv[i + 1]; 8177 count++; 8178 } 8179 ITEM_FOR_EACH(item, &itemList, NULL, &iter) { 8180 int changedI = FALSE; 8181 for (i = 0; i < count; i++) { 8182 COLUMN_FOR_EACH(treeColumn, &co[i].columns, NULL, &citer) { 8183 columnIndex = TreeColumn_Index(treeColumn); 8184 column = Item_FindColumn(tree, item, columnIndex); 8185 if ((column == NULL) || (column->style == NULL)) { 8186 NoStyleMsg(tree, item, columnIndex); 8187 result = TCL_ERROR; 8188 goto doneTEXT; 8189 } 8190 result = isImage ? 8191 TreeStyle_SetImage(tree, item, 8192 (TreeItemColumn) column, column->style, co[i].obj) : 8193 TreeStyle_SetText(tree, item, 8194 (TreeItemColumn) column, column->style, co[i].obj); 8195 if (result != TCL_OK) 8196 goto doneTEXT; 8197 TreeItemColumn_InvalidateSize(tree, (TreeItemColumn) column); 8198 Tree_InvalidateColumnWidth(tree, treeColumn); 8199 changedI = TRUE; 8200 } 8201 } 8202 if (changedI) { 8203 TreeItem_InvalidateHeight(tree, item); 8204 Tree_FreeItemDInfo(tree, item, NULL); 8205 changed = TRUE; 8206 } 8207 } 8208 if (changed) 8209 Tree_DInfoChanged(tree, DINFO_REDO_RANGES); 8210doneTEXT: 8211 for (i = 0; i < count; i++) { 8212 TreeColumnList_Free(&co[i].columns); 8213 } 8214 STATIC_FREE(co, struct columnObj, objc / 2); 8215 break; 8216 } 8217 } 8218 8219 TreeItemList_Free(&itemList); 8220 TreeItemList_Free(&item2List); 8221 return result; 8222 8223errorExit: 8224 TreeItemList_Free(&itemList); 8225 TreeItemList_Free(&item2List); 8226 return TCL_ERROR; 8227} 8228 8229/* 8230 *---------------------------------------------------------------------- 8231 * 8232 * TreeItem_Debug -- 8233 * 8234 * Perform some sanity checks on an Item and its descendants. 8235 * 8236 * Results: 8237 * TCL_OK or TCL_ERROR. 8238 * 8239 * Side effects: 8240 * None. 8241 * 8242 *---------------------------------------------------------------------- 8243 */ 8244 8245int TreeItem_Debug( 8246 TreeCtrl *tree, /* Widget info. */ 8247 TreeItem item /* Item token. */ 8248 ) 8249{ 8250 TreeItem child; 8251 Tcl_Interp *interp = tree->interp; 8252 int count; 8253 8254 if (item->parent == item) { 8255 FormatResult(interp, 8256 "parent of %d is itself", item->id); 8257 return TCL_ERROR; 8258 } 8259 8260 if (item->parent == NULL) { 8261 if (item->prevSibling != NULL) { 8262 FormatResult(interp, 8263 "parent of %d is nil, prevSibling is not nil", 8264 item->id); 8265 return TCL_ERROR; 8266 } 8267 if (item->nextSibling != NULL) { 8268 FormatResult(interp, 8269 "parent of %d is nil, nextSibling is not nil", 8270 item->id); 8271 return TCL_ERROR; 8272 } 8273 } 8274 8275 if (item->prevSibling != NULL) { 8276 if (item->prevSibling == item) { 8277 FormatResult(interp, 8278 "prevSibling of %d is itself", 8279 item->id); 8280 return TCL_ERROR; 8281 } 8282 if (item->prevSibling->nextSibling != item) { 8283 FormatResult(interp, 8284 "item%d.prevSibling.nextSibling is not it", 8285 item->id); 8286 return TCL_ERROR; 8287 } 8288 } 8289 8290 if (item->nextSibling != NULL) { 8291 if (item->nextSibling == item) { 8292 FormatResult(interp, 8293 "nextSibling of %d is itself", 8294 item->id); 8295 return TCL_ERROR; 8296 } 8297 if (item->nextSibling->prevSibling != item) { 8298 FormatResult(interp, 8299 "item%d.nextSibling->prevSibling is not it", 8300 item->id); 8301 return TCL_ERROR; 8302 } 8303 } 8304 8305 if (item->numChildren < 0) { 8306 FormatResult(interp, 8307 "numChildren of %d is %d", 8308 item->id, item->numChildren); 8309 return TCL_ERROR; 8310 } 8311 8312 if (item->numChildren == 0) { 8313 if (item->firstChild != NULL) { 8314 FormatResult(interp, 8315 "item%d.numChildren is zero, firstChild is not nil", 8316 item->id); 8317 return TCL_ERROR; 8318 } 8319 if (item->lastChild != NULL) { 8320 FormatResult(interp, 8321 "item%d.numChildren is zero, lastChild is not nil", 8322 item->id); 8323 return TCL_ERROR; 8324 } 8325 } 8326 8327 if (item->numChildren > 0) { 8328 if (item->firstChild == NULL) { 8329 FormatResult(interp, 8330 "item%d.firstChild is nil", 8331 item->id); 8332 return TCL_ERROR; 8333 } 8334 if (item->firstChild == item) { 8335 FormatResult(interp, 8336 "item%d.firstChild is itself", 8337 item->id); 8338 return TCL_ERROR; 8339 } 8340 if (item->firstChild->parent != item) { 8341 FormatResult(interp, 8342 "item%d.firstChild.parent is not it", 8343 item->id); 8344 return TCL_ERROR; 8345 } 8346 if (item->firstChild->prevSibling != NULL) { 8347 FormatResult(interp, 8348 "item%d.firstChild.prevSibling is not nil", 8349 item->id); 8350 return TCL_ERROR; 8351 } 8352 8353 if (item->lastChild == NULL) { 8354 FormatResult(interp, 8355 "item%d.lastChild is nil", 8356 item->id); 8357 return TCL_ERROR; 8358 } 8359 if (item->lastChild == item) { 8360 FormatResult(interp, 8361 "item%d.lastChild is itself", 8362 item->id); 8363 return TCL_ERROR; 8364 } 8365 if (item->lastChild->parent != item) { 8366 FormatResult(interp, 8367 "item%d.lastChild.parent is not it", 8368 item->id); 8369 return TCL_ERROR; 8370 } 8371 if (item->lastChild->nextSibling != NULL) { 8372 FormatResult(interp, 8373 "item%d.lastChild.nextSibling is not nil", 8374 item->id); 8375 return TCL_ERROR; 8376 } 8377 8378 /* Count number of children */ 8379 count = 0; 8380 child = item->firstChild; 8381 while (child != NULL) { 8382 count++; 8383 child = child->nextSibling; 8384 } 8385 if (count != item->numChildren) { 8386 FormatResult(interp, 8387 "item%d.numChildren is %d, but counted %d", 8388 item->id, item->numChildren, count); 8389 return TCL_ERROR; 8390 } 8391 8392 /* Debug each child recursively */ 8393 child = item->firstChild; 8394 while (child != NULL) { 8395 if (child->parent != item) { 8396 FormatResult(interp, 8397 "child->parent of %d is not it", 8398 item->id); 8399 return TCL_ERROR; 8400 } 8401 if (TreeItem_Debug(tree, child) != TCL_OK) 8402 return TCL_ERROR; 8403 child = child->nextSibling; 8404 } 8405 } 8406 return TCL_OK; 8407} 8408 8409/* 8410 *---------------------------------------------------------------------- 8411 * 8412 * SpanWalkProc_Identify -- 8413 * 8414 * Callback routine to TreeItem_WalkSpans for TreeItem_Identify. 8415 * 8416 * Results: 8417 * None. 8418 * 8419 * Side effects: 8420 * None. 8421 * 8422 *---------------------------------------------------------------------- 8423 */ 8424 8425static int 8426SpanWalkProc_Identify( 8427 TreeCtrl *tree, 8428 TreeItem item, 8429 SpanInfo *spanPtr, 8430 StyleDrawArgs *drawArgs, 8431 ClientData clientData 8432 ) 8433{ 8434 struct { 8435 int x; 8436 int y; 8437 char *buf; 8438 } *data = clientData; 8439 8440 if ((data->x < drawArgs->x + drawArgs->indent) || 8441 (data->x >= drawArgs->x + drawArgs->width)) 8442 return 0; 8443 8444 sprintf(data->buf + strlen(data->buf), " column %s%d", 8445 tree->columnPrefix, TreeColumn_GetID(spanPtr->treeColumn)); 8446 8447 if (drawArgs->style != NULL) { 8448 char *elem = TreeStyle_Identify(drawArgs, data->x, data->y); 8449 if (elem != NULL) 8450 sprintf(data->buf + strlen(data->buf), " elem %s", elem); 8451 } 8452 return 1; /* stop */ 8453} 8454 8455/* 8456 *---------------------------------------------------------------------- 8457 * 8458 * TreeItem_Identify -- 8459 * 8460 * Determine which column and element the given point is in. 8461 * This is used by the [identify] widget command. 8462 * 8463 * Results: 8464 * If the Item is not ReallyVisible() or no columns are visible 8465 * then buf[] is untouched. Otherwise the given string may be 8466 * appended with "column C" followed by "elem E" although both 8467 * are optional. 8468 * 8469 * Side effects: 8470 * None. 8471 * 8472 *---------------------------------------------------------------------- 8473 */ 8474 8475void 8476TreeItem_Identify( 8477 TreeCtrl *tree, /* Widget info. */ 8478 TreeItem item, /* Item token. */ 8479 int lock, /* Columns to hit-test. */ 8480 int x, int y, /* Item coords to hit-test with. */ 8481 char *buf /* NULL-terminated string which may be 8482 * appended. */ 8483 ) 8484{ 8485 int left, top, width, height; 8486 struct { 8487 int x; 8488 int y; 8489 char *buf; 8490 } clientData; 8491 8492 if (Tree_ItemBbox(tree, item, lock, 8493 &left, &top, &width, &height) < 0) 8494 return; 8495 8496 /* Tree_ItemBbox returns canvas coords. x/y are item coords. */ 8497 clientData.x = x; 8498 clientData.y = y; 8499 clientData.buf = buf; 8500 8501 TreeItem_WalkSpans(tree, item, lock, 8502 0, 0, width, height, 8503 SpanWalkProc_Identify, (ClientData) &clientData); 8504} 8505 8506/* 8507 *---------------------------------------------------------------------- 8508 * 8509 * SpanWalkProc_Identify2 -- 8510 * 8511 * Callback routine to TreeItem_WalkSpans for TreeItem_Identify2. 8512 * 8513 * Results: 8514 * None. 8515 * 8516 * Side effects: 8517 * None. 8518 * 8519 *---------------------------------------------------------------------- 8520 */ 8521 8522static int 8523SpanWalkProc_Identify2( 8524 TreeCtrl *tree, 8525 TreeItem item, 8526 SpanInfo *spanPtr, 8527 StyleDrawArgs *drawArgs, 8528 ClientData clientData 8529 ) 8530{ 8531 Tcl_Obj *subListObj; 8532 struct { 8533 int x1; int y1; 8534 int x2; int y2; 8535 Tcl_Obj *listObj; 8536 } *data = clientData; 8537 8538 if ((data->x2 < drawArgs->x + drawArgs->indent) || 8539 (data->x1 >= drawArgs->x + drawArgs->width)) 8540 return 0; 8541 8542 subListObj = Tcl_NewListObj(0, NULL); 8543 Tcl_ListObjAppendElement(tree->interp, subListObj, 8544 TreeColumn_ToObj(tree, spanPtr->treeColumn)); 8545 if (drawArgs->style != NULL) { 8546 StyleDrawArgs drawArgsCopy = *drawArgs; 8547 TreeStyle_Identify2(&drawArgsCopy, data->x1, data->y1, 8548 data->x2, data->y2, 8549 subListObj); 8550 } 8551 Tcl_ListObjAppendElement(tree->interp, data->listObj, subListObj); 8552 return drawArgs->x + drawArgs->width >= data->x2; 8553} 8554 8555/* 8556 *---------------------------------------------------------------------- 8557 * 8558 * TreeItem_Identify2 -- 8559 * 8560 * Determine which columns and elements intersect the given 8561 * area. This is used by the [marquee identify] widget command. 8562 * 8563 * Results: 8564 * If the Item is not ReallyVisible() or no columns are visible 8565 * then listObj is untouched. Otherwise the list is appended 8566 * with C {E ...} C {E...}. 8567 * 8568 * Side effects: 8569 * None. 8570 * 8571 *---------------------------------------------------------------------- 8572 */ 8573 8574void 8575TreeItem_Identify2( 8576 TreeCtrl *tree, /* Widget info. */ 8577 TreeItem item, /* Item token. */ 8578 int x1, int y1, /* Top-left of area to hit-test. */ 8579 int x2, int y2, /* Bottom-right of area to hit-test. */ 8580 Tcl_Obj *listObj /* Initialized list object. */ 8581 ) 8582{ 8583 int left, top, width, height; 8584 struct { 8585 int x1; int y1; 8586 int x2; int y2; 8587 Tcl_Obj *listObj; 8588 } clientData; 8589 8590 if (Tree_ItemBbox(tree, item, COLUMN_LOCK_NONE, 8591 &left, &top, &width, &height) < 0) 8592 return; 8593 8594 /* Tree_ItemBbox returns canvas coords. x1 etc are canvas coords. */ 8595 clientData.x1 = x1; 8596 clientData.y1 = y1; 8597 clientData.x2 = x2; 8598 clientData.y2 = y2; 8599 clientData.listObj = listObj; 8600 8601 TreeItem_WalkSpans(tree, item, COLUMN_LOCK_NONE, 8602 left, top, width, height, 8603 SpanWalkProc_Identify2, (ClientData) &clientData); 8604} 8605 8606/* 8607 *---------------------------------------------------------------------- 8608 * 8609 * SpanWalkProc_GetRects -- 8610 * 8611 * Callback routine to TreeItem_WalkSpans for TreeItem_GetRects. 8612 * 8613 * Results: 8614 * None. 8615 * 8616 * Side effects: 8617 * None. 8618 * 8619 *---------------------------------------------------------------------- 8620 */ 8621 8622static int 8623SpanWalkProc_GetRects( 8624 TreeCtrl *tree, 8625 TreeItem item, 8626 SpanInfo *spanPtr, 8627 StyleDrawArgs *drawArgs, 8628 ClientData clientData 8629 ) 8630{ 8631 int objc; 8632 Tcl_Obj *CONST *objv; 8633 struct { 8634 TreeColumn treeColumn; 8635 int count; 8636 Tcl_Obj *CONST *objv; 8637 TreeRectangle *rects; 8638 int result; 8639 } *data = clientData; 8640 8641 if (spanPtr->treeColumn != data->treeColumn) 8642 return 0; 8643 8644 /* Bounds of span. */ 8645 if (data->count == 0) { 8646 /* Not sure why I add 'indent' but for compatibility I will leave 8647 * it in. */ 8648 data->rects[0].x = drawArgs->x + drawArgs->indent; 8649 data->rects[0].y = drawArgs->y; 8650 data->rects[0].width = drawArgs->width - drawArgs->indent; 8651 data->rects[0].height = drawArgs->height; 8652 data->result = 1; /* # of rects */ 8653 return 1; /* stop */ 8654 } 8655 8656 if (drawArgs->style == NULL) { 8657 NoStyleMsg(tree, item, TreeColumn_Index(spanPtr->treeColumn)); 8658 data->result = -1; /* error */ 8659 return 1; /* stop */ 8660 } 8661 8662 if (data->count == -1) { 8663 /* All the elements. */ 8664 objc = 0; 8665 objv = NULL; 8666 } else { 8667 objc = data->count; 8668 objv = data->objv; 8669 } 8670 8671 data->result = TreeStyle_GetElemRects(drawArgs, objc, objv, data->rects); 8672 return 1; /* stop */ 8673} 8674 8675/* 8676 *---------------------------------------------------------------------- 8677 * 8678 * TreeItem_GetRects -- 8679 * 8680 * Returns zero or more bounding boxes for an item-column or 8681 * element(s) in an item-column. 8682 * 8683 * Results: 8684 * If the item is not visible, or has zero height/width, or the given 8685 * column is obscurred by a preceding span, or the column has zero 8686 * width or is not visible, the return value is zero. 8687 * 8688 * If count==0, the bounding box of the span is returned and the 8689 * return value is 1 (for a single rect). 8690 * 8691 * If count!=0, and the item-column does not have a style assigned, an 8692 * error is left in the interpreter result and the return value is -1. 8693 * 8694 * If count==-1, the bounding box of each element in the style is 8695 * returned and the return value is the number of elements in the style. 8696 * 8697 * If count>0, the bounding box of each element specified by the objv[] 8698 * array is returned and the return value is equal to count. If any of 8699 * the objv[] elements in not in the style, an error is left in the 8700 * interpreter result and the return value is -1. 8701 * 8702 * Side effects: 8703 * None. 8704 * 8705 *---------------------------------------------------------------------- 8706 */ 8707 8708int 8709TreeItem_GetRects( 8710 TreeCtrl *tree, /* Widget info. */ 8711 TreeItem item, /* Item token. */ 8712 TreeColumn treeColumn, /* The column to get rects for. */ 8713 int count, /* -1 means get rects for all elements. 8714 * 0 means get bounds of the span. 8715 * 1+ means objv[] contains names of elements 8716 * to get rects for. */ 8717 Tcl_Obj *CONST objv[], /* Array of element names or NULL. */ 8718 TreeRectangle rects[] /* Out: returned bounding boxes. */ 8719 ) 8720{ 8721 int left, top, width, height; 8722 int lock = TreeColumn_Lock(treeColumn); 8723 struct { 8724 TreeColumn treeColumn; 8725 int count; 8726 Tcl_Obj *CONST *objv; 8727 TreeRectangle *rects; 8728 int result; 8729 } clientData; 8730 8731 if (Tree_ItemBbox(tree, item, lock, 8732 &left, &top, &width, &height) < 0) 8733 return 0; 8734 8735 clientData.treeColumn = treeColumn; 8736 clientData.count = count; 8737 clientData.objv = objv; 8738 clientData.rects = rects; 8739 clientData.result = 0; /* -1 error, 0 no rects, 1+ success */ 8740 8741 TreeItem_WalkSpans(tree, item, lock, 8742 left, top, width, height, 8743 SpanWalkProc_GetRects, (ClientData) &clientData); 8744 8745 return clientData.result; 8746} 8747 8748/* 8749 *---------------------------------------------------------------------- 8750 * 8751 * TreeItem_Init -- 8752 * 8753 * Perform item-related initialization when a new TreeCtrl is 8754 * created. 8755 * 8756 * Results: 8757 * TCL_OK. 8758 * 8759 * Side effects: 8760 * Memory is allocated. 8761 * 8762 *---------------------------------------------------------------------- 8763 */ 8764 8765int 8766TreeItem_Init( 8767 TreeCtrl *tree /* Widget info. */ 8768 ) 8769{ 8770 ItemButtonCO_Init(itemOptionSpecs, "-button", ITEM_FLAG_BUTTON, 8771 ITEM_FLAG_BUTTON_AUTO); 8772 BooleanFlagCO_Init(itemOptionSpecs, "-visible", ITEM_FLAG_VISIBLE); 8773 BooleanFlagCO_Init(itemOptionSpecs, "-wrap", ITEM_FLAG_WRAP); 8774 8775 tree->itemOptionTable = Tk_CreateOptionTable(tree->interp, itemOptionSpecs); 8776 8777 tree->root = Item_AllocRoot(tree); 8778 tree->activeItem = tree->root; /* always non-null */ 8779 tree->anchorItem = tree->root; /* always non-null */ 8780 8781 return TCL_OK; 8782} 8783