1/* 2 * tkTreeDisplay.c -- 3 * 4 * This module implements treectrl widget's main display code. 5 * 6 * Copyright (c) 2002-2009 Tim Baker 7 * 8 * RCS: @(#) $Id: tkTreeDisplay.c,v 1.99 2010/06/13 23:09:09 treectrl Exp $ 9 */ 10 11#include "tkTreeCtrl.h" 12 13/* Window -> Canvas */ 14#define W2Cy(y) ((y) + tree->yOrigin) 15#define DW2Cy(y) ((y) + dInfo->yOrigin) 16 17#define COMPLEX_WHITESPACE 18#define REDRAW_RGN 19 20typedef struct TreeColumnDInfo_ TreeColumnDInfo_; 21typedef struct TreeDInfo_ TreeDInfo_; 22typedef struct RItem RItem; 23typedef struct Range Range; 24typedef struct DItem DItem; 25 26static void Range_RedoIfNeeded(TreeCtrl *tree); 27static int Range_TotalWidth(TreeCtrl *tree, Range *range_); 28static int Range_TotalHeight(TreeCtrl *tree, Range *range_); 29static void Range_Redo(TreeCtrl *tree); 30static Range *Range_UnderPoint(TreeCtrl *tree, int *x_, int *y_, int nearest); 31static RItem *Range_ItemUnderPoint(TreeCtrl *tree, Range *range, int *x_, 32 int *y_); 33 34/* One of these per TreeItem that is ReallyVisible(). */ 35struct RItem 36{ 37 TreeItem item; /* The item. */ 38 Range *range; /* Range the item is in. */ 39 int size; /* Height or width consumed in Range. */ 40 int offset; /* Vertical or horizontal offset in Range. */ 41 int index; /* 0-based index in Range. */ 42}; 43 44/* A collection of visible TreeItems. */ 45struct Range 46{ 47 RItem *first; 48 RItem *last; 49 int totalWidth; 50 int totalHeight; 51 int index; /* 0-based index in list of Ranges. */ 52 int offset; /* vertical/horizontal offset from canvas 53 * top/left. */ 54 Range *prev; 55 Range *next; 56}; 57 58typedef struct DItemArea { 59 int x; /* Where it should be drawn, window coords. */ 60 int width; /* Current width. */ 61 int dirty[4]; /* Dirty area in item coords. */ 62#define DITEM_DIRTY 0x0001 63#define DITEM_ALL_DIRTY 0x0002 64 int flags; 65} DItemArea; 66 67/* Display information for a TreeItem that is onscreen. */ 68struct DItem 69{ 70#ifdef TREECTRL_DEBUG 71 char magic[4]; 72#endif 73 TreeItem item; 74 int y; /* Where it should be drawn, window coords. */ 75 int height; /* Current height. */ 76 DItemArea area; 77 DItemArea left, right; 78 int oldX, oldY; /* Where it was last drawn, window coords. */ 79 Range *range; /* Range the TreeItem is in. */ 80 int index; /* Used for alternating background colors. */ 81 int oldIndex; /* Used for alternating background colors. */ 82 int *spans; /* span[n] is the column index of the item 83 * column displayed at the n'th tree column. */ 84 DItem *next; 85}; 86 87/* Display information for a TreeColumn. */ 88struct TreeColumnDInfo_ 89{ 90 int offset; /* Last seen x-offset */ 91 int width; /* Last seen column width */ 92}; 93 94/* Display information for a TreeCtrl. */ 95struct TreeDInfo_ 96{ 97 GC scrollGC; 98 int xOrigin; /* Last seen TreeCtrl.xOrigin */ 99 int yOrigin; /* Last seen TreeCtrl.yOrigin */ 100 int totalWidth; /* Last seen Tree_TotalWidth() */ 101 int totalHeight; /* Last seen Tree_TotalHeight() */ 102 int headerHeight; /* Last seen TreeCtrl.headerHeight */ 103 DItem *dItem; /* Head of list for each displayed item */ 104 DItem *dItemLast; /* Temp for UpdateDInfo() */ 105 DItem *dItemFree; /* List of unused DItems */ 106 Range *rangeFirst; /* Head of Ranges */ 107 Range *rangeLast; /* Tail of Ranges */ 108 Range *rangeFirstD; /* First range with valid display info */ 109 Range *rangeLastD; /* Last range with valid display info */ 110 RItem *rItem; /* Block of RItems for all Ranges */ 111 int rItemMax; /* size of rItem[] */ 112 int itemHeight; /* Observed max TreeItem height */ 113 int itemWidth; /* Observed max TreeItem width */ 114 TreeDrawable pixmapW; /* Pixmap as big as the window */ 115 TreeDrawable pixmapI; /* Pixmap as big as the largest item */ 116 TreeDrawable pixmapT; /* Pixmap as big as the window. 117 Use on non-composited desktops when 118 displaying non-XOR dragimage, marquee 119 and/or proxies. */ 120 TkRegion dirtyRgn; /* DOUBLEBUFFER_WINDOW */ 121 int flags; /* DINFO_XXX */ 122 int xScrollIncrement; /* Last seen TreeCtr.xScrollIncrement */ 123 int yScrollIncrement; /* Last seen TreeCtr.yScrollIncrement */ 124 int *xScrollIncrements; /* When tree->xScrollIncrement is zero */ 125 int *yScrollIncrements; /* When tree->yScrollIncrement is zero */ 126 int xScrollIncrementCount; /* Size of xScrollIncrements. */ 127 int yScrollIncrementCount; /* Size of yScrollIncrements. */ 128 int incrementTop; /* yScrollIncrement[] index of item at top */ 129 int incrementLeft; /* xScrollIncrement[] index of item at left */ 130 TkRegion wsRgn; /* Region containing whitespace */ 131#ifdef COMPLEX_WHITESPACE 132 int complexWhitespace; 133#endif 134 Tcl_HashTable itemVisHash; /* Table of visible items */ 135 int requests; /* Incremented for every call to 136 Tree_EventuallyRedraw */ 137 int bounds[4], empty; /* Bounds of TREE_AREA_CONTENT */ 138 int boundsL[4], emptyL; /* Bounds of TREE_AREA_LEFT */ 139 int boundsR[4], emptyR; /* Bounds of TREE_AREA_RIGHT */ 140 int widthOfColumnsLeft; /* Last seen Tree_WidthOfLeftColumns() */ 141 int widthOfColumnsRight; /* Last seen Tree_WidthOfRightColumns() */ 142 Range *rangeLock; /* If there is no Range for non-locked 143 * columns, this range holds the vertical 144 * offset and height of each ReallyVisible 145 * item for displaying locked columns. */ 146#ifdef REDRAW_RGN 147 TkRegion redrawRgn; /* Contains all redrawn (not copied) pixels 148 * during a single Tree_Display call. */ 149#endif /* REDRAW_RGN */ 150 int overlays; /* TRUE if the dragimage|marquee|proxy 151 * were drawn in non-XOR mode. */ 152}; 153 154#ifdef COMPLEX_WHITESPACE 155static int ComplexWhitespace(TreeCtrl *tree); 156#endif 157 158/*========*/ 159 160void 161Tree_FreeItemRInfo(TreeCtrl *tree, TreeItem item) 162{ 163 TreeItem_SetRInfo(tree, item, NULL); 164} 165 166static Range * 167Range_Free(TreeCtrl *tree, Range *range) 168{ 169 Range *next = range->next; 170 WFREE(range, Range); 171 return next; 172} 173 174/* 175 *---------------------------------------------------------------------- 176 * 177 * Range_Redo -- 178 * 179 * This procedure puts all ReallyVisible() TreeItems into a list of 180 * Ranges. If tree->wrapMode is TREE_WRAP_NONE and no visible items 181 * have the -wrap=true option there will only be a single Range. 182 * 183 * Results: 184 * None. 185 * 186 * Side effects: 187 * Memory may be allocated. 188 * 189 *---------------------------------------------------------------------- 190 */ 191 192static void 193Range_Redo( 194 TreeCtrl *tree /* Widget info. */ 195 ) 196{ 197 TreeDInfo dInfo = tree->dInfo; 198 Range *rangeList = dInfo->rangeFirst; 199 Range *range; 200 RItem *rItem; 201 TreeItem item = tree->root; 202 int fixedWidth = -1, stepWidth = -1; 203 int wrapCount = 0, wrapPixels = 0; 204 int count, pixels, rItemCount = 0; 205 int rangeIndex = 0, itemIndex; 206 207 if (tree->debug.enable && tree->debug.display) 208 dbwin("Range_Redo %s\n", Tk_PathName(tree->tkwin)); 209 210 /* Update column variables */ 211 (void) Tree_WidthOfColumns(tree); 212 213 dInfo->rangeFirst = NULL; 214 dInfo->rangeLast = NULL; 215 216 if (tree->columnCountVis < 1) 217 goto freeRanges; 218 219 switch (tree->wrapMode) { 220 case TREE_WRAP_NONE: 221 break; 222 case TREE_WRAP_ITEMS: 223 wrapCount = tree->wrapArg; 224 break; 225 case TREE_WRAP_PIXELS: 226 wrapPixels = tree->wrapArg; 227 break; 228 case TREE_WRAP_WINDOW: 229 wrapPixels = tree->vertical ? 230 Tree_ContentHeight(tree) : 231 Tree_ContentWidth(tree); 232 if (wrapPixels < 0) 233 wrapPixels = 0; 234 break; 235 } 236 237 /* For horizontal layout with wrapping I need to know how wide each item 238 * is. This is the same block of code as in Range_TotalWidth */ 239 if ((wrapPixels > 0) && !tree->vertical) { 240 241 /* More than one item column, so all items have the same width */ 242 if (tree->columnCountVis > 1) 243 fixedWidth = Tree_WidthOfColumns(tree); 244 245 /* Single item column, fixed width for all items */ 246 else if (tree->itemWidth > 0) 247 fixedWidth = tree->itemWidth; 248 249 /* Single item column, fixed width for all items */ 250 /* THIS IS FOR COMPATIBILITY ONLY */ 251 else if (TreeColumn_FixedWidth(tree->columnVis) != -1) 252 fixedWidth = TreeColumn_FixedWidth(tree->columns); 253 254 /* Single item column, want all items same width */ 255 else if (tree->itemWidthEqual 256#ifdef DEPRECATED 257 || TreeColumn_WidthHack(tree->columnVis) 258#endif /* DEPRECATED */ 259 ) { 260 fixedWidth = TreeColumn_WidthOfItems(tree->columnVis); 261 262 /* Each item is a multiple of this width */ 263 if (tree->itemWidMult > 0) 264 stepWidth = tree->itemWidMult; 265#ifdef DEPRECATED 266 else 267 stepWidth = TreeColumn_StepWidth(tree->columnVis); 268#endif /* DEPRECATED */ 269 270 if ((stepWidth != -1) && (fixedWidth % stepWidth)) 271 fixedWidth += stepWidth - fixedWidth % stepWidth; 272 273 /* Single item column, variable item width */ 274 } else { 275 276 /* Each item is a multiple of this width */ 277 if (tree->itemWidMult > 0) 278 stepWidth = tree->itemWidMult; 279#ifdef DEPRECATED 280 else 281 stepWidth = TreeColumn_StepWidth(tree->columnVis); 282#endif /* DEPRECATED */ 283 } 284 } 285 286 /* Speed up ReallyVisible() and get itemVisCount */ 287 Tree_UpdateItemIndex(tree); 288 289 if (dInfo->rItemMax < tree->itemVisCount) { 290 dInfo->rItem = (RItem *) ckrealloc((char *) dInfo->rItem, 291 tree->itemVisCount * sizeof(RItem)); 292 dInfo->rItemMax = tree->itemVisCount; 293 } 294 295 if (!TreeItem_ReallyVisible(tree, item)) 296 item = TreeItem_NextVisible(tree, item); 297 while (item != NULL) { 298 if (rangeList == NULL) 299 range = (Range *) ckalloc(sizeof(Range)); 300 else { 301 range = rangeList; 302 rangeList = rangeList->next; 303 } 304 memset(range, '\0', sizeof(Range)); 305 range->totalWidth = -1; 306 range->totalHeight = -1; 307 range->index = rangeIndex++; 308 count = 0; 309 pixels = 0; 310 itemIndex = 0; 311 while (1) { 312 rItem = dInfo->rItem + rItemCount; 313 if (rItemCount >= dInfo->rItemMax) 314 panic("rItemCount > dInfo->rItemMax"); 315 if (range->first == NULL) 316 range->first = range->last = rItem; 317 TreeItem_SetRInfo(tree, item, (TreeItemRInfo) rItem); 318 rItem->item = item; 319 rItem->range = range; 320 rItem->index = itemIndex; 321 322 /* Range must be <= this number of pixels */ 323 if (wrapPixels > 0) { 324 rItem->offset = pixels; 325 if (tree->vertical) { 326 rItem->size = TreeItem_Height(tree, item); 327 } else { 328 if (fixedWidth != -1) { 329 rItem->size = fixedWidth; 330 } else { 331 TreeItemColumn itemColumn = 332 TreeItem_FindColumn(tree, item, 333 TreeColumn_Index(tree->columnVis)); 334 if (itemColumn != NULL) { 335 int columnWidth = 336 TreeItemColumn_NeededWidth(tree, item, 337 itemColumn); 338 if (tree->columnTreeVis) 339 columnWidth += TreeItem_Indent(tree, item); 340 rItem->size = columnWidth; 341 } else 342 rItem->size = 0; 343 if ((stepWidth != -1) && (rItem->size % stepWidth)) 344 rItem->size += stepWidth - rItem->size % stepWidth; 345 } 346 } 347 /* Too big */ 348 if (pixels + rItem->size > wrapPixels) { 349 /* Ensure at least one item is in this Range */ 350 if (itemIndex == 0) { 351 pixels += rItem->size; 352 rItemCount++; 353 } 354 break; 355 } 356 pixels += rItem->size; 357 } 358 range->last = rItem; 359 itemIndex++; 360 rItemCount++; 361 if (++count == wrapCount) 362 break; 363 item = TreeItem_NextVisible(tree, item); 364 if (item == NULL) 365 break; 366 if (TreeItem_GetWrap(tree, item)) 367 break; 368 } 369 /* Since we needed to calculate the height or width of this range, 370 * we don't need to do it later in Range_TotalWidth/Height() */ 371 if (wrapPixels > 0) { 372 if (tree->vertical) 373 range->totalHeight = pixels; 374 else 375 range->totalWidth = pixels; 376 } 377 378 if (dInfo->rangeFirst == NULL) 379 dInfo->rangeFirst = range; 380 else { 381 range->prev = dInfo->rangeLast; 382 dInfo->rangeLast->next = range; 383 } 384 dInfo->rangeLast = range; 385 item = TreeItem_NextVisible(tree, range->last->item); 386 } 387 388freeRanges: 389 while (rangeList != NULL) 390 rangeList = Range_Free(tree, rangeList); 391 392 /* If there are no visible non-locked columns, we won't have a Range. 393 * But we need to know the offset/size of each item for drawing any 394 * locked columns (and for vertical scrolling... and hit testing). */ 395 if (dInfo->rangeLock != NULL) { 396 (void) Range_Free(tree, dInfo->rangeLock); 397 dInfo->rangeLock = NULL; 398 } 399 (void) Tree_WidthOfColumns(tree); /* update columnCountVisLeft etc */ 400 if ((dInfo->rangeFirst == NULL) && 401 (tree->columnCountVisLeft || 402 tree->columnCountVisRight)) { 403 404 /* Speed up ReallyVisible() and get itemVisCount */ 405 Tree_UpdateItemIndex(tree); 406 407 if (tree->itemVisCount == 0) 408 return; 409 410 if (dInfo->rItemMax < tree->itemVisCount) { 411 dInfo->rItem = (RItem *) ckrealloc((char *) dInfo->rItem, 412 tree->itemVisCount * sizeof(RItem)); 413 dInfo->rItemMax = tree->itemVisCount; 414 } 415 416 dInfo->rangeLock = (Range *) ckalloc(sizeof(Range)); 417 range = dInfo->rangeLock; 418 419 pixels = 0; 420 itemIndex = 0; 421 rItem = dInfo->rItem; 422 item = tree->root; 423 if (!TreeItem_ReallyVisible(tree, item)) 424 item = TreeItem_NextVisible(tree, item); 425 while (item != NULL) { 426 rItem->item = item; 427 rItem->range = range; 428 rItem->size = TreeItem_Height(tree, item); 429 rItem->offset = pixels; 430 rItem->index = itemIndex++; 431 TreeItem_SetRInfo(tree, item, (TreeItemRInfo) rItem); 432 pixels += rItem->size; 433 rItem++; 434 item = TreeItem_NextVisible(tree, item); 435 } 436 437 range->offset = 0; 438 range->first = dInfo->rItem; 439 range->last = dInfo->rItem + tree->itemVisCount - 1; 440 range->totalWidth = 1; 441 range->totalHeight = pixels; 442 range->prev = range->next = NULL; 443 } 444} 445 446/* 447 *---------------------------------------------------------------------- 448 * 449 * Range_TotalWidth -- 450 * 451 * Return the width of a Range. The width is only calculated if 452 * it hasn't been done yet by Range_Redo(). 453 * 454 * Results: 455 * Pixel width of the Range. 456 * 457 * Side effects: 458 * None. 459 * 460 *---------------------------------------------------------------------- 461 */ 462 463static int 464Range_TotalWidth( 465 TreeCtrl *tree, /* Widget info. */ 466 Range *range /* Range to return the width of. */ 467 ) 468{ 469 TreeItem item; 470 TreeItemColumn itemColumn; 471 RItem *rItem; 472 int fixedWidth = -1, stepWidth = -1; 473 int itemWidth; 474 475 if (range->totalWidth >= 0) 476 return range->totalWidth; 477 478 if (tree->vertical) { 479 480 /* More than one item column, so all ranges have the same width */ 481 if (tree->columnCountVis > 1) 482 return range->totalWidth = Tree_WidthOfColumns(tree); 483 484 /* If wrapping is disabled, then use the column width, 485 * since it may expand to fill the window */ 486#if 1 487 if ((tree->wrapMode == TREE_WRAP_NONE) && (tree->itemWrapCount <= 0)) 488#else 489 if (tree->wrapMode == TREE_WRAP_NONE) 490#endif 491 return range->totalWidth = TreeColumn_UseWidth(tree->columnVis); 492 493 /* Single item column, fixed width for all ranges */ 494 if (tree->itemWidth > 0) 495 return range->totalWidth = tree->itemWidth; 496 497 /* Single item column, fixed width for all ranges */ 498 /* THIS IS FOR COMPATIBILITY ONLY */ 499 if (TreeColumn_FixedWidth(tree->columnVis) != -1) 500 return range->totalWidth = TreeColumn_FixedWidth(tree->columnVis); 501 502 /* Single item column, each item is a multiple of this width */ 503 if (tree->itemWidMult > 0) 504 stepWidth = tree->itemWidMult; 505#ifdef DEPRECATED 506 else 507 stepWidth = TreeColumn_StepWidth(tree->columnVis); 508#endif /* DEPRECATED */ 509 510 /* Single item column, want all items same width */ 511 if (tree->itemWidthEqual 512#ifdef DEPRECATED 513 || TreeColumn_WidthHack(tree->columnVis) 514#endif /* DEPRECATED */ 515 ) { 516 range->totalWidth = TreeColumn_WidthOfItems(tree->columnVis); 517 if ((stepWidth != -1) && (range->totalWidth % stepWidth)) 518 range->totalWidth += stepWidth - range->totalWidth % stepWidth; 519 return range->totalWidth; 520 } 521 522 /* Max needed width of items in this range */ 523 range->totalWidth = 0; 524 rItem = range->first; 525 while (1) { 526 item = rItem->item; 527 itemColumn = TreeItem_FindColumn(tree, item, 528 TreeColumn_Index(tree->columnVis)); 529 if (itemColumn != NULL) 530 itemWidth = TreeItemColumn_NeededWidth(tree, item, 531 itemColumn); 532 else 533 itemWidth = 0; 534 if (tree->columnTreeVis) 535 itemWidth += TreeItem_Indent(tree, item); 536 if (itemWidth > range->totalWidth) 537 range->totalWidth = itemWidth; 538 if (rItem == range->last) 539 break; 540 rItem++; 541 } 542 if ((stepWidth != -1) && (range->totalWidth % stepWidth)) 543 range->totalWidth += stepWidth - range->totalWidth % stepWidth; 544 return range->totalWidth; 545 } 546 else { 547 /* More than one item column, so all items have the same width */ 548 if (tree->columnCountVis > 1) 549 fixedWidth = Tree_WidthOfColumns(tree); 550 551 /* Single item column, fixed width for all items */ 552 else if (tree->itemWidth > 0) 553 fixedWidth = tree->itemWidth; 554 555 /* Single item column, fixed width for all items */ 556 /* THIS IS FOR COMPATIBILITY ONLY */ 557 else if (TreeColumn_FixedWidth(tree->columnVis) != -1) 558 fixedWidth = TreeColumn_FixedWidth(tree->columnVis); 559 560 /* Single item column, want all items same width */ 561 else if (tree->itemWidthEqual 562#ifdef DEPRECATED 563 || TreeColumn_WidthHack(tree->columnVis) 564#endif /* DEPRECATED */ 565 ) { 566 fixedWidth = TreeColumn_WidthOfItems(tree->columnVis); 567 568 /* Each item is a multiple of this width */ 569 if (tree->itemWidMult > 0) 570 stepWidth = tree->itemWidMult; 571#ifdef DEPRECATED 572 else 573 stepWidth = TreeColumn_StepWidth(tree->columnVis); 574#endif /* DEPRECATED */ 575 576 if ((stepWidth != -1) && (fixedWidth % stepWidth)) 577 fixedWidth += stepWidth - fixedWidth % stepWidth; 578 } 579 580 /* Single item column, variable item width */ 581 else { 582 /* Each item is a multiple of this width */ 583 if (tree->itemWidMult > 0) 584 stepWidth = tree->itemWidMult; 585#ifdef DEPRECATED 586 else 587 stepWidth = TreeColumn_StepWidth(tree->columnVis); 588#endif /* DEPRECATED */ 589 } 590 591 /* Sum of widths of items in this range */ 592 range->totalWidth = 0; 593 rItem = range->first; 594 while (1) { 595 item = rItem->item; 596 if (fixedWidth != -1) 597 itemWidth = fixedWidth; 598 else { 599 itemColumn = TreeItem_FindColumn(tree, item, 600 TreeColumn_Index(tree->columnVis)); 601 if (itemColumn != NULL) 602 itemWidth = TreeItemColumn_NeededWidth(tree, item, 603 itemColumn); 604 else 605 itemWidth = 0; 606 607 if (tree->columnTreeVis) 608 itemWidth += TreeItem_Indent(tree, item); 609 610 if ((stepWidth != -1) && (itemWidth % stepWidth)) 611 itemWidth += stepWidth - itemWidth % stepWidth; 612 } 613 614 rItem = (RItem *) TreeItem_GetRInfo(tree, item); 615 rItem->offset = range->totalWidth; 616 rItem->size = itemWidth; 617 618 range->totalWidth += itemWidth; 619 if (rItem == range->last) 620 break; 621 rItem++; 622 } 623 return range->totalWidth; 624 } 625} 626 627/* 628 *---------------------------------------------------------------------- 629 * 630 * Range_TotalHeight -- 631 * 632 * Return the height of a Range. The height is only calculated if 633 * it hasn't been done yet by Range_Redo(). 634 * 635 * Results: 636 * Pixel height of the Range. 637 * 638 * Side effects: 639 * None. 640 * 641 *---------------------------------------------------------------------- 642 */ 643 644static int 645Range_TotalHeight( 646 TreeCtrl *tree, /* Widget info. */ 647 Range *range /* Range to return the height of. */ 648 ) 649{ 650 TreeItem item; 651 RItem *rItem; 652 int itemHeight; 653 654 if (range->totalHeight >= 0) 655 return range->totalHeight; 656 657 range->totalHeight = 0; 658 rItem = range->first; 659 while (1) { 660 item = rItem->item; 661 itemHeight = TreeItem_Height(tree, item); 662 if (tree->vertical) { 663 rItem->offset = range->totalHeight; 664 rItem->size = itemHeight; 665 range->totalHeight += itemHeight; 666 } 667 else { 668 if (itemHeight > range->totalHeight) 669 range->totalHeight = itemHeight; 670 } 671 if (rItem == range->last) 672 break; 673 rItem++; 674 } 675 return range->totalHeight; 676} 677 678/* 679 *---------------------------------------------------------------------- 680 * 681 * Tree_TotalWidth -- 682 * 683 * Return the width needed by all the Ranges. The width is only 684 * recalculated if it was marked out-of-date. 685 * 686 * Results: 687 * Pixel width of the "canvas". 688 * 689 * Side effects: 690 * The list of Ranges will be recalculated if needed. 691 * 692 *---------------------------------------------------------------------- 693 */ 694 695int 696Tree_TotalWidth( 697 TreeCtrl *tree /* Widget info. */ 698 ) 699{ 700 TreeDInfo dInfo = tree->dInfo; 701 Range *range; 702 int rangeWidth; 703 704 Range_RedoIfNeeded(tree); 705 706 if (tree->totalWidth >= 0) 707 return tree->totalWidth; 708 709 if (dInfo->rangeFirst == NULL) 710 return tree->totalWidth = Tree_WidthOfColumns(tree); 711 712 tree->totalWidth = 0; 713 range = dInfo->rangeFirst; 714 while (range != NULL) { 715 rangeWidth = Range_TotalWidth(tree, range); 716 if (tree->vertical) { 717 range->offset = tree->totalWidth; 718 tree->totalWidth += rangeWidth; 719 } 720 else { 721 if (rangeWidth > tree->totalWidth) 722 tree->totalWidth = rangeWidth; 723 } 724 range = range->next; 725 } 726 return tree->totalWidth; 727} 728 729/* 730 *---------------------------------------------------------------------- 731 * 732 * Tree_TotalHeight -- 733 * 734 * Return the height needed by all the Ranges. The height is only 735 * recalculated if it was marked out-of-date. 736 * 737 * Results: 738 * Pixel height of the "canvas". 739 * 740 * Side effects: 741 * The list of Ranges will be recalculated if needed. 742 * 743 *---------------------------------------------------------------------- 744 */ 745 746int 747Tree_TotalHeight( 748 TreeCtrl *tree /* Widget info. */ 749 ) 750{ 751 TreeDInfo dInfo = tree->dInfo; 752 Range *range; 753 int rangeHeight; 754 755 Range_RedoIfNeeded(tree); 756 757 if (tree->totalHeight >= 0) 758 return tree->totalHeight; 759 760 tree->totalHeight = 0; 761 range = dInfo->rangeFirst; 762 while (range != NULL) { 763 rangeHeight = Range_TotalHeight(tree, range); 764 if (tree->vertical) { 765 if (rangeHeight > tree->totalHeight) 766 tree->totalHeight = rangeHeight; 767 } 768 else { 769 range->offset = tree->totalHeight; 770 tree->totalHeight += rangeHeight; 771 } 772 range = range->next; 773 } 774 775 /* If dInfo->rangeLock is not NULL, then we are displaying some items 776 * in locked columns but no non-locked columns. */ 777 if (dInfo->rangeLock != NULL) { 778 if (dInfo->rangeLock->totalHeight > tree->totalHeight) 779 tree->totalHeight = dInfo->rangeLock->totalHeight; 780 } 781 782 return tree->totalHeight; 783} 784 785/* 786 *---------------------------------------------------------------------- 787 * 788 * Range_UnderPoint -- 789 * 790 * Return the Range containing the given coordinates. 791 * 792 * Results: 793 * Range containing the coordinates or NULL if the point is 794 * outside any Range. 795 * 796 * Side effects: 797 * The list of Ranges will be recalculated if needed. 798 * 799 *---------------------------------------------------------------------- 800 */ 801 802static Range * 803Range_UnderPoint( 804 TreeCtrl *tree, /* Widget info. */ 805 int *x_, /* In: window x coordinate. 806 * Out: x coordinate relative to the top-left 807 * of the Range. */ 808 int *y_, /* In: window y coordinate. 809 * Out: y coordinate relative to the top-left 810 * of the Range. */ 811 int nearest /* TRUE if the Range nearest the coordinates 812 * should be returned. */ 813 ) 814{ 815 TreeDInfo dInfo = tree->dInfo; 816 Range *range; 817 int x = *x_, y = *y_; 818 819 Range_RedoIfNeeded(tree); 820 821 if ((Tree_TotalWidth(tree) <= 0) || (Tree_TotalHeight(tree) <= 0)) 822 return NULL; 823 824 range = dInfo->rangeFirst; 825 826 if (nearest) { 827 int minX, minY, maxX, maxY; 828 829 if (!Tree_AreaBbox(tree, TREE_AREA_CONTENT, &minX, &minY, &maxX, &maxY)) 830 return NULL; 831 832 /* Keep inside borders and header. Perhaps another flag needed. */ 833 if (x < minX) 834 x = minX; 835 if (x >= maxX) 836 x = maxX - 1; 837 if (y < minY) 838 y = minY; 839 if (y >= maxY) 840 y = maxY - 1; 841 } 842 843 /* Window -> canvas */ 844 x += tree->xOrigin; 845 y += tree->yOrigin; 846 847 if (nearest) { 848 if (x < 0) 849 x = 0; 850 if (x >= Tree_TotalWidth(tree)) 851 x = Tree_TotalWidth(tree) - 1; 852 if (y < 0) 853 y = 0; 854 if (y >= Tree_TotalHeight(tree)) 855 y = Tree_TotalHeight(tree) - 1; 856 } 857 else { 858 if (x < 0) 859 return NULL; 860 if (x >= Tree_TotalWidth(tree)) 861 return NULL; 862 if (y < 0) 863 return NULL; 864 if (y >= Tree_TotalHeight(tree)) 865 return NULL; 866 } 867 868 if (tree->vertical) { 869 while (range != NULL) { 870 if ((x >= range->offset) && (x < range->offset + range->totalWidth)) { 871 if (nearest || (y < range->totalHeight)) { 872 (*x_) = x - range->offset; 873 (*y_) = MIN(y, range->totalHeight - 1); 874 return range; 875 } 876 return NULL; 877 } 878 range = range->next; 879 } 880 return NULL; 881 } 882 else { 883 while (range != NULL) { 884 if ((y >= range->offset) && (y < range->offset + range->totalHeight)) { 885 if (nearest || (x < range->totalWidth)) { 886 (*x_) = MIN(x, range->totalWidth - 1); 887 (*y_) = y - range->offset; 888 return range; 889 } 890 return NULL; 891 } 892 range = range->next; 893 } 894 return NULL; 895 } 896} 897 898/* 899 *---------------------------------------------------------------------- 900 * 901 * Range_ItemUnderPoint -- 902 * 903 * Return the RItem containing the given x and/or y coordinates. 904 * 905 * Results: 906 * RItem containing the coordinates. Panics() if no RItem is found. 907 * 908 * Side effects: 909 * None. 910 * 911 *---------------------------------------------------------------------- 912 */ 913 914static RItem * 915Range_ItemUnderPoint( 916 TreeCtrl *tree, /* Widget info. */ 917 Range *range, /* Range to search. */ 918 int *x_, /* In: x coordinate relative to top-left of 919 * the Range. 920 * Out: x coordinate relative to top-left of 921 * the returned RItem. 922 * May be NULL if y_ is not NULL. */ 923 int *y_ /* In: y coordinate relative to top-left of 924 * the Range. 925 * Out: y coordinate relative to top-left of 926 * the returned RItem. 927 * May be NULL if x_ is not NULL. */ 928 ) 929{ 930 RItem *rItem; 931 int x = -666, y = -666; 932 int i, l, u; 933 934 if (x_ != NULL) { 935 x = (*x_); 936 if (x < 0 || x >= range->totalWidth) 937 goto panicNow; 938 } 939 if (y_ != NULL) { 940 y = (*y_); 941 if (y < 0 || y >= range->totalHeight) 942 goto panicNow; 943 } 944 945 if (tree->vertical) { 946 /* Binary search */ 947 l = 0; 948 u = range->last->index; 949 while (l <= u) { 950 i = (l + u) / 2; 951 rItem = range->first + i; 952 if ((y >= rItem->offset) && (y < rItem->offset + rItem->size)) { 953 /* Range -> item coords */ 954 if (x_ != NULL) (*x_) = x; 955 if (y_ != NULL) (*y_) = y - rItem->offset; 956 return rItem; 957 } 958 if (y < rItem->offset) 959 u = i - 1; 960 else 961 l = i + 1; 962 } 963 } 964 else { 965 /* Binary search */ 966 l = 0; 967 u = range->last->index; 968 while (l <= u) { 969 i = (l + u) / 2; 970 rItem = range->first + i; 971 if ((x >= rItem->offset) && (x < rItem->offset + rItem->size)) { 972 /* Range -> item coords */ 973 if (x_ != NULL) (*x_) = x - rItem->offset; 974 if (y_ != NULL) (*y_) = y; 975 return rItem; 976 } 977 if (x < rItem->offset) 978 u = i - 1; 979 else 980 l = i + 1; 981 } 982 } 983 984 panicNow: 985 panic("Range_ItemUnderPoint: can't find TreeItem in Range: x %d y %d W %d H %d", 986 x, y, range->totalWidth, range->totalHeight); 987 return NULL; 988} 989 990/* 991 *---------------------------------------------------------------------- 992 * 993 * Increment_AddX -- 994 * 995 * Appends one or more values to the list of horizontal scroll 996 * increments. 997 * 998 * Results: 999 * New size of DInfo.xScrollIncrements. 1000 * 1001 * Side effects: 1002 * Memory may be allocated. 1003 * 1004 *---------------------------------------------------------------------- 1005 */ 1006 1007static int 1008Increment_AddX( 1009 TreeCtrl *tree, /* Widget info. */ 1010 int offset, /* Offset to add. */ 1011 int size /* Current size of DInfo.xScrollIncrements. */ 1012 ) 1013{ 1014 TreeDInfo dInfo = tree->dInfo; 1015 int visWidth = Tree_ContentWidth(tree); 1016 1017 while ((visWidth > 1) && (dInfo->xScrollIncrementCount > 0) && 1018 (offset - dInfo->xScrollIncrements[dInfo->xScrollIncrementCount - 1] 1019 > visWidth)) { 1020 size = Increment_AddX(tree, 1021 dInfo->xScrollIncrements[dInfo->xScrollIncrementCount - 1] + visWidth, 1022 size); 1023 } 1024 if (dInfo->xScrollIncrementCount + 1 > size) { 1025 size *= 2; 1026 dInfo->xScrollIncrements = (int *) ckrealloc( 1027 (char *) dInfo->xScrollIncrements, size * sizeof(int)); 1028 } 1029 dInfo->xScrollIncrements[dInfo->xScrollIncrementCount++] = offset; 1030 return size; 1031} 1032 1033/* 1034 *---------------------------------------------------------------------- 1035 * 1036 * Increment_AddY -- 1037 * 1038 * Appends one or more values to the list of vertical scroll 1039 * increments. 1040 * 1041 * Results: 1042 * New size of DInfo.yScrollIncrements. 1043 * 1044 * Side effects: 1045 * Memory may be allocated. 1046 * 1047 *---------------------------------------------------------------------- 1048 */ 1049 1050static int 1051Increment_AddY( 1052 TreeCtrl *tree, /* Widget info. */ 1053 int offset, /* Offset to add. */ 1054 int size /* Current size of DInfo.yScrollIncrements. */ 1055 ) 1056{ 1057 TreeDInfo dInfo = tree->dInfo; 1058 int visHeight = Tree_ContentHeight(tree); 1059 1060 while ((visHeight > 1) && (dInfo->yScrollIncrementCount > 0) && 1061 (offset - dInfo->yScrollIncrements[dInfo->yScrollIncrementCount - 1] 1062 > visHeight)) { 1063 size = Increment_AddY(tree, 1064 dInfo->yScrollIncrements[dInfo->yScrollIncrementCount - 1] + visHeight, 1065 size); 1066 } 1067 if (dInfo->yScrollIncrementCount + 1 > size) { 1068 size *= 2; 1069 dInfo->yScrollIncrements = (int *) ckrealloc( 1070 (char *) dInfo->yScrollIncrements, size * sizeof(int)); 1071 } 1072 dInfo->yScrollIncrements[dInfo->yScrollIncrementCount++] = offset; 1073 return size; 1074} 1075 1076/* 1077 *---------------------------------------------------------------------- 1078 * 1079 * RItemsToIncrementsX -- 1080 * 1081 * Recalculate the list of horizontal scroll increments. This gets 1082 * called when the TreeCtrl -orient option is "horizontal" and 1083 * -xscrollincrement option is "". 1084 * 1085 * Results: 1086 * DInfo.xScrollIncrements is updated if the canvas width is > 0. 1087 * 1088 * Side effects: 1089 * Memory may be allocated. 1090 * 1091 *---------------------------------------------------------------------- 1092 */ 1093 1094static void 1095RItemsToIncrementsX( 1096 TreeCtrl *tree /* Widget info. */ 1097 ) 1098{ 1099 TreeDInfo dInfo = tree->dInfo; 1100 Range *range, *rangeFirst = dInfo->rangeFirst; 1101 RItem *rItem; 1102 int visWidth = Tree_ContentWidth(tree); 1103 int totalWidth = Tree_TotalWidth(tree); 1104 int x1, x2, x; 1105 int size; 1106 1107 if (totalWidth <= 0 /* dInfo->rangeFirst == NULL */) 1108 return; 1109 1110 size = 10; 1111 dInfo->xScrollIncrements = (int *) ckalloc(size * sizeof(int)); 1112 1113 if (rangeFirst == NULL) { 1114 /* Only the column headers are shown. */ 1115 } else if (rangeFirst->next == NULL) { 1116 /* A single horizontal range is easy. Add one increment for the 1117 * left edge of each item. */ 1118 rItem = rangeFirst->first; 1119 while (1) { 1120 size = Increment_AddX(tree, rItem->offset, size); 1121 if (rItem == rangeFirst->last) 1122 break; 1123 rItem++; 1124 } 1125 } else { 1126 /* First increment is zero */ 1127 dInfo->xScrollIncrements[dInfo->xScrollIncrementCount++] = 0; 1128 1129 x1 = 0; 1130 while (1) { 1131 x2 = totalWidth; 1132 for (range = rangeFirst; range != NULL; range = range->next) { 1133 if (x1 >= range->totalWidth) 1134 continue; 1135 1136 /* Find RItem whose right side is >= x1 by smallest amount */ 1137 x = x1; 1138 rItem = Range_ItemUnderPoint(tree, range, &x, NULL); 1139 if (rItem->offset + rItem->size < x2) 1140 x2 = rItem->offset + rItem->size; 1141 } 1142 if (x2 == totalWidth) 1143 break; 1144 size = Increment_AddX(tree, x2, size); 1145 x1 = x2; 1146 } 1147 } 1148 if ((visWidth > 1) && (totalWidth - 1149 dInfo->xScrollIncrements[dInfo->xScrollIncrementCount - 1] > visWidth)) { 1150 Increment_AddX(tree, totalWidth, size); 1151 dInfo->xScrollIncrementCount--; 1152 dInfo->xScrollIncrements[dInfo->xScrollIncrementCount - 1] = totalWidth - visWidth; 1153 } 1154} 1155 1156/* 1157 *---------------------------------------------------------------------- 1158 * 1159 * RItemsToIncrementsY -- 1160 * 1161 * Recalculate the list of vertical scroll increments. This gets 1162 * called when the TreeCtrl -orient option is "vertical" and 1163 * -yscrollincrement option is "". 1164 * 1165 * Results: 1166 * DInfo.yScrollIncrements is updated if the canvas height is > 0. 1167 * 1168 * Side effects: 1169 * Memory may be allocated. 1170 * 1171 *---------------------------------------------------------------------- 1172 */ 1173 1174static void 1175RItemsToIncrementsY( 1176 TreeCtrl *tree /* Widget info. */ 1177 ) 1178{ 1179 TreeDInfo dInfo = tree->dInfo; 1180 Range *range, *rangeFirst; 1181 RItem *rItem; 1182 int visHeight = Tree_ContentHeight(tree); 1183 int totalHeight = Tree_TotalHeight(tree); 1184 int y1, y2, y; 1185 int size; 1186 1187 if (totalHeight <= 0 /* dInfo->rangeFirst == NULL */) 1188 return; 1189 1190 size = 10; 1191 dInfo->yScrollIncrements = (int *) ckalloc(size * sizeof(int)); 1192 1193 /* If only locked columns are visible, we still scroll vertically. */ 1194 rangeFirst = dInfo->rangeFirst; 1195 if (rangeFirst == NULL) 1196 rangeFirst = dInfo->rangeLock; 1197 1198 if (rangeFirst->next == NULL) { 1199 /* A single vertical range is easy. Add one increment for the 1200 * top edge of each item. */ 1201 rItem = rangeFirst->first; 1202 while (1) { 1203 size = Increment_AddY(tree, rItem->offset, size); 1204 if (rItem == rangeFirst->last) 1205 break; 1206 rItem++; 1207 } 1208 } else { 1209 /* First increment is zero */ 1210 dInfo->yScrollIncrements[dInfo->yScrollIncrementCount++] = 0; 1211 1212 y1 = 0; 1213 while (1) { 1214 y2 = totalHeight; 1215 for (range = rangeFirst; range != NULL; range = range->next) { 1216 if (y1 >= range->totalHeight) 1217 continue; 1218 1219 /* Find RItem whose bottom edge is >= y1 by smallest amount */ 1220 y = y1; 1221 rItem = Range_ItemUnderPoint(tree, range, NULL, &y); 1222 if (rItem->offset + rItem->size < y2) 1223 y2 = rItem->offset + rItem->size; 1224 } 1225 if (y2 == totalHeight) 1226 break; 1227 size = Increment_AddY(tree, y2, size); 1228 y1 = y2; 1229 } 1230 } 1231 1232 if ((visHeight > 1) && (totalHeight - 1233 dInfo->yScrollIncrements[dInfo->yScrollIncrementCount - 1] > visHeight)) { 1234 size = Increment_AddY(tree, totalHeight, size); 1235 dInfo->yScrollIncrementCount--; 1236 dInfo->yScrollIncrements[dInfo->yScrollIncrementCount - 1] = totalHeight - visHeight; 1237 } 1238} 1239 1240/* 1241 *---------------------------------------------------------------------- 1242 * 1243 * RangesToIncrementsX -- 1244 * 1245 * Recalculate the list of horizontal scroll increments. This gets 1246 * called when the TreeCtrl -orient option is "vertical" and 1247 * -xscrollincrement option is "". 1248 * 1249 * Results: 1250 * DInfo.xScrollIncrements is updated if there are any Ranges. 1251 * 1252 * Side effects: 1253 * Memory may be allocated. 1254 * 1255 *---------------------------------------------------------------------- 1256 */ 1257 1258static void 1259RangesToIncrementsX( 1260 TreeCtrl *tree /* Widget info. */ 1261 ) 1262{ 1263 TreeDInfo dInfo = tree->dInfo; 1264 Range *range = dInfo->rangeFirst; 1265 int visWidth = Tree_ContentWidth(tree); 1266 int totalWidth = Tree_TotalWidth(tree); 1267 int size; 1268 1269 if (totalWidth <= visWidth) 1270 return; 1271 1272 /* First increment is zero */ 1273 size = 10; 1274 dInfo->xScrollIncrements = (int *) ckalloc(size * sizeof(int)); 1275 dInfo->xScrollIncrements[dInfo->xScrollIncrementCount++] = 0; 1276 1277 if (dInfo->rangeFirst != NULL) { 1278 range = dInfo->rangeFirst->next; 1279 while (range != NULL) { 1280 size = Increment_AddX(tree, range->offset, size); 1281 range = range->next; 1282 } 1283 } 1284 if ((visWidth > 1) && (totalWidth - 1285 dInfo->xScrollIncrements[dInfo->xScrollIncrementCount - 1] > visWidth)) { 1286 size = Increment_AddX(tree, totalWidth, size); 1287 dInfo->xScrollIncrementCount--; 1288 dInfo->xScrollIncrements[dInfo->xScrollIncrementCount - 1] = totalWidth - visWidth; 1289 } 1290} 1291 1292/* 1293 *---------------------------------------------------------------------- 1294 * 1295 * RangesToIncrementsY -- 1296 * 1297 * Recalculate the list of vertical scroll increments. This gets 1298 * called when the TreeCtrl -orient option is "horizontal" and 1299 * -yscrollincrement option is "". 1300 * 1301 * Results: 1302 * DInfo.yScrollIncrements is updated if there are any Ranges. 1303 * 1304 * Side effects: 1305 * Memory may be allocated. 1306 * 1307 *---------------------------------------------------------------------- 1308 */ 1309 1310static void 1311RangesToIncrementsY( 1312 TreeCtrl *tree /* Widget info. */ 1313 ) 1314{ 1315 TreeDInfo dInfo = tree->dInfo; 1316 Range *range = dInfo->rangeFirst; 1317 int visHeight = Tree_ContentHeight(tree); 1318 int totalHeight = Tree_TotalHeight(tree); 1319 int size; 1320 1321 if (dInfo->rangeFirst == NULL) 1322 return; 1323 1324 /* First increment is zero */ 1325 size = 10; 1326 dInfo->yScrollIncrements = (int *) ckalloc(size * sizeof(int)); 1327 dInfo->yScrollIncrements[dInfo->yScrollIncrementCount++] = 0; 1328 1329 range = dInfo->rangeFirst->next; 1330 while (range != NULL) { 1331 size = Increment_AddY(tree, range->offset, size); 1332 range = range->next; 1333 } 1334 if ((visHeight > 1) && (totalHeight - 1335 dInfo->yScrollIncrements[dInfo->yScrollIncrementCount - 1] > visHeight)) { 1336 size = Increment_AddY(tree, totalHeight, size); 1337 dInfo->yScrollIncrementCount--; 1338 dInfo->yScrollIncrements[dInfo->yScrollIncrementCount - 1] = totalHeight - visHeight; 1339 } 1340} 1341 1342/* 1343 *---------------------------------------------------------------------- 1344 * 1345 * Increment_Redo -- 1346 * 1347 * Recalculate the lists of scroll increments. 1348 * 1349 * Results: 1350 * DInfo.xScrollIncrements and DInfo.xScrollIncrements are updated. 1351 * Either may be set to NULL. The old values are freed, if any. 1352 * 1353 * Side effects: 1354 * Memory may be allocated. 1355 * 1356 *---------------------------------------------------------------------- 1357 */ 1358 1359static void 1360Increment_Redo( 1361 TreeCtrl *tree /* Widget info. */ 1362 ) 1363{ 1364 TreeDInfo dInfo = tree->dInfo; 1365 1366 /* Free x */ 1367 if (dInfo->xScrollIncrements != NULL) 1368 ckfree((char *) dInfo->xScrollIncrements); 1369 dInfo->xScrollIncrements = NULL; 1370 dInfo->xScrollIncrementCount = 0; 1371 1372 /* Free y */ 1373 if (dInfo->yScrollIncrements != NULL) 1374 ckfree((char *) dInfo->yScrollIncrements); 1375 dInfo->yScrollIncrements = NULL; 1376 dInfo->yScrollIncrementCount = 0; 1377 1378 if (tree->vertical) { 1379 /* No xScrollIncrement is given. Snap to left edge of a Range */ 1380 if (tree->xScrollIncrement <= 0) 1381 RangesToIncrementsX(tree); 1382 1383 /* No yScrollIncrement is given. Snap to top edge of a TreeItem */ 1384 if (tree->yScrollIncrement <= 0) 1385 RItemsToIncrementsY(tree); 1386 } 1387 else { 1388 /* No xScrollIncrement is given. Snap to left edge of a TreeItem */ 1389 if (tree->xScrollIncrement <= 0) 1390 RItemsToIncrementsX(tree); 1391 1392 /* No yScrollIncrement is given. Snap to top edge of a Range */ 1393 if (tree->yScrollIncrement <= 0) 1394 RangesToIncrementsY(tree); 1395 } 1396} 1397 1398/* 1399 *---------------------------------------------------------------------- 1400 * 1401 * Increment_RedoIfNeeded -- 1402 * 1403 * Recalculate the lists of scroll increments if needed. 1404 * 1405 * Results: 1406 * DInfo.xScrollIncrements and DInfo.xScrollIncrements may be 1407 * updated. 1408 * 1409 * Side effects: 1410 * Memory may be allocated. The list of Ranges will be recalculated 1411 * if needed. 1412 * 1413 *---------------------------------------------------------------------- 1414 */ 1415 1416static void 1417Increment_RedoIfNeeded( 1418 TreeCtrl *tree /* Widget info. */ 1419 ) 1420{ 1421 TreeDInfo dInfo = tree->dInfo; 1422 1423 Range_RedoIfNeeded(tree); 1424 1425 /* Check for x|yScrollIncrement >0 changing to <=0 */ 1426 if (((dInfo->yScrollIncrement > 0) != (tree->yScrollIncrement > 0)) || 1427 ((dInfo->xScrollIncrement > 0) != (tree->xScrollIncrement > 0))) { 1428 dInfo->yScrollIncrement = tree->yScrollIncrement; 1429 dInfo->xScrollIncrement = tree->xScrollIncrement; 1430 dInfo->flags |= DINFO_REDO_INCREMENTS; 1431 } 1432 if (dInfo->flags & DINFO_REDO_INCREMENTS) { 1433 Increment_Redo(tree); 1434 dInfo->flags &= ~DINFO_REDO_INCREMENTS; 1435 } 1436} 1437 1438/* 1439 *---------------------------------------------------------------------- 1440 * 1441 * B_IncrementFind -- 1442 * 1443 * Search a list of increments and return one nearest to the 1444 * given offset. 1445 * 1446 * Results: 1447 * Index of the nearest increment <= the given offset. 1448 * Panic() if no appropriate offset if found. 1449 * 1450 * Side effects: 1451 * None. 1452 * 1453 *---------------------------------------------------------------------- 1454 */ 1455 1456static int 1457B_IncrementFind( 1458 int *increments, /* DInfo.x|yScrollIncrements. */ 1459 int count, /* Length of increments[]. */ 1460 int offset /* Offset to search with. */ 1461 ) 1462{ 1463 int i, l, u, v; 1464 1465 if (offset < 0) 1466 offset = 0; 1467 1468 /* Binary search */ 1469 l = 0; 1470 u = count - 1; 1471 while (l <= u) { 1472 i = (l + u) / 2; 1473 v = increments[i]; 1474 if ((offset >= v) && ((i == count - 1) || (offset < increments[i + 1]))) 1475 return i; 1476 if (offset < v) 1477 u = i - 1; 1478 else 1479 l = i + 1; 1480 } 1481 panic("B_IncrementFind failed (count %d offset %d)", count, offset); 1482 return -1; 1483} 1484 1485/* 1486 *---------------------------------------------------------------------- 1487 * 1488 * B_IncrementFindX -- 1489 * 1490 * Search DInfo.xScrollIncrements and return one nearest to the 1491 * given offset. 1492 * 1493 * Results: 1494 * Index of the nearest increment <= the given offset. 1495 * Panic() if no appropriate offset if found. 1496 * 1497 * Side effects: 1498 * None. 1499 * 1500 *---------------------------------------------------------------------- 1501 */ 1502 1503static int 1504B_IncrementFindX( 1505 TreeCtrl *tree, /* Widget info. */ 1506 int offset /* Offset to search with. */ 1507 ) 1508{ 1509 TreeDInfo dInfo = tree->dInfo; 1510 1511 return B_IncrementFind( 1512 dInfo->xScrollIncrements, 1513 dInfo->xScrollIncrementCount, 1514 offset); 1515} 1516 1517/* 1518 *---------------------------------------------------------------------- 1519 * 1520 * B_IncrementFindY -- 1521 * 1522 * Search DInfo.yScrollIncrements and return one nearest to the 1523 * given offset. 1524 * 1525 * Results: 1526 * Index of the nearest increment <= the given offset. 1527 * Panic() if no appropriate offset if found. 1528 * 1529 * Side effects: 1530 * None. 1531 * 1532 *---------------------------------------------------------------------- 1533 */ 1534 1535static int 1536B_IncrementFindY( 1537 TreeCtrl *tree, /* Widget info. */ 1538 int offset /* Offset to search with. */ 1539 ) 1540{ 1541 TreeDInfo dInfo = tree->dInfo; 1542 1543 return B_IncrementFind( 1544 dInfo->yScrollIncrements, 1545 dInfo->yScrollIncrementCount, 1546 offset); 1547} 1548 1549/* 1550 *-------------------------------------------------------------- 1551 * 1552 * B_XviewCmd -- 1553 * 1554 * This procedure is invoked to process the "xview" option for 1555 * the widget command for a TreeCtrl. See the user documentation 1556 * for details on what it does. 1557 * 1558 * NOTE: This procedure is called when the -xscrollincrement option 1559 * is unspecified. 1560 * 1561 * Results: 1562 * A standard Tcl result. 1563 * 1564 * Side effects: 1565 * See the user documentation. 1566 * 1567 *-------------------------------------------------------------- 1568 */ 1569 1570int 1571B_XviewCmd( 1572 TreeCtrl *tree, /* Widget info. */ 1573 int objc, /* Number of arguments. */ 1574 Tcl_Obj *CONST objv[] /* Argument values. */ 1575 ) 1576{ 1577 Tcl_Interp *interp = tree->interp; 1578 TreeDInfo dInfo = tree->dInfo; 1579 1580 if (objc == 2) { 1581 double fractions[2]; 1582 1583 Tree_GetScrollFractionsX(tree, fractions); 1584 FormatResult(interp, "%g %g", fractions[0], fractions[1]); 1585 } else { 1586 int count, index = 0, indexMax, offset, type; 1587 double fraction; 1588 int visWidth = Tree_ContentWidth(tree); 1589 int totWidth = Tree_TotalWidth(tree); 1590 1591 if (visWidth < 0) 1592 visWidth = 0; 1593 if (totWidth <= visWidth) 1594 return TCL_OK; 1595 1596 if (visWidth > 1) { 1597 /* Find incrementLeft when scrolled to right */ 1598 indexMax = Increment_FindX(tree, totWidth - visWidth); 1599 offset = Increment_ToOffsetX(tree, indexMax); 1600 if (offset < totWidth - visWidth) { 1601 indexMax++; 1602 offset = Increment_ToOffsetX(tree, indexMax); 1603 } 1604 1605 /* Add some fake content to right */ 1606 if (offset + visWidth > totWidth) 1607 totWidth = offset + visWidth; 1608 } else { 1609 indexMax = Increment_FindX(tree, totWidth); 1610 visWidth = 1; 1611 } 1612 1613 type = Tk_GetScrollInfoObj(interp, objc, objv, &fraction, &count); 1614 switch (type) { 1615 case TK_SCROLL_ERROR: 1616 return TCL_ERROR; 1617 case TK_SCROLL_MOVETO: 1618 offset = (int) (fraction * totWidth + 0.5); 1619 index = Increment_FindX(tree, offset); 1620 break; 1621 case TK_SCROLL_PAGES: 1622 offset = Tree_ContentLeft(tree) + tree->xOrigin; 1623 offset += (int) (count * visWidth * 0.9); 1624 index = Increment_FindX(tree, offset); 1625 if ((count > 0) && (index == 1626 Increment_FindX(tree, Tree_ContentLeft(tree) + tree->xOrigin))) 1627 index++; 1628 break; 1629 case TK_SCROLL_UNITS: 1630 index = dInfo->incrementLeft + count; 1631 break; 1632 } 1633 1634 /* Don't scroll too far left */ 1635 if (index < 0) 1636 index = 0; 1637 1638 /* Don't scroll too far right */ 1639 if (index > indexMax) 1640 index = indexMax; 1641 1642 offset = Increment_ToOffsetX(tree, index); 1643 if ((index != dInfo->incrementLeft) || (tree->xOrigin != offset - Tree_ContentLeft(tree))) { 1644 dInfo->incrementLeft = index; 1645 tree->xOrigin = offset - Tree_ContentLeft(tree); 1646 Tree_EventuallyRedraw(tree); 1647 } 1648 } 1649 return TCL_OK; 1650} 1651 1652/* 1653 *-------------------------------------------------------------- 1654 * 1655 * B_YviewCmd -- 1656 * 1657 * This procedure is invoked to process the "yview" option for 1658 * the widget command for a TreeCtrl. See the user documentation 1659 * for details on what it does. 1660 * 1661 * NOTE: This procedure is called when the -yscrollincrement option 1662 * is unspecified. 1663 * 1664 * Results: 1665 * A standard Tcl result. 1666 * 1667 * Side effects: 1668 * See the user documentation. 1669 * 1670 *-------------------------------------------------------------- 1671 */ 1672 1673int 1674B_YviewCmd( 1675 TreeCtrl *tree, /* Widget info. */ 1676 int objc, /* Number of arguments. */ 1677 Tcl_Obj *CONST objv[] /* Argument values. */ 1678 ) 1679{ 1680 Tcl_Interp *interp = tree->interp; 1681 TreeDInfo dInfo = tree->dInfo; 1682 1683 if (objc == 2) { 1684 double fractions[2]; 1685 1686 Tree_GetScrollFractionsY(tree, fractions); 1687 FormatResult(interp, "%g %g", fractions[0], fractions[1]); 1688 } 1689 else { 1690 int count, index = 0, indexMax, offset, type; 1691 double fraction; 1692 int visHeight = Tree_ContentHeight(tree); 1693 int totHeight = Tree_TotalHeight(tree); 1694 1695 if (visHeight < 0) 1696 visHeight = 0; 1697 if (totHeight <= visHeight) 1698 return TCL_OK; 1699 1700 if (visHeight > 1) { 1701 /* Find incrementTop when scrolled to bottom */ 1702 indexMax = Increment_FindY(tree, totHeight - visHeight); 1703 offset = Increment_ToOffsetY(tree, indexMax); 1704 if (offset < totHeight - visHeight) { 1705 indexMax++; 1706 offset = Increment_ToOffsetY(tree, indexMax); 1707 } 1708 1709 /* Add some fake content to bottom */ 1710 if (offset + visHeight > totHeight) 1711 totHeight = offset + visHeight; 1712 } 1713 else { 1714 indexMax = Increment_FindY(tree, totHeight); 1715 visHeight = 1; 1716 } 1717 1718 type = Tk_GetScrollInfoObj(interp, objc, objv, &fraction, &count); 1719 switch (type) { 1720 case TK_SCROLL_ERROR: 1721 return TCL_ERROR; 1722 case TK_SCROLL_MOVETO: 1723 offset = (int) (fraction * totHeight + 0.5); 1724 index = Increment_FindY(tree, offset); 1725 break; 1726 case TK_SCROLL_PAGES: 1727 offset = Tree_ContentTop(tree) + tree->yOrigin; 1728 offset += (int) (count * visHeight * 0.9); 1729 index = Increment_FindY(tree, offset); 1730 if ((count > 0) && (index == 1731 Increment_FindY(tree, Tree_ContentTop(tree) + tree->yOrigin))) 1732 index++; 1733 break; 1734 case TK_SCROLL_UNITS: 1735 index = dInfo->incrementTop + count; 1736 break; 1737 } 1738 1739 /* Don't scroll too far up */ 1740 if (index < 0) 1741 index = 0; 1742 1743 /* Don't scroll too far down */ 1744 if (index > indexMax) 1745 index = indexMax; 1746 1747 offset = Increment_ToOffsetY(tree, index); 1748 if ((index != dInfo->incrementTop) || (tree->yOrigin != offset - Tree_ContentTop(tree))) { 1749 dInfo->incrementTop = index; 1750 tree->yOrigin = offset - Tree_ContentTop(tree); 1751 Tree_EventuallyRedraw(tree); 1752 } 1753 } 1754 return TCL_OK; 1755} 1756 1757/* 1758 *-------------------------------------------------------------- 1759 * 1760 * Tree_ItemUnderPoint -- 1761 * 1762 * Return a TreeItem containing the given coordinates. 1763 * 1764 * Results: 1765 * TreeItem token or NULL if no item contains the point. 1766 * 1767 * Side effects: 1768 * The list of Ranges will be recalculated if needed. 1769 * 1770 *-------------------------------------------------------------- 1771 */ 1772 1773TreeItem 1774Tree_ItemUnderPoint( 1775 TreeCtrl *tree, /* Widget info. */ 1776 int *x_, int *y_, /* In: window coordinates. 1777 * Out: coordinates relative to top-left 1778 * corner of the returned item. */ 1779 int nearest /* TRUE if the item nearest the coordinates 1780 * should be returned. */ 1781 ) 1782{ 1783 Range *range; 1784 RItem *rItem; 1785 int hit; 1786 1787 hit = Tree_HitTest(tree, *x_, *y_); 1788 if (!nearest && ((hit == TREE_AREA_LEFT) || (hit == TREE_AREA_RIGHT))) { 1789 TreeDInfo dInfo = tree->dInfo; 1790 1791 Range_RedoIfNeeded(tree); 1792 range = dInfo->rangeFirst; 1793 1794 /* If range is NULL use dInfo->rangeLock. */ 1795 if (range == NULL) { 1796 if (dInfo->rangeLock == NULL) 1797 return NULL; 1798 range = dInfo->rangeLock; 1799 } 1800 1801 if (*y_ + tree->yOrigin < range->totalHeight) { 1802 int x = *x_; 1803 int y = *y_; 1804 1805 if (hit == TREE_AREA_RIGHT) { 1806 x -= Tree_ContentRight(tree); 1807 } else { 1808 x -= Tree_BorderLeft(tree); 1809 } 1810 1811 /* Window -> canvas */ 1812 y += tree->yOrigin; 1813 1814 rItem = Range_ItemUnderPoint(tree, range, NULL, &y); 1815 *x_ = x; 1816 *y_ = y; 1817 return rItem->item; 1818 } 1819 return NULL; 1820 } 1821 1822 range = Range_UnderPoint(tree, x_, y_, nearest); 1823 if (range == NULL) 1824 return NULL; 1825 rItem = Range_ItemUnderPoint(tree, range, x_, y_); 1826 if (rItem != NULL) 1827 return rItem->item; 1828 return NULL; 1829} 1830 1831/* 1832 *-------------------------------------------------------------- 1833 * 1834 * Tree_AreaBbox -- 1835 * 1836 * Return the bounding box of a visible area. 1837 * 1838 * Results: 1839 * Return value is TRUE if the area is non-empty. 1840 * 1841 * Side effects: 1842 * Column and item layout will be updated if needed. 1843 * 1844 *-------------------------------------------------------------- 1845 */ 1846 1847int 1848Tree_AreaBbox( 1849 TreeCtrl *tree, 1850 int area, 1851 int *x1_, 1852 int *y1_, 1853 int *x2_, 1854 int *y2_ 1855 ) 1856{ 1857 int x1 = 0, y1 = 0, x2 = 0, y2 = 0; 1858 1859 switch (area) { 1860 case TREE_AREA_HEADER: 1861 x1 = Tree_BorderLeft(tree); 1862 y1 = Tree_BorderTop(tree); 1863 x2 = Tree_BorderRight(tree); 1864 y2 = Tree_ContentTop(tree); 1865 break; 1866 case TREE_AREA_CONTENT: 1867 x1 = Tree_ContentLeft(tree); 1868 y1 = Tree_ContentTop(tree); 1869 x2 = Tree_ContentRight(tree); 1870 y2 = Tree_ContentBottom(tree); 1871 break; 1872 case TREE_AREA_LEFT: 1873 x1 = Tree_BorderLeft(tree); 1874 y1 = Tree_ContentTop(tree); 1875 x2 = Tree_ContentLeft(tree); 1876 y2 = Tree_ContentBottom(tree); 1877 /* Don't overlap right-locked columns. */ 1878 if (x2 > Tree_ContentRight(tree)) 1879 x2 = Tree_ContentRight(tree); 1880 break; 1881 case TREE_AREA_RIGHT: 1882 x1 = Tree_ContentRight(tree); 1883 y1 = Tree_ContentTop(tree); 1884 x2 = Tree_BorderRight(tree); 1885 y2 = Tree_ContentBottom(tree); 1886 break; 1887 } 1888 1889 if (x2 <= x1 || y2 <= y1) 1890 return FALSE; 1891 1892 if (x1 < Tree_BorderLeft(tree)) 1893 x1 = Tree_BorderLeft(tree); 1894 if (x2 > Tree_BorderRight(tree)) 1895 x2 = Tree_BorderRight(tree); 1896 1897 if (y1 < Tree_BorderTop(tree)) 1898 y1 = Tree_BorderTop(tree); 1899 if (y2 > Tree_BorderBottom(tree)) 1900 y2 = Tree_BorderBottom(tree); 1901 1902 *x1_ = x1; 1903 *y1_ = y1; 1904 *x2_ = x2; 1905 *y2_ = y2; 1906 return (x2 > x1) && (y2 > y1); 1907} 1908 1909/* 1910 *-------------------------------------------------------------- 1911 * 1912 * Tree_HitTest -- 1913 * 1914 * Determine which are of the window contains the given point. 1915 * 1916 * Results: 1917 * Return value is one of the TREE_AREA_xxx constants. 1918 * 1919 * Side effects: 1920 * Column layout will be updated if needed. 1921 * 1922 *-------------------------------------------------------------- 1923 */ 1924 1925int 1926Tree_HitTest( 1927 TreeCtrl *tree, 1928 int x, 1929 int y 1930 ) 1931{ 1932 if ((x < Tree_BorderLeft(tree)) || (x >= Tree_BorderRight(tree))) 1933 return TREE_AREA_NONE; 1934 if ((y < Tree_BorderTop(tree)) || (y >= Tree_BorderBottom(tree))) 1935 return TREE_AREA_NONE; 1936 1937 if (y < Tree_HeaderBottom(tree)) { 1938 return TREE_AREA_HEADER; 1939 } 1940 /* Right-locked columns are drawn over the left. */ 1941 if (x >= Tree_ContentRight(tree)) { 1942 return TREE_AREA_RIGHT; 1943 } 1944 if (x < Tree_ContentLeft(tree)) { 1945 return TREE_AREA_LEFT; 1946 } 1947 if (Tree_ContentLeft(tree) >= Tree_ContentRight(tree)) { 1948 return TREE_AREA_NONE; 1949 } 1950 return TREE_AREA_CONTENT; 1951} 1952 1953/* 1954 *-------------------------------------------------------------- 1955 * 1956 * Tree_ItemBbox -- 1957 * 1958 * Return the bounding box for an item. 1959 * 1960 * Results: 1961 * Return value is -1 if the item is not ReallyVisible() 1962 * or if there are no visible columns. The coordinates 1963 * are relative to the top-left corner of the canvas. 1964 * 1965 * Side effects: 1966 * Column layout will be updated if needed. 1967 * The list of Ranges will be recalculated if needed. 1968 * 1969 *-------------------------------------------------------------- 1970 */ 1971 1972int 1973Tree_ItemBbox( 1974 TreeCtrl *tree, /* Widget info. */ 1975 TreeItem item, /* Item whose bbox is needed. */ 1976 int lock, 1977 int *x, int *y, /* Returned left and top. */ 1978 int *w, int *h /* Returned width and height. */ 1979 ) 1980{ 1981 Range *range; 1982 RItem *rItem; 1983 1984 if (!TreeItem_ReallyVisible(tree, item)) 1985 return -1; 1986 1987 /* Update columnCountVisXXX if needed */ 1988 (void) Tree_WidthOfColumns(tree); 1989 1990 Range_RedoIfNeeded(tree); 1991 rItem = (RItem *) TreeItem_GetRInfo(tree, item); 1992 1993 switch (lock) { 1994 case COLUMN_LOCK_LEFT: 1995 if (tree->columnCountVisLeft == 0) 1996 return -1; 1997 *x = Tree_BorderLeft(tree) + tree->xOrigin; /* window -> canvas */ 1998 *y = rItem->offset; 1999 *w = Tree_WidthOfLeftColumns(tree); 2000 *h = rItem->size; 2001 return 0; 2002 case COLUMN_LOCK_NONE: 2003 break; 2004 case COLUMN_LOCK_RIGHT: 2005 if (tree->columnCountVisRight == 0) 2006 return -1; 2007 *x = Tree_ContentRight(tree) + tree->xOrigin; /* window -> canvas */ 2008 *y = rItem->offset; 2009 *w = Tree_WidthOfRightColumns(tree); 2010 *h = rItem->size; 2011 return 0; 2012 } 2013 2014 if (tree->columnCountVis < 1) 2015 return -1; 2016 2017 range = rItem->range; 2018 if (tree->vertical) { 2019 (*x) = range->offset; 2020 (*w) = range->totalWidth; 2021 (*y) = rItem->offset; 2022 (*h) = rItem->size; 2023 } 2024 else { 2025 (*x) = rItem->offset; 2026 (*w) = rItem->size; 2027 (*y) = range->offset; 2028 (*h) = range->totalHeight; 2029 } 2030 return 0; 2031} 2032 2033/* 2034 *-------------------------------------------------------------- 2035 * 2036 * Tree_ItemLARB -- 2037 * 2038 * Return an adjacent item above, below, to the left or to the 2039 * right of the given item. 2040 * 2041 * Results: 2042 * An adjacent item or NULL if there is no such item. 2043 * 2044 * Side effects: 2045 * The list of Ranges will be recalculated if needed. 2046 * 2047 *-------------------------------------------------------------- 2048 */ 2049 2050TreeItem 2051Tree_ItemLARB( 2052 TreeCtrl *tree, /* Widget info. */ 2053 TreeItem item, /* Item to use as a reference. */ 2054 int vertical, /* TRUE if items are arranged 2055 * from top-to-bottom in each Range. */ 2056 int prev /* TRUE for above/left, 2057 * FALSE for below/right. */ 2058 ) 2059{ 2060 RItem *rItem, *rItem2; 2061 Range *range; 2062 int i, l, u; 2063 2064 if (!TreeItem_ReallyVisible(tree, item) || (tree->columnCountVis < 1)) 2065 return NULL; 2066 Range_RedoIfNeeded(tree); 2067 rItem = (RItem *) TreeItem_GetRInfo(tree, item); 2068 if (vertical) { 2069 if (prev) { 2070 if (rItem == rItem->range->first) 2071 return NULL; 2072 rItem--; 2073 } 2074 else { 2075 if (rItem == rItem->range->last) 2076 return NULL; 2077 rItem++; 2078 } 2079 return rItem->item; 2080 } 2081 else { 2082 /* Find the previous range */ 2083 range = prev ? rItem->range->prev : rItem->range->next; 2084 if (range == NULL) 2085 return NULL; 2086 2087 /* Find item with same index */ 2088 /* Binary search */ 2089 l = 0; 2090 u = range->last->index; 2091 while (l <= u) { 2092 i = (l + u) / 2; 2093 rItem2 = range->first + i; 2094 if (rItem2->index == rItem->index) 2095 return rItem2->item; 2096 if (rItem->index < rItem2->index) 2097 u = i - 1; 2098 else 2099 l = i + 1; 2100 } 2101 } 2102 return NULL; 2103} 2104 2105TreeItem 2106Tree_ItemLeft( 2107 TreeCtrl *tree, 2108 TreeItem item) 2109{ 2110 return Tree_ItemLARB(tree, item, !tree->vertical, TRUE); 2111} 2112 2113TreeItem 2114Tree_ItemAbove( 2115 TreeCtrl *tree, 2116 TreeItem item) 2117{ 2118 return Tree_ItemLARB(tree, item, tree->vertical, TRUE); 2119} 2120 2121TreeItem 2122Tree_ItemRight( 2123 TreeCtrl *tree, 2124 TreeItem item) 2125{ 2126 return Tree_ItemLARB(tree, item, !tree->vertical, FALSE); 2127} 2128 2129TreeItem 2130Tree_ItemBelow( 2131 TreeCtrl *tree, 2132 TreeItem item) 2133{ 2134 return Tree_ItemLARB(tree, item, tree->vertical, FALSE); 2135} 2136 2137/* 2138 *-------------------------------------------------------------- 2139 * 2140 * Tree_ItemFL -- 2141 * 2142 * Return the first or last item in the same row or column 2143 * as the given item. 2144 * 2145 * Results: 2146 * First/last item or NULL if there is no such item. 2147 * 2148 * Side effects: 2149 * The list of Ranges will be recalculated if needed. 2150 * 2151 *-------------------------------------------------------------- 2152 */ 2153 2154TreeItem 2155Tree_ItemFL( 2156 TreeCtrl *tree, /* Widget info. */ 2157 TreeItem item, /* Item to use as a reference. */ 2158 int vertical, /* TRUE if items are arranged 2159 * from top-to-bottom in each Range. */ 2160 int first /* TRUE for top/left, 2161 * FALSE for bottom/right. */ 2162 ) 2163{ 2164 TreeDInfo dInfo = tree->dInfo; 2165 RItem *rItem, *rItem2; 2166 Range *range; 2167 int i, l, u; 2168 2169 if (!TreeItem_ReallyVisible(tree, item) || (tree->columnCountVis < 1)) { 2170 return NULL; 2171 } 2172 Range_RedoIfNeeded(tree); 2173 rItem = (RItem *) TreeItem_GetRInfo(tree, item); 2174 if (vertical) { 2175 return (first ? rItem->range->first->item : rItem->range->last->item); 2176 } else { 2177 /* Find the first/last range */ 2178 range = first ? dInfo->rangeFirst : dInfo->rangeLast; 2179 2180 /* Check next/prev range until happy */ 2181 while (1) { 2182 if (range == rItem->range) 2183 return item; 2184 2185 /* Find item with same index */ 2186 /* Binary search */ 2187 l = 0; 2188 u = range->last->index; 2189 while (l <= u) { 2190 i = (l + u) / 2; 2191 rItem2 = range->first + i; 2192 if (rItem2->index == rItem->index) 2193 return rItem2->item; 2194 if (rItem->index < rItem2->index) 2195 u = i - 1; 2196 else 2197 l = i + 1; 2198 } 2199 2200 range = first ? range->next : range->prev; 2201 } 2202 } 2203 return NULL; 2204} 2205 2206TreeItem 2207Tree_ItemTop( 2208 TreeCtrl *tree, 2209 TreeItem item) 2210{ 2211 return Tree_ItemFL(tree, item, tree->vertical, TRUE); 2212} 2213 2214TreeItem 2215Tree_ItemBottom( 2216 TreeCtrl *tree, 2217 TreeItem item) 2218{ 2219 return Tree_ItemFL(tree, item, tree->vertical, FALSE); 2220} 2221 2222TreeItem 2223Tree_ItemLeftMost( 2224 TreeCtrl *tree, 2225 TreeItem item) 2226{ 2227 return Tree_ItemFL(tree, item, !tree->vertical, TRUE); 2228} 2229 2230TreeItem 2231Tree_ItemRightMost( 2232 TreeCtrl *tree, 2233 TreeItem item) 2234{ 2235 return Tree_ItemFL(tree, item, !tree->vertical, FALSE); 2236} 2237 2238/* 2239 *-------------------------------------------------------------- 2240 * 2241 * Tree_ItemToRNC -- 2242 * 2243 * Return the row and column for the given item. 2244 * 2245 * Results: 2246 * A standard Tcl result. 2247 * 2248 * Side effects: 2249 * The list of Ranges will be recalculated if needed. 2250 * 2251 *-------------------------------------------------------------- 2252 */ 2253 2254int 2255Tree_ItemToRNC( 2256 TreeCtrl *tree, /* Widget info. */ 2257 TreeItem item, /* Item to get row n' column of. */ 2258 int *row, int *col /* Returned row and column. */ 2259 ) 2260{ 2261 RItem *rItem; 2262 2263 if (!TreeItem_ReallyVisible(tree, item) || (tree->columnCountVis < 1)) 2264 return TCL_ERROR; 2265 Range_RedoIfNeeded(tree); 2266 rItem = (RItem *) TreeItem_GetRInfo(tree, item); 2267 if (tree->vertical) { 2268 (*row) = rItem->index; 2269 (*col) = rItem->range->index; 2270 } 2271 else { 2272 (*row) = rItem->range->index; 2273 (*col) = rItem->index; 2274 } 2275 return TCL_OK; 2276} 2277 2278/* 2279 *-------------------------------------------------------------- 2280 * 2281 * Tree_RNCToItem -- 2282 * 2283 * Return the item at a given row and column. 2284 * 2285 * Results: 2286 * Token for the item. Never returns NULL unless there are no 2287 * Ranges. 2288 * 2289 * Side effects: 2290 * The list of Ranges will be recalculated if needed. 2291 * 2292 *-------------------------------------------------------------- 2293 */ 2294 2295TreeItem 2296Tree_RNCToItem( 2297 TreeCtrl *tree, /* Widget info. */ 2298 int row, int col /* Row and column. These values are 2299 * clipped to valid values. */ 2300 ) 2301{ 2302 TreeDInfo dInfo = tree->dInfo; 2303 Range *range; 2304 RItem *rItem; 2305 int i, l, u; 2306 2307 Range_RedoIfNeeded(tree); 2308 range = dInfo->rangeFirst; 2309 if (range == NULL) 2310 return NULL; 2311 if (row < 0) 2312 row = 0; 2313 if (col < 0) 2314 col = 0; 2315 if (tree->vertical) { 2316 if (col > dInfo->rangeLast->index) 2317 col = dInfo->rangeLast->index; 2318 while (range->index != col) 2319 range = range->next; 2320 rItem = range->last; 2321 if (row > rItem->index) 2322 row = rItem->index; 2323 /* Binary search */ 2324 l = 0; 2325 u = range->last->index; 2326 while (l <= u) { 2327 i = (l + u) / 2; 2328 rItem = range->first + i; 2329 if (rItem->index == row) 2330 break; 2331 if (row < rItem->index) 2332 u = i - 1; 2333 else 2334 l = i + 1; 2335 } 2336 } 2337 else { 2338 if (row > dInfo->rangeLast->index) 2339 row = dInfo->rangeLast->index; 2340 while (range->index != row) 2341 range = range->next; 2342 rItem = range->last; 2343 if (col > rItem->index) 2344 col = rItem->index; 2345 /* Binary search */ 2346 l = 0; 2347 u = range->last->index; 2348 while (l <= u) { 2349 i = (l + u) / 2; 2350 rItem = range->first + i; 2351 if (rItem->index == col) 2352 break; 2353 if (col < rItem->index) 2354 u = i - 1; 2355 else 2356 l = i + 1; 2357 } 2358 } 2359 return rItem->item; 2360} 2361 2362/*=============*/ 2363 2364static void 2365DisplayDelay(TreeCtrl *tree) 2366{ 2367 if (tree->debug.enable && 2368 tree->debug.display && 2369 tree->debug.displayDelay > 0) { 2370#if !defined(WIN32) && !defined(MAC_OSX_TK) 2371 XSync(tree->display, False); 2372#endif 2373 Tcl_Sleep(tree->debug.displayDelay); 2374 } 2375} 2376 2377/* 2378 *-------------------------------------------------------------- 2379 * 2380 * DItem_Alloc -- 2381 * 2382 * Allocate and initialize a new DItem, and store a pointer to it 2383 * in the given item. 2384 * 2385 * Results: 2386 * Pointer to the DItem which may come from an existing pool of 2387 * unused DItems. 2388 * 2389 * Side effects: 2390 * Memory may be allocated. 2391 * 2392 *-------------------------------------------------------------- 2393 */ 2394 2395static DItem * 2396DItem_Alloc( 2397 TreeCtrl *tree, /* Widget info. */ 2398 RItem *rItem /* Range info for the item. */ 2399 ) 2400{ 2401 TreeDInfo dInfo = tree->dInfo; 2402 DItem *dItem; 2403 2404 dItem = (DItem *) TreeItem_GetDInfo(tree, rItem->item); 2405 if (dItem != NULL) 2406 panic("tried to allocate duplicate DItem"); 2407 2408 /* Pop unused DItem from stack */ 2409 if (dInfo->dItemFree != NULL) { 2410 dItem = dInfo->dItemFree; 2411 dInfo->dItemFree = dItem->next; 2412 /* No free DItems, alloc a new one */ 2413 } else { 2414 dItem = (DItem *) ckalloc(sizeof(DItem)); 2415 } 2416 memset(dItem, '\0', sizeof(DItem)); 2417#ifdef TREECTRL_DEBUG 2418 strncpy(dItem->magic, "MAGC", 4); 2419#endif 2420 dItem->item = rItem->item; 2421 dItem->area.flags = DITEM_DIRTY | DITEM_ALL_DIRTY; 2422 dItem->left.flags = DITEM_DIRTY | DITEM_ALL_DIRTY; 2423 dItem->right.flags = DITEM_DIRTY | DITEM_ALL_DIRTY; 2424 TreeItem_SetDInfo(tree, rItem->item, (TreeItemDInfo) dItem); 2425 return dItem; 2426} 2427 2428/* 2429 *-------------------------------------------------------------- 2430 * 2431 * DItem_Unlink -- 2432 * 2433 * Remove a DItem from a linked list of DItems. 2434 * 2435 * Results: 2436 * Pointer to the given list of DItems. 2437 * 2438 * Side effects: 2439 * None. 2440 * 2441 *-------------------------------------------------------------- 2442 */ 2443 2444static DItem * 2445DItem_Unlink( 2446 DItem *head, /* First in linked list. */ 2447 DItem *dItem /* DItem to remove from list. */ 2448 ) 2449{ 2450 DItem *prev; 2451 2452 if (head == dItem) 2453 head = dItem->next; 2454 else { 2455 for (prev = head; 2456 prev->next != dItem; 2457 prev = prev->next) { 2458 /* nothing */ 2459 } 2460 prev->next = dItem->next; 2461 } 2462 dItem->next = NULL; 2463 return head; 2464} 2465 2466/* 2467 *-------------------------------------------------------------- 2468 * 2469 * DItem_Free -- 2470 * 2471 * Add a DItem to the pool of unused DItems. If the DItem belongs 2472 * to a TreeItem the pointer to the DItem is set to NULL in that 2473 * TreeItem. 2474 * 2475 * Results: 2476 * Pointer to the next DItem. 2477 * 2478 * Side effects: 2479 * None. 2480 * 2481 *-------------------------------------------------------------- 2482 */ 2483 2484static DItem * 2485DItem_Free( 2486 TreeCtrl *tree, /* Widget info. */ 2487 DItem *dItem /* DItem to free. */ 2488 ) 2489{ 2490 TreeDInfo dInfo = tree->dInfo; 2491 DItem *next = dItem->next; 2492#ifdef TREECTRL_DEBUG 2493 if (strncmp(dItem->magic, "MAGC", 4) != 0) 2494 panic("DItem_Free: dItem.magic != MAGC"); 2495#endif 2496 if (dItem->item != NULL) { 2497 TreeItem_SetDInfo(tree, dItem->item, (TreeItemDInfo) NULL); 2498 dItem->item = NULL; 2499 } 2500 /* Push unused DItem on the stack */ 2501 dItem->next = dInfo->dItemFree; 2502 dInfo->dItemFree = dItem; 2503 return next; 2504} 2505 2506/* 2507 *-------------------------------------------------------------- 2508 * 2509 * FreeDItems -- 2510 * 2511 * Add a list of DItems to the pool of unused DItems, 2512 * optionally removing the DItems from the DInfo.dItem list. 2513 * 2514 * Results: 2515 * None. 2516 * 2517 * Side effects: 2518 * None. 2519 * 2520 *-------------------------------------------------------------- 2521 */ 2522 2523static void 2524FreeDItems( 2525 TreeCtrl *tree, /* Widget info. */ 2526 DItem *first, /* First DItem to free. */ 2527 DItem *last, /* DItem after the last one to free. */ 2528 int unlink /* TRUE if the DItems should be removed 2529 * from the DInfo.dItem list. */ 2530 ) 2531{ 2532 TreeDInfo dInfo = tree->dInfo; 2533 DItem *prev; 2534 2535 if (unlink) { 2536 if (dInfo->dItem == first) 2537 dInfo->dItem = last; 2538 else { 2539 for (prev = dInfo->dItem; 2540 prev->next != first; 2541 prev = prev->next) { 2542 /* nothing */ 2543 } 2544 prev->next = last; 2545 } 2546 } 2547 while (first != last) 2548 first = DItem_Free(tree, first); 2549} 2550 2551/* 2552 *-------------------------------------------------------------- 2553 * 2554 * Tree_ItemsInArea -- 2555 * 2556 * Return a list of items overlapping the given area. 2557 * 2558 * Results: 2559 * Initializes the given TreeItemList and appends any items 2560 * in the given area. 2561 * 2562 * Side effects: 2563 * The list of Ranges will be recalculated if needed. Memory may 2564 * be allocated. 2565 * 2566 *-------------------------------------------------------------- 2567 */ 2568 2569void 2570Tree_ItemsInArea( 2571 TreeCtrl *tree, /* Widget info. */ 2572 TreeItemList *items, /* Uninitialized list. The caller must free 2573 * it with TreeItemList_Free. */ 2574 int minX, int minY, /* Left, top in canvas coordinates. */ 2575 int maxX, int maxY /* Right, bottom in canvas coordinates. 2576 * Points on the right/bottom edge are not 2577 * included in the area. */ 2578 ) 2579{ 2580 TreeDInfo dInfo = tree->dInfo; 2581 int x, y, rx = 0, ry = 0, ix, iy, dx, dy; 2582 Range *range; 2583 RItem *rItem; 2584 2585 TreeItemList_Init(tree, items, 0); 2586 2587 Range_RedoIfNeeded(tree); 2588 range = dInfo->rangeFirst; 2589 2590 if (tree->vertical) { 2591 /* Find the first range which could be in the area horizontally */ 2592 while (range != NULL) { 2593 if ((range->offset < maxX) && 2594 (range->offset + range->totalWidth >= minX)) { 2595 rx = range->offset; 2596 ry = 0; 2597 break; 2598 } 2599 range = range->next; 2600 } 2601 } 2602 else { 2603 /* Find the first range which could be in the area vertically */ 2604 while (range != NULL) { 2605 if ((range->offset < maxY) && 2606 (range->offset + range->totalHeight >= minY)) { 2607 rx = 0; 2608 ry = range->offset; 2609 break; 2610 } 2611 range = range->next; 2612 } 2613 } 2614 2615 if (range == NULL) 2616 return; 2617 2618 while (range != NULL) { 2619 if ((rx + range->totalWidth > minX) && 2620 (ry + range->totalHeight > minY)) { 2621 if (tree->vertical) { 2622 /* Range coords */ 2623 dx = MAX(minX - rx, 0); 2624 dy = minY; 2625 } 2626 else { 2627 dx = minX; 2628 dy = MAX(minY - ry, 0); 2629 } 2630 ix = dx; 2631 iy = dy; 2632 rItem = Range_ItemUnderPoint(tree, range, &ix, &iy); 2633 2634 /* Canvas coords of top-left of item */ 2635 x = rx + dx - ix; 2636 y = ry + dy - iy; 2637 2638 while (1) { 2639 TreeItemList_Append(items, rItem->item); 2640 if (tree->vertical) { 2641 y += rItem->size; 2642 if (y >= maxY) 2643 break; 2644 } 2645 else { 2646 x += rItem->size; 2647 if (x >= maxX) 2648 break; 2649 } 2650 if (rItem == range->last) 2651 break; 2652 rItem++; 2653 } 2654 } 2655 if (tree->vertical) { 2656 if (rx + range->totalWidth >= maxX) 2657 break; 2658 rx += range->totalWidth; 2659 } 2660 else { 2661 if (ry + range->totalHeight >= maxY) 2662 break; 2663 ry += range->totalHeight; 2664 } 2665 range = range->next; 2666 } 2667} 2668 2669#define DCOLUMN 2670#ifdef DCOLUMN 2671 2672/* 2673 *-------------------------------------------------------------- 2674 * 2675 * GetOnScreenColumnsForItemAux -- 2676 * 2677 * Determine which columns of an item are onscreen. 2678 * 2679 * Results: 2680 * Sets the column list. 2681 * 2682 * Side effects: 2683 * Memory may be allocated. 2684 * 2685 *-------------------------------------------------------------- 2686 */ 2687 2688static void 2689GetOnScreenColumnsForItemAux( 2690 TreeCtrl *tree, /* Widget info. */ 2691 DItem *dItem, /* Display info for an item. */ 2692 DItemArea *area, /* Layout info. */ 2693 int bounds[4], /* TREE_AREA_xxx bounds. */ 2694 int lock, /* Set of columns we care about. */ 2695 TreeColumnList *columns /* Initialized list to append to. */ 2696 ) 2697{ 2698 int minX, maxX, columnIndex = 0, x = 0, i, width; 2699 TreeColumn column = NULL, column2; 2700 2701 minX = MAX(area->x, bounds[0]); 2702 maxX = MIN(area->x + area->width, bounds[2]); 2703 2704 minX -= area->x; 2705 maxX -= area->x; 2706 2707 switch (lock) { 2708 case COLUMN_LOCK_LEFT: 2709 column = tree->columnLockLeft; 2710 break; 2711 case COLUMN_LOCK_NONE: 2712 column = tree->columnLockNone; 2713 break; 2714 case COLUMN_LOCK_RIGHT: 2715 column = tree->columnLockRight; 2716 break; 2717 } 2718 2719 for (columnIndex = TreeColumn_Index(column); 2720 columnIndex < tree->columnCount; columnIndex++) { 2721 if (TreeColumn_Lock(column) != lock) 2722 break; 2723 column2 = TreeColumn_Next(column); 2724 width = TreeColumn_GetDInfo(column)->width; 2725 if (width == 0) /* also handles hidden columns */ 2726 goto next; 2727 if (dItem->spans != NULL) { 2728 /* FIXME: not possible since I skip over the entire span. */ 2729 if (dItem->spans[columnIndex] != columnIndex) 2730 goto next; 2731 /* Calculate the width of the span. */ 2732 for (i = columnIndex + 1; i < tree->columnCount && 2733 dItem->spans[i] == columnIndex; i++) { 2734 width += TreeColumn_GetDInfo(column2)->width; 2735 column2 = TreeColumn_Next(column2); 2736 } 2737 columnIndex = i - 1; 2738 } 2739 if (x < maxX && x + width > minX) { 2740 TreeColumnList_Append(columns, column); 2741 } 2742next: 2743 x += width; 2744 if (x >= maxX) 2745 break; 2746 column = column2; 2747 } 2748} 2749 2750/* 2751 *-------------------------------------------------------------- 2752 * 2753 * GetOnScreenColumnsForItem -- 2754 * 2755 * Determine which columns of an item are onscreen. 2756 * 2757 * Results: 2758 * Sets the column list. 2759 * 2760 * Side effects: 2761 * Memory may be allocated. 2762 * 2763 *-------------------------------------------------------------- 2764 */ 2765 2766static int 2767GetOnScreenColumnsForItem( 2768 TreeCtrl *tree, /* Widget info. */ 2769 DItem *dItem, /* Display info for an item. */ 2770 TreeColumnList *columns /* Initialized list to append to. */ 2771 ) 2772{ 2773 TreeDInfo dInfo = tree->dInfo; 2774 2775 if (!dInfo->emptyL) { 2776 GetOnScreenColumnsForItemAux(tree, dItem, &dItem->left, 2777 dInfo->boundsL, COLUMN_LOCK_LEFT, columns); 2778 } 2779 if (!dInfo->empty && dInfo->rangeFirst != NULL) { 2780 GetOnScreenColumnsForItemAux(tree, dItem, &dItem->area, 2781 dInfo->bounds, COLUMN_LOCK_NONE, columns); 2782 } 2783 if (!dInfo->emptyR) { 2784 GetOnScreenColumnsForItemAux(tree, dItem, &dItem->right, 2785 dInfo->boundsR, COLUMN_LOCK_RIGHT, columns); 2786 } 2787 return TreeColumnList_Count(columns); 2788} 2789 2790/* 2791 *-------------------------------------------------------------- 2792 * 2793 * TrackOnScreenColumnsForItem -- 2794 * 2795 * Compares the list of onscreen columns for an item to the 2796 * list of previously-onscreen columns for the item. 2797 * 2798 * Results: 2799 * Hides window elements for columns that are no longer 2800 * onscreen. 2801 * 2802 * Side effects: 2803 * Memory may be allocated. 2804 * 2805 *-------------------------------------------------------------- 2806 */ 2807 2808static void 2809TrackOnScreenColumnsForItem( 2810 TreeCtrl *tree, /* Widget info. */ 2811 TreeItem item, /* Item token. */ 2812 Tcl_HashEntry *hPtr /* DInfo.itemVisHash entry. */ 2813 ) 2814{ 2815 TreeColumnList columns; 2816 TreeColumn column, *value; 2817 DItem *dItem; 2818 int i, j, count = 0, n = 0; 2819 Tcl_DString dString; 2820 2821 TreeColumnList_Init(tree, &columns, 0); 2822 Tcl_DStringInit(&dString); 2823 2824 /* dItem is NULL if the item just went offscreen. */ 2825 dItem = (DItem *) TreeItem_GetDInfo(tree, item); 2826 if (dItem != NULL) 2827 count = GetOnScreenColumnsForItem(tree, dItem, &columns); 2828 2829 if (tree->debug.enable && tree->debug.span) 2830 DStringAppendf(&dString, "onscreen columns for item %d:", 2831 TreeItem_GetID(tree, item)); 2832 2833 /* value is NULL if the item just came onscreen. */ 2834 value = (TreeColumn *) Tcl_GetHashValue(hPtr); 2835 if (value == NULL) { 2836 value = (TreeColumn *) ckalloc(sizeof(TreeColumn) * (count + 1)); 2837 value[0] = NULL; 2838 } 2839 2840 /* Track newly-visible columns */ 2841 for (i = 0; i < count; i++) { 2842 column = TreeColumnList_Nth(&columns, i); 2843 for (j = 0; value[j] != NULL; j++) { 2844 if (column == value[j]) 2845 break; 2846 } 2847 if (value[j] == NULL) { 2848 if (tree->debug.enable && tree->debug.span) 2849 DStringAppendf(&dString, " +%d", TreeColumn_GetID(column)); 2850 n++; 2851 } 2852 } 2853 2854 /* Track newly-hidden columns */ 2855 for (j = 0; value[j] != NULL; j++) { 2856 column = value[j]; 2857 for (i = 0; i < count; i++) { 2858 if (TreeColumnList_Nth(&columns, i) == column) 2859 break; 2860 } 2861 if (i == count) { 2862 TreeItemColumn itemColumn = TreeItem_FindColumn(tree, item, 2863 TreeColumn_Index(column)); 2864 if (itemColumn != NULL) { 2865 TreeStyle style = TreeItemColumn_GetStyle(tree, itemColumn); 2866 if (style != NULL) 2867 TreeStyle_OnScreen(tree, style, FALSE); 2868 } 2869 if (tree->debug.enable && tree->debug.span) 2870 DStringAppendf(&dString, " -%d", TreeColumn_GetID(column)); 2871 n++; 2872 } 2873 } 2874 2875 if (n && tree->debug.enable && tree->debug.span) 2876 dbwin("%s\n", Tcl_DStringValue(&dString)); 2877 2878 /* Set the list of onscreen columns unless it is the same or the item 2879 * is hidden. */ 2880 if (n > 0 && dItem != NULL) { 2881 value = (TreeColumn *) ckrealloc((char *) value, 2882 sizeof(TreeColumn) * (count + 1)); 2883 memcpy(value, (TreeColumn *) columns.pointers, 2884 sizeof(TreeColumn) * count); 2885 value[count] = NULL; 2886 Tcl_SetHashValue(hPtr, (ClientData) value); 2887 } 2888 2889 Tcl_DStringFree(&dString); 2890 TreeColumnList_Free(&columns); 2891} 2892 2893#endif /* DCOLUMN */ 2894 2895/* 2896 *-------------------------------------------------------------- 2897 * 2898 * UpdateDInfoForRange -- 2899 * 2900 * Allocates or updates a DItem for every on-screen item in a Range. 2901 * If an item already has a DItem (because the item was previously 2902 * displayed), then the DItem may be marked dirty if there were 2903 * changes to the item's on-screen size or position. 2904 * 2905 * Results: 2906 * The return value is the possibly-updated dItemHead. 2907 * 2908 * Side effects: 2909 * Memory may be allocated. 2910 * 2911 *-------------------------------------------------------------- 2912 */ 2913 2914static DItem * 2915UpdateDInfoForRange( 2916 TreeCtrl *tree, /* Widget info. */ 2917 DItem *dItemHead, /* Linked list of used DItems. */ 2918 Range *range, /* Range to update DItems for. */ 2919 RItem *rItem, /* First item in the Range we care about. */ 2920 int x, int y /* Left & top window coordinates of rItem. */ 2921 ) 2922{ 2923 TreeDInfo dInfo = tree->dInfo; 2924 DItem *dItem; 2925 DItemArea *area; 2926 TreeItem item; 2927 int maxX, maxY; 2928 int index, indexVis; 2929 int bgImgWidth, bgImgHeight; 2930 2931 if (tree->backgroundImage != NULL) 2932 Tk_SizeOfImage(tree->backgroundImage, &bgImgWidth, &bgImgHeight); 2933 2934 maxX = Tree_ContentRight(tree); 2935 maxY = Tree_ContentBottom(tree); 2936 2937 /* Top-to-bottom */ 2938 if (tree->vertical) { 2939 while (1) { 2940 item = rItem->item; 2941 2942 /* Update item/style layout. This can be needed when using fixed 2943 * column widths. */ 2944 (void) TreeItem_Height(tree, item); 2945 2946 TreeItem_ToIndex(tree, item, &index, &indexVis); 2947 switch (tree->backgroundMode) { 2948#ifdef DEPRECATED 2949 case BG_MODE_INDEX: 2950#endif 2951 case BG_MODE_ORDER: break; 2952#ifdef DEPRECATED 2953 case BG_MODE_VISINDEX: 2954#endif 2955 case BG_MODE_ORDERVIS: index = indexVis; break; 2956 case BG_MODE_COLUMN: index = range->index; break; 2957 case BG_MODE_ROW: index = rItem->index; break; 2958 } 2959 2960 dItem = (DItem *) TreeItem_GetDInfo(tree, item); 2961 2962 /* Re-use a previously allocated DItem */ 2963 if (dItem != NULL) { 2964 dItemHead = DItem_Unlink(dItemHead, dItem); 2965 area = &dItem->area; 2966 2967 /* This item is already marked for total redraw */ 2968 if (area->flags & DITEM_ALL_DIRTY) 2969 ; /* nothing */ 2970 2971 /* All display info is marked as invalid */ 2972 else if (dInfo->flags & DINFO_INVALIDATE) 2973 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 2974 2975 /* The range may have changed size */ 2976 else if ((area->width != range->totalWidth) || 2977 (dItem->height != rItem->size)) 2978 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 2979 2980 /* Items may have alternating background colors. */ 2981 else if ((tree->columnBgCnt > 1) && 2982 ((index % tree->columnBgCnt) != 2983 (dItem->index % tree->columnBgCnt))) 2984 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 2985 2986 /* We don't copy items horizontally to their new position, 2987 * except for horizontal scrolling which moves the whole 2988 * range */ 2989 else if (x != dItem->oldX + (dInfo->xOrigin - tree->xOrigin)) 2990 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 2991 2992 /* If we are displaying dotted lines and the item has moved 2993 * from odd-top to non-odd-top or vice versa, must redraw 2994 * the lines for this item. */ 2995 else if (tree->showLines && 2996 (tree->lineStyle == LINE_STYLE_DOT) && 2997 tree->columnTreeVis && 2998 (TreeColumn_Lock(tree->columnTree) == COLUMN_LOCK_NONE) && 2999 ((DW2Cy(dItem->oldY) & 1) != (W2Cy(y) & 1))) 3000 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 3001 3002 /* We can't copy the item to its new position unless it 3003 * has the same part of the background image behind it */ 3004 else if ((tree->backgroundImage != NULL) && 3005 (((dItem->oldY + dInfo->yOrigin) % bgImgHeight) != 3006 ((y + tree->yOrigin) % bgImgHeight))) 3007 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 3008 } 3009 3010 /* Make a new DItem */ 3011 else { 3012 dItem = DItem_Alloc(tree, rItem); 3013 area = &dItem->area; 3014 } 3015 3016 area->x = x; 3017 dItem->y = y; 3018 area->width = Range_TotalWidth(tree, range); 3019 dItem->height = rItem->size; 3020 dItem->range = range; 3021 dItem->index = index; 3022 3023 dItem->spans = TreeItem_GetSpans(tree, dItem->item); 3024 3025 /* Keep track of the maximum item size */ 3026 if (area->width > dInfo->itemWidth) 3027 dInfo->itemWidth = area->width; 3028 if (dItem->height > dInfo->itemHeight) 3029 dInfo->itemHeight = dItem->height; 3030 3031 /* Linked list of DItems */ 3032 if (dInfo->dItem == NULL) 3033 dInfo->dItem = dItem; 3034 else 3035 dInfo->dItemLast->next = dItem; 3036 dInfo->dItemLast = dItem; 3037 3038 if (rItem == range->last) 3039 break; 3040 3041 /* Advance to next TreeItem */ 3042 rItem++; 3043 3044 /* Stop when out of bounds */ 3045 y += dItem->height; 3046 if (y >= maxY) 3047 break; 3048 } 3049 } 3050 3051 /* Left-to-right */ 3052 else { 3053 while (1) { 3054 item = rItem->item; 3055 3056 /* Update item/style layout. This can be needed when using fixed 3057 * column widths. */ 3058 (void) TreeItem_Height(tree, item); 3059 3060 TreeItem_ToIndex(tree, item, &index, &indexVis); 3061 switch (tree->backgroundMode) { 3062#ifdef DEPRECATED 3063 case BG_MODE_INDEX: 3064#endif 3065 case BG_MODE_ORDER: break; 3066#ifdef DEPRECATED 3067 case BG_MODE_VISINDEX: 3068#endif 3069 case BG_MODE_ORDERVIS: index = indexVis; break; 3070 case BG_MODE_COLUMN: index = rItem->index; break; 3071 case BG_MODE_ROW: index = range->index; break; 3072 } 3073 3074 dItem = (DItem *) TreeItem_GetDInfo(tree, item); 3075 3076 /* Re-use a previously allocated DItem */ 3077 if (dItem != NULL) { 3078 dItemHead = DItem_Unlink(dItemHead, dItem); 3079 area = &dItem->area; 3080 3081 /* This item is already marked for total redraw */ 3082 if (area->flags & DITEM_ALL_DIRTY) 3083 ; /* nothing */ 3084 3085 /* All display info is marked as invalid */ 3086 else if (dInfo->flags & DINFO_INVALIDATE) 3087 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 3088 3089 /* The range may have changed size */ 3090 else if ((area->width != rItem->size) || 3091 (dItem->height != range->totalHeight)) 3092 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 3093 3094 /* Items may have alternating background colors. */ 3095 else if ((tree->columnBgCnt > 1) && 3096 ((index % tree->columnBgCnt) != 3097 (dItem->index % tree->columnBgCnt))) 3098 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 3099 3100 /* We don't copy items vertically to their new position, 3101 * except for vertical scrolling which moves the whole range */ 3102 else if (y != dItem->oldY + (dInfo->yOrigin - tree->yOrigin)) 3103 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 3104 3105 /* If we are displaying dotted lines and the item has moved 3106 * from odd-top to non-odd-top or vice versa, must redraw 3107 * the lines for this item. */ 3108 else if (tree->showLines && 3109 (tree->lineStyle == LINE_STYLE_DOT) && 3110 tree->columnTreeVis && 3111 ((DW2Cy(dItem->oldY) & 1) != (W2Cy(y) & 1))) 3112 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 3113 3114 /* We can't copy the item to its new position unless it 3115 * has the same part of the background image behind it */ 3116 else if ((tree->backgroundImage != NULL) && 3117 (((dItem->oldX + dInfo->xOrigin) % bgImgWidth) != 3118 ((x + tree->xOrigin) % bgImgWidth))) 3119 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 3120 } 3121 3122 /* Make a new DItem */ 3123 else { 3124 dItem = DItem_Alloc(tree, rItem); 3125 area = &dItem->area; 3126 } 3127 3128 area->x = x; 3129 dItem->y = y; 3130 area->width = rItem->size; 3131 dItem->height = Range_TotalHeight(tree, range); 3132 dItem->range = range; 3133 dItem->index = index; 3134 3135 dItem->spans = TreeItem_GetSpans(tree, dItem->item); 3136 3137 /* Keep track of the maximum item size */ 3138 if (area->width > dInfo->itemWidth) 3139 dInfo->itemWidth = area->width; 3140 if (dItem->height > dInfo->itemHeight) 3141 dInfo->itemHeight = dItem->height; 3142 3143 /* Linked list of DItems */ 3144 if (dInfo->dItem == NULL) 3145 dInfo->dItem = dItem; 3146 else 3147 dInfo->dItemLast->next = dItem; 3148 dInfo->dItemLast = dItem; 3149 3150 if (rItem == range->last) 3151 break; 3152 3153 /* Advance to next TreeItem */ 3154 rItem++; 3155 3156 /* Stop when out of bounds */ 3157 x += area->width; 3158 if (x >= maxX) 3159 break; 3160 } 3161 } 3162 3163 return dItemHead; 3164} 3165 3166/* 3167 *-------------------------------------------------------------- 3168 * 3169 * Tree_UpdateDInfo -- 3170 * 3171 * At the finish of this procedure every on-screen item will have 3172 * a DItem associated with it and no off-screen item will have 3173 * a DItem. 3174 * 3175 * Results: 3176 * None. 3177 * 3178 * Side effects: 3179 * Memory may be allocated. 3180 * 3181 *-------------------------------------------------------------- 3182 */ 3183 3184static void 3185Tree_UpdateDInfo( 3186 TreeCtrl *tree /* Widget info. */ 3187 ) 3188{ 3189 TreeDInfo dInfo = tree->dInfo; 3190 DItem *dItemHead = dInfo->dItem; 3191 int x, y, rx = 0, ry = 0, ix, iy, dx, dy; 3192 int minX, minY, maxX, maxY; 3193 Range *range; 3194 RItem *rItem; 3195 DItem *dItem; 3196 3197 if (tree->debug.enable && tree->debug.display) 3198 dbwin("Tree_UpdateDInfo %s\n", Tk_PathName(tree->tkwin)); 3199 3200 dInfo->dItem = dInfo->dItemLast = NULL; 3201 dInfo->rangeFirstD = dInfo->rangeLastD = NULL; 3202 dInfo->itemWidth = dInfo->itemHeight = 0; 3203 3204 dInfo->empty = !Tree_AreaBbox(tree, TREE_AREA_CONTENT, 3205 &dInfo->bounds[0], &dInfo->bounds[1], 3206 &dInfo->bounds[2], &dInfo->bounds[3]); 3207 dInfo->emptyL = !Tree_AreaBbox(tree, TREE_AREA_LEFT, 3208 &dInfo->boundsL[0], &dInfo->boundsL[1], 3209 &dInfo->boundsL[2], &dInfo->boundsL[3]); 3210 dInfo->emptyR = !Tree_AreaBbox(tree, TREE_AREA_RIGHT, 3211 &dInfo->boundsR[0], &dInfo->boundsR[1], 3212 &dInfo->boundsR[2], &dInfo->boundsR[3]); 3213 3214 if (dInfo->empty) 3215 goto done; 3216 3217 minX = dInfo->bounds[0]; 3218 minY = dInfo->bounds[1]; 3219 maxX = dInfo->bounds[2]; 3220 maxY = dInfo->bounds[3]; 3221 3222 range = dInfo->rangeFirst; 3223 if (tree->vertical) { 3224 /* Find the first range which could be onscreen horizontally. 3225 * It may not be onscreen if it has less height than other ranges. */ 3226 while (range != NULL) { 3227 if ((range->offset < maxX + tree->xOrigin) && 3228 (range->offset + range->totalWidth >= minX + tree->xOrigin)) { 3229 rx = range->offset; 3230 ry = 0; 3231 break; 3232 } 3233 range = range->next; 3234 } 3235 } 3236 else { 3237 /* Find the first range which could be onscreen vertically. 3238 * It may not be onscreen if it has less width than other ranges. */ 3239 while (range != NULL) { 3240 if ((range->offset < maxY + tree->yOrigin) && 3241 (range->offset + range->totalHeight >= minY + tree->yOrigin)) { 3242 rx = 0; 3243 ry = range->offset; 3244 break; 3245 } 3246 range = range->next; 3247 } 3248 } 3249 3250 while (range != NULL) { 3251 if ((rx + range->totalWidth > minX + tree->xOrigin) && 3252 (ry + range->totalHeight > minY + tree->yOrigin)) { 3253 if (tree->vertical) { 3254 /* Range coords */ 3255 dx = MAX(minX + tree->xOrigin - rx, 0); 3256 dy = minY + tree->yOrigin; 3257 } 3258 else { 3259 dx = minX + tree->xOrigin; 3260 dy = MAX(minY + tree->yOrigin - ry, 0); 3261 } 3262 ix = dx; 3263 iy = dy; 3264 rItem = Range_ItemUnderPoint(tree, range, &ix, &iy); 3265 3266 /* Window coords of top-left of item */ 3267 x = (rx - tree->xOrigin) + dx - ix; 3268 y = (ry - tree->yOrigin) + dy - iy; 3269 dItemHead = UpdateDInfoForRange(tree, dItemHead, range, rItem, x, y); 3270 } 3271 3272 /* Track this range even if it has no DItems, so whitespace is 3273 * erased */ 3274 if (dInfo->rangeFirstD == NULL) 3275 dInfo->rangeFirstD = range; 3276 dInfo->rangeLastD = range; 3277 3278 if (tree->vertical) { 3279 rx += range->totalWidth; 3280 if (rx >= maxX + tree->xOrigin) 3281 break; 3282 } 3283 else { 3284 ry += range->totalHeight; 3285 if (ry >= maxY + tree->yOrigin) 3286 break; 3287 } 3288 range = range->next; 3289 } 3290 3291 if (dInfo->dItemLast != NULL) 3292 dInfo->dItemLast->next = NULL; 3293done: 3294 3295 if (dInfo->dItem != NULL) 3296 goto skipLock; 3297 if (!tree->itemVisCount) 3298 goto skipLock; 3299 if (dInfo->emptyL && dInfo->emptyR) 3300 goto skipLock; 3301 range = dInfo->rangeFirst; 3302 if ((range != NULL) && !range->totalHeight) 3303 goto skipLock; 3304 3305 { 3306 int y = Tree_ContentTop(tree) + tree->yOrigin; /* Window -> Canvas */ 3307 int index, indexVis; 3308 3309 /* If no non-locked columns are displayed, we have no Range and 3310 * must use dInfo->rangeLock. */ 3311 if (range == NULL) { 3312 range = dInfo->rangeLock; 3313 } 3314 3315 /* Find the first item on-screen vertically. */ 3316 rItem = Range_ItemUnderPoint(tree, range, NULL, &y); 3317 3318 y = rItem->offset; /* Top of the item */ 3319 y -= tree->yOrigin; /* Canvas -> Window */ 3320 3321 while (1) { 3322 DItem *dItem = (DItem *) TreeItem_GetDInfo(tree, rItem->item); 3323 3324 /* Re-use a previously allocated DItem */ 3325 if (dItem != NULL) { 3326 dItemHead = DItem_Unlink(dItemHead, dItem); 3327 } else { 3328 dItem = DItem_Alloc(tree, rItem); 3329 } 3330 3331 TreeItem_ToIndex(tree, rItem->item, &index, &indexVis); 3332 switch (tree->backgroundMode) { 3333#ifdef DEPRECATED 3334 case BG_MODE_INDEX: 3335#endif 3336 case BG_MODE_ORDER: break; 3337#ifdef DEPRECATED 3338 case BG_MODE_VISINDEX: 3339#endif 3340 case BG_MODE_ORDERVIS: index = indexVis; break; 3341 case BG_MODE_COLUMN: index = range->index; break; 3342 case BG_MODE_ROW: index = rItem->index; break; 3343 } 3344 3345 dItem->y = y; 3346 dItem->height = rItem->size; 3347 dItem->range = range; 3348 dItem->index = index; 3349 3350 dItem->spans = TreeItem_GetSpans(tree, dItem->item); 3351 3352 /* Keep track of the maximum item size */ 3353 if (dItem->height > dInfo->itemHeight) 3354 dInfo->itemHeight = dItem->height; 3355 3356 /* Linked list of DItems */ 3357 if (dInfo->dItem == NULL) 3358 dInfo->dItem = dItem; 3359 else 3360 dInfo->dItemLast->next = dItem; 3361 dInfo->dItemLast = dItem; 3362 3363 if (rItem == range->last) 3364 break; 3365 y += rItem->size; 3366 if (y >= Tree_ContentBottom(tree)) 3367 break; 3368 rItem++; 3369 } 3370 } 3371skipLock: 3372 if (!dInfo->emptyL || !dInfo->emptyR) { 3373 int bgImgWidth, bgImgHeight; 3374 DItemArea *area; 3375 3376 if (!dInfo->emptyL) { 3377 /* Keep track of the maximum item size */ 3378 if (dInfo->widthOfColumnsLeft > dInfo->itemWidth) 3379 dInfo->itemWidth = dInfo->widthOfColumnsLeft; 3380 } 3381 if (!dInfo->emptyR) { 3382 /* Keep track of the maximum item size */ 3383 if (dInfo->widthOfColumnsRight > dInfo->itemWidth) 3384 dInfo->itemWidth = dInfo->widthOfColumnsRight; 3385 } 3386 3387 if (tree->backgroundImage != NULL) 3388 Tk_SizeOfImage(tree->backgroundImage, &bgImgWidth, &bgImgHeight); 3389 3390 for (dItem = dInfo->dItem; dItem != NULL; dItem = dItem->next) { 3391 3392 if (!dInfo->emptyL) { 3393 3394 area = &dItem->left; 3395 area->x = Tree_BorderLeft(tree); 3396 area->width = dInfo->widthOfColumnsLeft; 3397 3398 /* This item is already marked for total redraw */ 3399 if (area->flags & DITEM_ALL_DIRTY) { 3400 ; /* nothing */ 3401 3402 /* All display info is marked as invalid */ 3403 } else if (dInfo->flags & DINFO_INVALIDATE) { 3404 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 3405 3406 /* Items may have alternating background colors. */ 3407 } else if ((tree->columnBgCnt > 1) && 3408 ((dItem->oldIndex % tree->columnBgCnt) != 3409 (dItem->index % tree->columnBgCnt))) { 3410 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 3411 3412 /* If we are displaying dotted lines and the item has moved 3413 * from odd-top to non-odd-top or vice versa, must redraw 3414 * the lines for this item. */ 3415 } else if (tree->showLines && 3416 (tree->lineStyle == LINE_STYLE_DOT) && 3417 tree->columnTreeVis && 3418 (TreeColumn_Lock(tree->columnTree) == COLUMN_LOCK_LEFT) && 3419 ((DW2Cy(dItem->oldY) & 1) != (W2Cy(dItem->y) & 1))) { 3420 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 3421 3422 /* We can't copy the item to its new position unless it 3423 * has the same part of the background image behind it */ 3424 } else if ((tree->backgroundImage != NULL) && 3425 ((dInfo->xOrigin % bgImgWidth) != 3426 (tree->xOrigin % bgImgWidth))) { 3427 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 3428 } 3429 } 3430 3431 if (!dInfo->emptyR) { 3432 3433 area = &dItem->right; 3434 area->x = Tree_ContentRight(tree); 3435 area->width = dInfo->widthOfColumnsRight; 3436 3437 /* This item is already marked for total redraw */ 3438 if (area->flags & DITEM_ALL_DIRTY) { 3439 ; /* nothing */ 3440 3441 /* All display info is marked as invalid */ 3442 } else if (dInfo->flags & DINFO_INVALIDATE) { 3443 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 3444 3445 /* Items may have alternating background colors. */ 3446 } else if ((tree->columnBgCnt > 1) && 3447 ((dItem->oldIndex % tree->columnBgCnt) != 3448 (dItem->index % tree->columnBgCnt))) { 3449 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 3450 3451 /* If we are displaying dotted lines and the item has moved 3452 * from odd-top to non-odd-top or vice versa, must redraw 3453 * the lines for this item. */ 3454 } else if (tree->showLines && 3455 (tree->lineStyle == LINE_STYLE_DOT) && 3456 tree->columnTreeVis && 3457 (TreeColumn_Lock(tree->columnTree) == COLUMN_LOCK_RIGHT) && 3458 ((DW2Cy(dItem->oldY) & 1) != (W2Cy(dItem->y) & 1))) { 3459 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 3460 3461 /* We can't copy the item to its new position unless it 3462 * has the same part of the background image behind it */ 3463 } else if ((tree->backgroundImage != NULL) && 3464 ((dInfo->xOrigin % bgImgWidth) != 3465 (tree->xOrigin % bgImgWidth))) { 3466 area->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 3467 } 3468 } 3469 } 3470 } 3471 3472 while (dItemHead != NULL) 3473 dItemHead = DItem_Free(tree, dItemHead); 3474 3475 dInfo->flags &= ~DINFO_INVALIDATE; 3476} 3477 3478/* 3479 *-------------------------------------------------------------- 3480 * 3481 * InvalidateWhitespace -- 3482 * 3483 * Subtract a rectangular area from the current whitespace 3484 * region. 3485 * 3486 * Results: 3487 * None. 3488 * 3489 * Side effects: 3490 * None. 3491 * 3492 *-------------------------------------------------------------- 3493 */ 3494 3495static void 3496InvalidateWhitespace( 3497 TreeCtrl *tree, /* Widget info. */ 3498 int x1, int y1, /* Window coords to invalidate. */ 3499 int x2, int y2) /* Window coords to invalidate. */ 3500{ 3501 TreeDInfo dInfo = tree->dInfo; 3502 3503 if ((x1 < x2 && y1 < y2) && TkRectInRegion(dInfo->wsRgn, x1, y1, 3504 x2 - x1, y2 - y1)) { 3505 XRectangle rect; 3506 TkRegion rgn = Tree_GetRegion(tree); 3507 3508 rect.x = x1; 3509 rect.y = y1; 3510 rect.width = x2 - x1; 3511 rect.height = y2 - y1; 3512 TkUnionRectWithRegion(&rect, rgn, rgn); 3513 TkSubtractRegion(dInfo->wsRgn, rgn, dInfo->wsRgn); 3514 Tree_FreeRegion(tree, rgn); 3515 } 3516} 3517 3518/* 3519 *-------------------------------------------------------------- 3520 * 3521 * InvalidateDItemX -- 3522 * 3523 * Mark a horizontal span of a DItem as dirty (needing to be 3524 * redrawn). The caller must set the DITEM_DIRTY flag afterwards. 3525 * 3526 * Results: 3527 * None. 3528 * 3529 * Side effects: 3530 * None. 3531 * 3532 *-------------------------------------------------------------- 3533 */ 3534 3535static void 3536InvalidateDItemX( 3537 DItem *dItem, /* Item to mark dirty. */ 3538 DItemArea *area, 3539 int itemX, /* x-coordinate of item. */ 3540 int dirtyX, /* Left edge of area to mark as dirty. */ 3541 int dirtyWidth /* Width of area to mark as dirty. */ 3542 ) 3543{ 3544 int x1, x2; 3545 3546 if (dirtyX <= itemX) 3547 area->dirty[LEFT] = 0; 3548 else { 3549 x1 = dirtyX - itemX; 3550 if (!(area->flags & DITEM_DIRTY) || (x1 < area->dirty[LEFT])) 3551 area->dirty[LEFT] = x1; 3552 } 3553 3554 if (dirtyX + dirtyWidth >= itemX + area->width) 3555 area->dirty[RIGHT] = area->width; 3556 else { 3557 x2 = dirtyX + dirtyWidth - itemX; 3558 if (!(area->flags & DITEM_DIRTY) || (x2 > area->dirty[RIGHT])) 3559 area->dirty[RIGHT] = x2; 3560 } 3561} 3562 3563/* 3564 *-------------------------------------------------------------- 3565 * 3566 * InvalidateDItemY -- 3567 * 3568 * Mark a vertical span of a DItem as dirty (needing to be 3569 * redrawn). The caller must set the DITEM_DIRTY flag afterwards. 3570 * 3571 * Results: 3572 * None. 3573 * 3574 * Side effects: 3575 * None. 3576 * 3577 *-------------------------------------------------------------- 3578 */ 3579 3580static void 3581InvalidateDItemY( 3582 DItem *dItem, /* Item to mark dirty. */ 3583 DItemArea *area, 3584 int itemY, /* y-coordinate of item. */ 3585 int dirtyY, /* Top edge of area to mark as dirty. */ 3586 int dirtyHeight /* Height of area to mark as dirty. */ 3587 ) 3588{ 3589 int y1, y2; 3590 3591 if (dirtyY <= itemY) 3592 area->dirty[TOP] = 0; 3593 else { 3594 y1 = dirtyY - itemY; 3595 if (!(area->flags & DITEM_DIRTY) || (y1 < area->dirty[TOP])) 3596 area->dirty[TOP] = y1; 3597 } 3598 3599 if (dirtyY + dirtyHeight >= itemY + dItem->height) 3600 area->dirty[BOTTOM] = dItem->height; 3601 else { 3602 y2 = dirtyY + dirtyHeight - itemY; 3603 if (!(area->flags & DITEM_DIRTY) || (y2 > area->dirty[BOTTOM])) 3604 area->dirty[BOTTOM] = y2; 3605 } 3606} 3607 3608/* 3609 *-------------------------------------------------------------- 3610 * 3611 * Range_RedoIfNeeded -- 3612 * 3613 * Recalculate the list of Ranges if they are marked out-of-date. 3614 * Also calculate the height and width of the canvas based on the 3615 * list of Ranges. 3616 * 3617 * Results: 3618 * None. 3619 * 3620 * Side effects: 3621 * Memory may be allocated. 3622 * 3623 *-------------------------------------------------------------- 3624 */ 3625 3626static void 3627Range_RedoIfNeeded( 3628 TreeCtrl *tree /* Widget info. */ 3629 ) 3630{ 3631 TreeDInfo dInfo = tree->dInfo; 3632 3633 if (dInfo->flags & DINFO_REDO_RANGES) { 3634 dInfo->rangeFirstD = dInfo->rangeLastD = NULL; 3635 dInfo->flags |= DINFO_OUT_OF_DATE; 3636 Range_Redo(tree); 3637 dInfo->flags &= ~DINFO_REDO_RANGES; 3638 3639#ifdef COMPLEX_WHITESPACE 3640 if (ComplexWhitespace(tree)) { 3641 dInfo->flags |= DINFO_DRAW_WHITESPACE; 3642 } 3643#endif 3644 3645 /* Do this after clearing REDO_RANGES to prevent infinite loop */ 3646 tree->totalWidth = tree->totalHeight = -1; 3647 (void) Tree_TotalWidth(tree); 3648 (void) Tree_TotalHeight(tree); 3649 dInfo->flags |= DINFO_REDO_INCREMENTS; 3650 } 3651} 3652 3653/* 3654 *-------------------------------------------------------------- 3655 * 3656 * DblBufWinDirty -- 3657 * 3658 * Add a rectangle to the dirty region of the "-doublebuffer window" 3659 * pixmap. 3660 * 3661 * Results: 3662 * None. 3663 * 3664 * Side effects: 3665 * None. 3666 * 3667 *-------------------------------------------------------------- 3668 */ 3669 3670static void 3671DblBufWinDirty( 3672 TreeCtrl *tree, 3673 int x1, 3674 int y1, 3675 int x2, 3676 int y2 3677 ) 3678{ 3679 TreeDInfo dInfo = tree->dInfo; 3680 XRectangle rect; 3681 3682 /* Fix BUG ID: 3015429 */ 3683 if (x1 >= x2 || y1 >= y2) 3684 return; 3685 3686 rect.x = x1; 3687 rect.y = y1; 3688 rect.width = x2 - x1; 3689 rect.height = y2 - y1; 3690 TkUnionRectWithRegion(&rect, dInfo->dirtyRgn, dInfo->dirtyRgn); 3691} 3692 3693#ifdef REDRAW_RGN 3694static void 3695AddRgnToRedrawRgn( 3696 TreeCtrl *tree, 3697 TkRegion rgn) 3698{ 3699 TreeDInfo dInfo = tree->dInfo; 3700 3701 Tree_UnionRegion(dInfo->redrawRgn, rgn, dInfo->redrawRgn); 3702} 3703 3704static void 3705AddRectToRedrawRgn( 3706 TreeCtrl *tree, 3707 int minX, 3708 int minY, 3709 int maxX, 3710 int maxY) 3711{ 3712 TkRegion rgn = Tree_GetRegion(tree); 3713 XRectangle rect; 3714 3715 rect.x = minX; 3716 rect.y = minY; 3717 rect.width = maxX - minX; 3718 rect.height = maxY - minY; 3719 Tree_SetRectRegion(rgn, &rect); 3720 3721 AddRgnToRedrawRgn(tree, rgn); 3722 3723 Tree_FreeRegion(tree, rgn); 3724} 3725#endif /* REDRAW_RGN */ 3726 3727/* 3728 *-------------------------------------------------------------- 3729 * 3730 * DItemAllDirty -- 3731 * 3732 * Determine if a DItem will be entirely redrawn in all columns. 3733 * 3734 * Results: 3735 * None. 3736 * 3737 * Side effects: 3738 * None. 3739 * 3740 *-------------------------------------------------------------- 3741 */ 3742 3743static int 3744DItemAllDirty( 3745 TreeCtrl *tree, 3746 DItem *dItem 3747 ) 3748{ 3749 TreeDInfo dInfo = tree->dInfo; 3750 3751 if ((!dInfo->empty && dInfo->rangeFirst != NULL) && 3752 !(dItem->area.flags & DITEM_ALL_DIRTY)) 3753 return 0; 3754 if (!dInfo->emptyL && !(dItem->left.flags & DITEM_ALL_DIRTY)) 3755 return 0; 3756 if (!dInfo->emptyR && !(dItem->right.flags & DITEM_ALL_DIRTY)) 3757 return 0; 3758 return 1; 3759} 3760 3761/* 3762 *-------------------------------------------------------------- 3763 * 3764 * ScrollVerticalComplex -- 3765 * 3766 * Perform scrolling by copying the pixels of items from the 3767 * previous display position to the current position. Any areas 3768 * of items copied over by the moved items are marked dirty. 3769 * 3770 * Results: 3771 * The number of items whose pixels were copied. 3772 * 3773 * Side effects: 3774 * Pixels are copied in the TreeCtrl window or in the 3775 * offscreen pixmap (if double-buffering is used). 3776 * 3777 *-------------------------------------------------------------- 3778 */ 3779 3780static int 3781ScrollVerticalComplex( 3782 TreeCtrl *tree /* Widget info. */ 3783 ) 3784{ 3785 TreeDInfo dInfo = tree->dInfo; 3786 DItem *dItem, *dItem2; 3787 Range *range; 3788 TkRegion damageRgn; 3789 int minX, minY, maxX, maxY; 3790 int oldX, oldY, width, height, offset; 3791 int y; 3792 int numCopy = 0; 3793 3794 if (dInfo->empty && dInfo->emptyL && dInfo->emptyR) 3795 return 0; 3796 3797 minX = Tree_BorderLeft(tree); 3798 minY = Tree_ContentTop(tree); 3799 maxX = Tree_BorderRight(tree); 3800 maxY = Tree_ContentBottom(tree); 3801 3802 /* Try updating the display by copying items on the screen to their 3803 * new location */ 3804 for (dItem = dInfo->dItem; 3805 dItem != NULL; 3806 dItem = dItem->next) { 3807 /* Copy an item to its new location unless: 3808 * (a) item display info is invalid 3809 * (b) item is in same location as last draw */ 3810 if (DItemAllDirty(tree, dItem) || 3811 (dItem->oldY == dItem->y)) 3812 continue; 3813 3814 numCopy++; 3815 3816 range = dItem->range; 3817 3818 /* This item was previously displayed so it only needs to be 3819 * copied to the new location. Copy all such items as one */ 3820 offset = dItem->y - dItem->oldY; 3821 height = dItem->height; 3822 for (dItem2 = dItem->next; 3823 dItem2 != NULL; 3824 dItem2 = dItem2->next) { 3825 if ((dItem2->range != range) || 3826 DItemAllDirty(tree, dItem2) || 3827 (dItem2->oldY + offset != dItem2->y)) 3828 break; 3829 numCopy++; 3830 height += dItem2->height; 3831 } 3832 3833 y = dItem->y; 3834 oldY = dItem->oldY; 3835 3836 /* Don't copy part of the window border */ 3837 if (oldY < minY) { 3838 height -= minY - oldY; 3839 oldY = minY; 3840 } 3841 if (oldY + height > maxY) 3842 height = maxY - oldY; 3843 3844 /* Don't copy over the window border */ 3845 if (oldY + offset < minY) { 3846 height -= minY - (oldY + offset); 3847 oldY += minY - (oldY + offset); 3848 } 3849 if (oldY + offset + height > maxY) 3850 height = maxY - (oldY + offset); 3851 3852 if (!dInfo->emptyL || !dInfo->emptyR) { 3853 oldX = minX; 3854 width = maxX - minX; 3855 } else { 3856 oldX = dItem->oldX; 3857 width = dItem->area.width; 3858 } 3859 3860 if (oldX < minX) { 3861 width -= minX - oldX; 3862 oldX = minX; 3863 } 3864 if (oldX + width > maxX) 3865 width = maxX - oldX; 3866 3867 /* Update oldY of copied items */ 3868 while (1) { 3869 /* If an item was partially visible, invalidate the exposed area */ 3870 if ((dItem->oldY < minY) && (offset > 0)) { 3871 if (!dInfo->empty && dInfo->rangeFirst != NULL) { 3872 InvalidateDItemX(dItem, &dItem->area, dItem->oldX, oldX, width); 3873 InvalidateDItemY(dItem, &dItem->area, dItem->oldY, dItem->oldY, minY - dItem->oldY); 3874 dItem->area.flags |= DITEM_DIRTY; 3875 } 3876 if (!dInfo->emptyL) { 3877 InvalidateDItemX(dItem, &dItem->left, dItem->left.x, oldX, width); 3878 InvalidateDItemY(dItem, &dItem->left, dItem->oldY, dItem->oldY, minY - dItem->oldY); 3879 dItem->left.flags |= DITEM_DIRTY; 3880 } 3881 if (!dInfo->emptyR) { 3882 InvalidateDItemX(dItem, &dItem->right, dItem->right.x, oldX, width); 3883 InvalidateDItemY(dItem, &dItem->right, dItem->oldY, dItem->oldY, minY - dItem->oldY); 3884 dItem->right.flags |= DITEM_DIRTY; 3885 } 3886 } 3887 if ((dItem->oldY + dItem->height > maxY) && (offset < 0)) { 3888 if (!dInfo->empty && dInfo->rangeFirst != NULL) { 3889 InvalidateDItemX(dItem, &dItem->area, dItem->oldX, oldX, width); 3890 InvalidateDItemY(dItem, &dItem->area, dItem->oldY, maxY, maxY - dItem->oldY + dItem->height); 3891 dItem->area.flags |= DITEM_DIRTY; 3892 } 3893 if (!dInfo->emptyL) { 3894 InvalidateDItemX(dItem, &dItem->left, dItem->left.x, oldX, width); 3895 InvalidateDItemY(dItem, &dItem->left, dItem->oldY, maxY, maxY - dItem->oldY + dItem->height); 3896 dItem->left.flags |= DITEM_DIRTY; 3897 } 3898 if (!dInfo->emptyR) { 3899 InvalidateDItemX(dItem, &dItem->right, dItem->right.x, oldX, width); 3900 InvalidateDItemY(dItem, &dItem->right, dItem->oldY, maxY, maxY - dItem->oldY + dItem->height); 3901 dItem->right.flags |= DITEM_DIRTY; 3902 } 3903 } 3904 dItem->oldY = dItem->y; 3905 if (dItem->next == dItem2) 3906 break; 3907 dItem = dItem->next; 3908 } 3909 3910 /* Invalidate parts of items being copied over */ 3911 for ( ; dItem2 != NULL; dItem2 = dItem2->next) { 3912 if (dItem2->range != range) 3913 break; 3914 if (!DItemAllDirty(tree, dItem2) && 3915 (dItem2->oldY + dItem2->height > y) && 3916 (dItem2->oldY < y + height)) { 3917 if (!dInfo->empty && dInfo->rangeFirst != NULL) { 3918 InvalidateDItemX(dItem2, &dItem2->area, dItem2->oldX, oldX, width); 3919 InvalidateDItemY(dItem2, &dItem2->area, dItem2->oldY, y, height); 3920 dItem2->area.flags |= DITEM_DIRTY; 3921 } 3922 if (!dInfo->emptyL) { 3923 InvalidateDItemX(dItem2, &dItem2->left, dItem2->left.x, oldX, width); 3924 InvalidateDItemY(dItem2, &dItem2->left, dItem2->oldY, y, height); 3925 dItem2->left.flags |= DITEM_DIRTY; 3926 } 3927 if (!dInfo->emptyR) { 3928 InvalidateDItemX(dItem2, &dItem2->right, dItem2->right.x, oldX, width); 3929 InvalidateDItemY(dItem2, &dItem2->right, dItem2->oldY, y, height); 3930 dItem2->right.flags |= DITEM_DIRTY; 3931 } 3932 } 3933 } 3934 3935 if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) { 3936 int dirtyMin, dirtyMax; 3937 XCopyArea(tree->display, dInfo->pixmapW.drawable, 3938 dInfo->pixmapW.drawable, tree->copyGC, 3939 oldX, oldY, width, height, 3940 oldX, oldY + offset); 3941 if (offset < 0) { 3942 dirtyMin = oldY + offset + height; 3943 dirtyMax = oldY + height; 3944 } else { 3945 dirtyMin = oldY; 3946 dirtyMax = oldY + offset; 3947 } 3948 Tree_InvalidateArea(tree, oldX, dirtyMin, oldX + width, dirtyMax); 3949 DblBufWinDirty(tree, oldX, oldY + offset, 3950 oldX + width, oldY + offset + height); 3951 continue; 3952 } 3953 3954 /* Copy */ 3955 damageRgn = Tree_GetRegion(tree); 3956 if (Tree_ScrollWindow(tree, dInfo->scrollGC, 3957 oldX, oldY, width, height, 0, offset, damageRgn)) { 3958 DisplayDelay(tree); 3959 Tree_InvalidateRegion(tree, damageRgn); 3960 } 3961 Tree_FreeRegion(tree, damageRgn); 3962 } 3963 return numCopy; 3964} 3965 3966/* 3967 *-------------------------------------------------------------- 3968 * 3969 * ScrollHorizontalSimple -- 3970 * 3971 * Perform scrolling by shifting the pixels in the content area of 3972 * the list to the left or right. 3973 * 3974 * Results: 3975 * None. 3976 * 3977 * Side effects: 3978 * Stuff is copied/scrolled in the TreeCtrl window or in the 3979 * offscreen pixmap (if double-buffering is used). 3980 * 3981 *-------------------------------------------------------------- 3982 */ 3983 3984static void 3985ScrollHorizontalSimple( 3986 TreeCtrl *tree /* Widget info. */ 3987 ) 3988{ 3989 TreeDInfo dInfo = tree->dInfo; 3990 DItem *dItem; 3991 TkRegion damageRgn; 3992 int minX, minY, maxX, maxY; 3993 int width, offset; 3994 int x, y; 3995 int dirtyMin, dirtyMax; 3996 3997 if (dInfo->xOrigin == tree->xOrigin) 3998 return; 3999 4000 /* Only column headers are visible. */ 4001 if (dInfo->rangeFirst == NULL) 4002 return; 4003 4004 if (dInfo->empty) 4005 return; 4006 4007 minX = dInfo->bounds[0]; 4008 minY = dInfo->bounds[1]; 4009 maxX = dInfo->bounds[2]; 4010 maxY = dInfo->bounds[3]; 4011 4012 /* Update oldX */ 4013 for (dItem = dInfo->dItem; 4014 dItem != NULL; 4015 dItem = dItem->next) { 4016 dItem->oldX = dItem->area.x; 4017 } 4018 4019 offset = dInfo->xOrigin - tree->xOrigin; 4020 4021 /* We only scroll the content, not the whitespace */ 4022 y = 0 - tree->yOrigin + Tree_TotalHeight(tree); 4023 if (y < maxY) 4024 maxY = y; 4025 4026 /* Simplify if a whole screen was scrolled. */ 4027 if (abs(offset) >= maxX - minX) { 4028 Tree_InvalidateArea(tree, minX, minY, maxX, maxY); 4029 return; 4030 } 4031 4032 /* We only scroll the content, not the whitespace */ 4033 x = 0 - tree->xOrigin + Tree_TotalWidth(tree); 4034 if (x < maxX) 4035 maxX = x; 4036 4037 width = maxX - minX - abs(offset); 4038 4039 /* Move pixels right */ 4040 if (offset > 0) { 4041 x = minX; 4042 dirtyMin = minX; 4043 dirtyMax = maxX - width; 4044 4045 /* Move pixels left */ 4046 } else { 4047 x = maxX - width; 4048 dirtyMin = minX + width; 4049 dirtyMax = maxX; 4050 } 4051 4052 damageRgn = Tree_GetRegion(tree); 4053 4054 if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) { 4055 XCopyArea(tree->display, dInfo->pixmapW.drawable, 4056 dInfo->pixmapW.drawable, 4057 tree->copyGC, 4058 x, minY, width, maxY - minY, 4059 x + offset, minY); 4060 Tree_InvalidateArea(tree, dirtyMin, minY, dirtyMax, maxY); 4061 Tree_FreeRegion(tree, damageRgn); 4062 return; 4063 } 4064 4065 if (Tree_ScrollWindow(tree, dInfo->scrollGC, 4066 x, minY, width, maxY - minY, offset, 0, damageRgn)) { 4067 DisplayDelay(tree); 4068 Tree_InvalidateRegion(tree, damageRgn); 4069 } 4070 Tree_FreeRegion(tree, damageRgn); 4071 Tree_InvalidateArea(tree, dirtyMin, minY, dirtyMax, maxY); 4072 4073 /* Invalidate the part of the whitespace that the content was copied 4074 * over. This fixes the case where items are deleted and the list 4075 * scrolls left: the deleted-item pixels were scrolled right over the 4076 * old whitespace. */ 4077 if (offset > 0) { 4078#if 1 4079 TkRegion rgn = Tree_GetRegion(tree); 4080 XRectangle rect; 4081 rect.x = dInfo->bounds[0]; 4082 rect.y = dInfo->bounds[1]; 4083 rect.width = dInfo->bounds[2] - rect.x; 4084 rect.height = dInfo->bounds[3] - rect.y; 4085 TkUnionRectWithRegion(&rect, rgn, rgn); 4086 TkSubtractRegion(rgn, dInfo->wsRgn, rgn); 4087 Tree_OffsetRegion(rgn, offset, 0); 4088 TkSubtractRegion(dInfo->wsRgn, rgn, dInfo->wsRgn); 4089 Tree_FreeRegion(tree, rgn); 4090#else 4091 dirtyMin = minX + width; 4092 dirtyMax = maxX; 4093 InvalidateWhitespace(tree, dirtyMin, minY, dirtyMax, maxY); 4094#endif 4095 } 4096} 4097 4098/* 4099 *-------------------------------------------------------------- 4100 * 4101 * ScrollVerticalSimple -- 4102 * 4103 * Perform scrolling by shifting the pixels in the content area of 4104 * the list up or down. 4105 * 4106 * Results: 4107 * None. 4108 * 4109 * Side effects: 4110 * Stuff is copied/scrolled in the TreeCtrl window or in the 4111 * offscreen pixmap (if double-buffering is used). 4112 * 4113 *-------------------------------------------------------------- 4114 */ 4115 4116static void 4117ScrollVerticalSimple( 4118 TreeCtrl *tree /* Widget info. */ 4119 ) 4120{ 4121 TreeDInfo dInfo = tree->dInfo; 4122 DItem *dItem; 4123 TkRegion damageRgn; 4124 int minX, minY, maxX, maxY; 4125 int height, offset; 4126 int x, y; 4127 int dirtyMin, dirtyMax; 4128 4129 if (dInfo->yOrigin == tree->yOrigin) 4130 return; 4131 4132 /* Update oldY */ 4133 for (dItem = dInfo->dItem; 4134 dItem != NULL; 4135 dItem = dItem->next) { 4136 dItem->oldY = dItem->y; 4137 } 4138 4139 if (dInfo->empty) 4140 return; 4141 4142 minX = dInfo->bounds[0]; 4143 minY = dInfo->bounds[1]; 4144 maxX = dInfo->bounds[2]; 4145 maxY = dInfo->bounds[3]; 4146 4147 offset = dInfo->yOrigin - tree->yOrigin; 4148 4149 /* Scroll the items, not the whitespace to the right */ 4150 x = 0 - tree->xOrigin + Tree_TotalWidth(tree); 4151 if (x < maxX) 4152 maxX = x; 4153 4154 /* Simplify if a whole screen was scrolled. */ 4155 if (abs(offset) > maxY - minY) { 4156 Tree_InvalidateArea(tree, minX, minY, maxX, maxY); 4157 return; 4158 } 4159 4160 height = maxY - minY - abs(offset); 4161 4162 /* Move pixels down */ 4163 if (offset > 0) { 4164 y = minY; 4165 dirtyMin = minY; 4166 dirtyMax = maxY - height; 4167 4168 /* Move pixels up */ 4169 } else { 4170 y = maxY - height; 4171 dirtyMin = minY + height; 4172 dirtyMax = maxY; 4173 } 4174 4175 damageRgn = Tree_GetRegion(tree); 4176 4177 if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) { 4178 XCopyArea(tree->display, dInfo->pixmapW.drawable, 4179 dInfo->pixmapW.drawable, tree->copyGC, 4180 minX, y, maxX - minX, height, 4181 minX, y + offset); 4182 Tree_InvalidateArea(tree, minX, dirtyMin, maxX, dirtyMax); 4183 Tree_FreeRegion(tree, damageRgn); 4184 return; 4185 } 4186 4187 if (Tree_ScrollWindow(tree, dInfo->scrollGC, 4188 minX, y, maxX - minX, height, 0, offset, damageRgn)) { 4189 DisplayDelay(tree); 4190 Tree_InvalidateRegion(tree, damageRgn); 4191 } 4192 Tree_FreeRegion(tree, damageRgn); 4193 Tree_InvalidateArea(tree, minX, dirtyMin, maxX, dirtyMax); 4194 4195 /* Invalidate the part of the whitespace that the content was copied 4196 * over. This fixes the case where items are deleted and the list 4197 * scrolls up: the deleted-item pixels were scrolled down over the 4198 * old whitespace. */ 4199 if (offset > 0) { 4200#if 1 4201 TkRegion rgn = Tree_GetRegion(tree); 4202 XRectangle rect; 4203 rect.x = dInfo->bounds[0]; 4204 rect.y = dInfo->bounds[1]; 4205 rect.width = dInfo->bounds[2] - rect.x; 4206 rect.height = dInfo->bounds[3] - rect.y; 4207 TkUnionRectWithRegion(&rect, rgn, rgn); 4208 TkSubtractRegion(rgn, dInfo->wsRgn, rgn); 4209 Tree_OffsetRegion(rgn, 0, offset); 4210 TkSubtractRegion(dInfo->wsRgn, rgn, dInfo->wsRgn); 4211 Tree_FreeRegion(tree, rgn); 4212#else 4213 dirtyMin = minY + height; 4214 dirtyMax = maxY; 4215 InvalidateWhitespace(tree, minX, dirtyMin, maxX, dirtyMax); 4216#endif 4217 } 4218} 4219 4220/* 4221 *-------------------------------------------------------------- 4222 * 4223 * ScrollHorizontalComplex -- 4224 * 4225 * Perform scrolling by copying the pixels of items from the 4226 * previous display position to the current position. Any areas 4227 * of items copied over by the moved items are marked dirty. 4228 * 4229 * Results: 4230 * The number of items whose pixels were copied. 4231 * 4232 * Side effects: 4233 * Pixels are copied in the TreeCtrl window or in the 4234 * offscreen pixmap (if double-buffering is used). 4235 * 4236 *-------------------------------------------------------------- 4237 */ 4238 4239static int 4240ScrollHorizontalComplex( 4241 TreeCtrl *tree /* Widget info. */ 4242 ) 4243{ 4244 TreeDInfo dInfo = tree->dInfo; 4245 DItem *dItem, *dItem2; 4246 Range *range; 4247 TkRegion damageRgn; 4248 int minX, minY, maxX, maxY; 4249 int oldX, oldY, width, height, offset; 4250 int x; 4251 int numCopy = 0; 4252 4253 if (!Tree_AreaBbox(tree, TREE_AREA_CONTENT, &minX, &minY, &maxX, &maxY)) 4254 return 0; 4255 4256 /* Try updating the display by copying items on the screen to their 4257 * new location */ 4258 for (dItem = dInfo->dItem; 4259 dItem != NULL; 4260 dItem = dItem->next) { 4261 /* Copy an item to its new location unless: 4262 * (a) item display info is invalid 4263 * (b) item is in same location as last draw */ 4264 if ((dItem->area.flags & DITEM_ALL_DIRTY) || 4265 (dItem->oldX == dItem->area.x)) 4266 continue; 4267 4268 numCopy++; 4269 4270 range = dItem->range; 4271 4272 /* This item was previously displayed so it only needs to be 4273 * copied to the new location. Copy all such items as one */ 4274 offset = dItem->area.x - dItem->oldX; 4275 width = dItem->area.width; 4276 for (dItem2 = dItem->next; 4277 dItem2 != NULL; 4278 dItem2 = dItem2->next) { 4279 if ((dItem2->range != range) || 4280 (dItem2->area.flags & DITEM_ALL_DIRTY) || 4281 (dItem2->oldX + offset != dItem2->area.x)) 4282 break; 4283 numCopy++; 4284 width += dItem2->area.width; 4285 } 4286 4287 x = dItem->area.x; 4288 oldX = dItem->oldX; 4289 4290 /* Don't copy part of the window border */ 4291 if (oldX < minX) { 4292 width -= minX - oldX; 4293 oldX = minX; 4294 } 4295 if (oldX + width > maxX) 4296 width = maxX - oldX; 4297 4298 /* Don't copy over the window border */ 4299 if (oldX + offset < minX) { 4300 width -= minX - (oldX + offset); 4301 oldX += minX - (oldX + offset); 4302 } 4303 if (oldX + offset + width > maxX) 4304 width = maxX - (oldX + offset); 4305 4306 oldY = dItem->oldY; 4307 height = dItem->height; /* range->totalHeight */ 4308 if (oldY < minY) { 4309 height -= minY - oldY; 4310 oldY = minY; 4311 } 4312 if (oldY + height > maxY) 4313 height = maxY - oldY; 4314 4315 /* Update oldX of copied items */ 4316 while (1) { 4317 /* If an item was partially visible, invalidate the exposed area */ 4318 if ((dItem->oldX < minX) && (offset > 0)) { 4319 InvalidateDItemX(dItem, &dItem->area, dItem->oldX, dItem->oldX, minX - dItem->oldX); 4320 InvalidateDItemY(dItem, &dItem->area, oldY, oldY, height); 4321 dItem->area.flags |= DITEM_DIRTY; 4322 } 4323 if ((dItem->oldX + dItem->area.width > maxX) && (offset < 0)) { 4324 InvalidateDItemX(dItem, &dItem->area, dItem->oldX, maxX, maxX - dItem->oldX + dItem->area.width); 4325 InvalidateDItemY(dItem, &dItem->area, oldY, oldY, height); 4326 dItem->area.flags |= DITEM_DIRTY; 4327 } 4328 dItem->oldX = dItem->area.x; 4329 if (dItem->next == dItem2) 4330 break; 4331 dItem = dItem->next; 4332 } 4333 4334 /* Invalidate parts of items being copied over */ 4335 for ( ; dItem2 != NULL; dItem2 = dItem2->next) { 4336 if (dItem2->range != range) 4337 break; 4338 if (!(dItem2->area.flags & DITEM_ALL_DIRTY) && 4339 (dItem2->oldX + dItem2->area.width > x) && 4340 (dItem2->oldX < x + width)) { 4341 InvalidateDItemX(dItem2, &dItem2->area, dItem2->oldX, x, width); 4342 InvalidateDItemY(dItem2, &dItem2->area, oldY, oldY, height); 4343 dItem2->area.flags |= DITEM_DIRTY; 4344 } 4345 } 4346 4347 if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) { 4348 int dirtyMin, dirtyMax; 4349 XCopyArea(tree->display, dInfo->pixmapW.drawable, 4350 dInfo->pixmapW.drawable, tree->copyGC, 4351 oldX, oldY, width, height, 4352 oldX + offset, oldY); 4353 if (offset < 0) { 4354 dirtyMin = oldX + offset + width; 4355 dirtyMax = oldX + width; 4356 } else { 4357 dirtyMin = oldX; 4358 dirtyMax = oldX + offset; 4359 } 4360 Tree_InvalidateArea(tree, dirtyMin, oldY, dirtyMax, oldY + height); 4361 DblBufWinDirty(tree, oldX + offset, oldY, oldX + offset + width, 4362 oldY + height); 4363 continue; 4364 } 4365 4366 /* Copy */ 4367 damageRgn = Tree_GetRegion(tree); 4368 if (Tree_ScrollWindow(tree, dInfo->scrollGC, 4369 oldX, oldY, width, height, offset, 0, damageRgn)) { 4370 DisplayDelay(tree); 4371 Tree_InvalidateRegion(tree, damageRgn); 4372 } 4373 Tree_FreeRegion(tree, damageRgn); 4374 } 4375 return numCopy; 4376} 4377 4378/* 4379 *---------------------------------------------------------------------- 4380 * 4381 * Proxy_IsXOR -- 4382 * 4383 * Return true if the column/row proxies should be drawn with XOR. 4384 * 4385 * Results: 4386 * None. 4387 * 4388 * Side effects: 4389 * None. 4390 * 4391 *---------------------------------------------------------------------- 4392 */ 4393 4394static int 4395Proxy_IsXOR(void) 4396{ 4397#if defined(WIN32) 4398 return FALSE; /* TRUE on XP, FALSE on Win7 (lots of flickering) */ 4399#elif defined(MAC_TK_CARBON) 4400 return TRUE; 4401#elif defined(MAC_TK_COCOA) 4402 return FALSE; /* Cocoa doesn't have XOR */ 4403#else 4404 return TRUE; /* X11 */ 4405#endif 4406} 4407 4408/* 4409 *-------------------------------------------------------------- 4410 * 4411 * Proxy_DrawXOR -- 4412 * 4413 * Draw (or erase) the visual indicator used when the user is 4414 * resizing a column or row (and -columnresizemode is "proxy"). 4415 * 4416 * Results: 4417 * None. 4418 * 4419 * Side effects: 4420 * Stuff is drawn in the TreeCtrl window (or erased, since this 4421 * is XOR drawing). 4422 * 4423 *-------------------------------------------------------------- 4424 */ 4425 4426static void 4427Proxy_DrawXOR( 4428 TreeCtrl *tree, /* Widget info. */ 4429 int x1, /* Vertical or horizontal line window coords. */ 4430 int y1, 4431 int x2, 4432 int y2 4433 ) 4434{ 4435#if defined(MAC_TK_CARBON) 4436 DrawXORLine(tree->display, Tk_WindowId(tree->tkwin), x1, y1, x2, y2); 4437#else 4438 XGCValues gcValues; 4439 unsigned long gcMask; 4440 GC gc; 4441 4442#if defined(MAC_TK_COCOA) 4443 gcValues.function = GXcopy; 4444#else 4445 gcValues.function = GXinvert; 4446#endif 4447 gcValues.graphics_exposures = False; 4448 gcMask = GCFunction | GCGraphicsExposures; 4449 gc = Tree_GetGC(tree, gcMask, &gcValues); 4450 4451#if defined(WIN32) 4452 /* GXinvert doesn't work with XFillRectangle() on Win32 */ 4453 XDrawLine(tree->display, Tk_WindowId(tree->tkwin), gc, x1, y1, x2, y2); 4454#else 4455 XFillRectangle(tree->display, Tk_WindowId(tree->tkwin), gc, 4456 x1, y1, MAX(x2 - x1, 1), MAX(y2 - y1, 1)); 4457#endif 4458 4459#endif /* !MAC_TK_CARBON */ 4460} 4461 4462/* 4463 *-------------------------------------------------------------- 4464 * 4465 * TreeColumnProxy_Display -- 4466 * 4467 * Display the visual indicator used when the user is 4468 * resizing a column (if it isn't displayed and should be 4469 * displayed). 4470 * 4471 * Results: 4472 * None. 4473 * 4474 * Side effects: 4475 * Stuff is drawn in the TreeCtrl window. 4476 * 4477 *-------------------------------------------------------------- 4478 */ 4479 4480void 4481TreeColumnProxy_Display( 4482 TreeCtrl *tree /* Widget info. */ 4483 ) 4484{ 4485 if (!tree->columnProxy.onScreen && (tree->columnProxy.xObj != NULL)) { 4486 tree->columnProxy.sx = tree->columnProxy.x; 4487 if (Proxy_IsXOR()) { 4488 Proxy_DrawXOR(tree, tree->columnProxy.x, Tree_BorderTop(tree), 4489 tree->columnProxy.x, Tree_BorderBottom(tree)); 4490 } else { 4491 Tree_EventuallyRedraw(tree); 4492 } 4493 tree->columnProxy.onScreen = TRUE; 4494 } 4495} 4496 4497/* 4498 *-------------------------------------------------------------- 4499 * 4500 * TreeColumnProxy_Undisplay -- 4501 * 4502 * Hide the visual indicator used when the user is 4503 * resizing a column (if it is displayed). 4504 * 4505 * Results: 4506 * None. 4507 * 4508 * Side effects: 4509 * Stuff is erased in the TreeCtrl window. 4510 * 4511 *-------------------------------------------------------------- 4512 */ 4513 4514void 4515TreeColumnProxy_Undisplay( 4516 TreeCtrl *tree /* Widget info. */ 4517 ) 4518{ 4519 if (tree->columnProxy.onScreen) { 4520 if (Proxy_IsXOR()) { 4521 Proxy_DrawXOR(tree, tree->columnProxy.sx, Tree_BorderTop(tree), 4522 tree->columnProxy.sx, Tree_BorderBottom(tree)); 4523 } else { 4524 Tree_EventuallyRedraw(tree); 4525 } 4526 tree->columnProxy.onScreen = FALSE; 4527 } 4528} 4529 4530/* 4531 *-------------------------------------------------------------- 4532 * 4533 * TreeRowProxy_Display -- 4534 * 4535 * Display the visual indicator used when the user is 4536 * resizing a row (if it isn't displayed and should be 4537 * displayed). 4538 * 4539 * Results: 4540 * None. 4541 * 4542 * Side effects: 4543 * Stuff is drawn in the TreeCtrl window. 4544 * 4545 *-------------------------------------------------------------- 4546 */ 4547 4548void 4549TreeRowProxy_Display( 4550 TreeCtrl *tree /* Widget info. */ 4551 ) 4552{ 4553 if (!tree->rowProxy.onScreen && (tree->rowProxy.yObj != NULL)) { 4554 tree->rowProxy.sy = tree->rowProxy.y; 4555 if (Proxy_IsXOR()) { 4556 Proxy_DrawXOR(tree, Tree_BorderLeft(tree), tree->rowProxy.y, 4557 Tree_BorderRight(tree), tree->rowProxy.y); 4558 } else { 4559 Tree_EventuallyRedraw(tree); 4560 } 4561 tree->rowProxy.onScreen = TRUE; 4562 } 4563} 4564 4565/* 4566 *-------------------------------------------------------------- 4567 * 4568 * TreeRowProxy_Undisplay -- 4569 * 4570 * Hide the visual indicator used when the user is 4571 * resizing a row (if it is displayed). 4572 * 4573 * Results: 4574 * None. 4575 * 4576 * Side effects: 4577 * Stuff is erased in the TreeCtrl window. 4578 * 4579 *-------------------------------------------------------------- 4580 */ 4581 4582void 4583TreeRowProxy_Undisplay( 4584 TreeCtrl *tree /* Widget info. */ 4585 ) 4586{ 4587 if (tree->rowProxy.onScreen) { 4588 if (Proxy_IsXOR()) { 4589 Proxy_DrawXOR(tree, Tree_BorderLeft(tree), tree->rowProxy.sy, 4590 Tree_BorderRight(tree), tree->rowProxy.sy); 4591 } else { 4592 Tree_EventuallyRedraw(tree); 4593 } 4594 tree->rowProxy.onScreen = FALSE; 4595 } 4596} 4597 4598/* 4599 *-------------------------------------------------------------- 4600 * 4601 * Proxy_Draw -- 4602 * 4603 * Draw the non-XOR -columnproxy or -rowproxy indicator. 4604 * 4605 * Results: 4606 * None. 4607 * 4608 * Side effects: 4609 * Stuff is drawn into a drawable. 4610 * 4611 *-------------------------------------------------------------- 4612 */ 4613 4614static void 4615Proxy_Draw( 4616 TreeCtrl *tree, /* Widget info. */ 4617 TreeDrawable td, /* Where to draw. */ 4618 int x1, /* Vertical or horizontal line window coords. */ 4619 int y1, 4620 int x2, 4621 int y2 4622 ) 4623{ 4624 XGCValues gcValues; 4625 unsigned long gcMask; 4626 GC gc; 4627 4628 gcValues.function = GXcopy; 4629 gcValues.graphics_exposures = False; 4630 gcMask = GCFunction | GCGraphicsExposures; 4631 gc = Tree_GetGC(tree, gcMask, &gcValues); 4632 4633 XDrawLine(tree->display, td.drawable, gc, x1, y1, x2, y2); 4634} 4635 4636/* 4637 *-------------------------------------------------------------- 4638 * 4639 * TreeColumnProxy_Draw -- 4640 * 4641 * Draw the non-XOR -columnproxy indicator if it is visible. 4642 * 4643 * Results: 4644 * None. 4645 * 4646 * Side effects: 4647 * Stuff is drawn into a drawable. 4648 * 4649 *-------------------------------------------------------------- 4650 */ 4651 4652static void 4653TreeColumnProxy_Draw( 4654 TreeCtrl *tree, /* Widget info. */ 4655 TreeDrawable td /* Where to draw. */ 4656 ) 4657{ 4658 if (tree->columnProxy.xObj == NULL) 4659 return; 4660 Proxy_Draw(tree, td, tree->columnProxy.x, Tree_BorderTop(tree), 4661 tree->columnProxy.x, Tree_BorderBottom(tree)); 4662} 4663 4664/* 4665 *-------------------------------------------------------------- 4666 * 4667 * TreeRowProxy_Draw -- 4668 * 4669 * Draw the non-XOR -rowproxy indicator if it is visible. 4670 * 4671 * Results: 4672 * None. 4673 * 4674 * Side effects: 4675 * Stuff is drawn into a drawable. 4676 * 4677 *-------------------------------------------------------------- 4678 */ 4679 4680static void 4681TreeRowProxy_Draw( 4682 TreeCtrl *tree, /* Widget info. */ 4683 TreeDrawable td /* Where to draw. */ 4684 ) 4685{ 4686 if (tree->rowProxy.yObj == NULL) 4687 return; 4688 Proxy_Draw(tree, td, Tree_BorderLeft(tree), tree->rowProxy.y, 4689 Tree_BorderRight(tree), tree->rowProxy.y); 4690} 4691 4692/* 4693 *-------------------------------------------------------------- 4694 * 4695 * CalcWhiteSpaceRegion -- 4696 * 4697 * Create a new region containing all the whitespace of the list 4698 * The whitespace is the area inside the borders/header where items 4699 * are not displayed. 4700 * 4701 * Results: 4702 * The new whitespace region, which may be empty. 4703 * 4704 * Side effects: 4705 * A new region is allocated. 4706 * 4707 *-------------------------------------------------------------- 4708 */ 4709 4710static TkRegion 4711CalcWhiteSpaceRegion( 4712 TreeCtrl *tree /* Widget info. */ 4713 ) 4714{ 4715 TreeDInfo dInfo = tree->dInfo; 4716 int x, y, minX, minY, maxX, maxY; 4717 int left, right, top, bottom; 4718 TkRegion wsRgn; 4719 XRectangle rect; 4720 Range *range; 4721 4722 x = 0 - tree->xOrigin; 4723 y = 0 - tree->yOrigin; 4724 4725 wsRgn = Tree_GetRegion(tree); 4726 4727 /* Erase area below left columns */ 4728 if (!dInfo->emptyL) { 4729 minX = dInfo->boundsL[0]; 4730 minY = dInfo->boundsL[1]; 4731 maxX = dInfo->boundsL[2]; 4732 maxY = dInfo->boundsL[3]; 4733 if (y + Tree_TotalHeight(tree) < maxY) { 4734 rect.x = minX; 4735 rect.y = y + Tree_TotalHeight(tree); 4736 rect.width = maxX - minX; 4737 rect.height = maxY - (y + Tree_TotalHeight(tree)); 4738 TkUnionRectWithRegion(&rect, wsRgn, wsRgn); 4739 } 4740 } 4741 4742 /* Erase area below right columns */ 4743 if (!dInfo->emptyR) { 4744 minX = dInfo->boundsR[0]; 4745 minY = dInfo->boundsR[1]; 4746 maxX = dInfo->boundsR[2]; 4747 maxY = dInfo->boundsR[3]; 4748 if (y + Tree_TotalHeight(tree) < maxY) { 4749 rect.x = minX; 4750 rect.y = y + Tree_TotalHeight(tree); 4751 rect.width = maxX - minX; 4752 rect.height = maxY - (y + Tree_TotalHeight(tree)); 4753 TkUnionRectWithRegion(&rect, wsRgn, wsRgn); 4754 } 4755 } 4756 4757 if (dInfo->empty) 4758 return wsRgn; 4759 4760 minX = dInfo->bounds[0]; 4761 minY = dInfo->bounds[1]; 4762 maxX = dInfo->bounds[2]; 4763 maxY = dInfo->bounds[3]; 4764 4765 /* Only the header is visible. */ 4766 if (dInfo->rangeFirst == NULL) { 4767 rect.x = minX; 4768 rect.y = minY; 4769 rect.width = maxX - rect.x; 4770 rect.height = maxY - rect.y; 4771 TkUnionRectWithRegion(&rect, wsRgn, wsRgn); 4772 return wsRgn; 4773 } 4774 4775 if (tree->vertical) { 4776 /* Erase area to right of last Range */ 4777 if (x + Tree_TotalWidth(tree) < maxX) { 4778 rect.x = x + Tree_TotalWidth(tree); 4779 rect.y = minY; 4780 rect.width = maxX - rect.x; 4781 rect.height = maxY - rect.y; 4782 TkUnionRectWithRegion(&rect, wsRgn, wsRgn); 4783 } 4784 } else { 4785 /* Erase area below last Range */ 4786 if (y + Tree_TotalHeight(tree) < maxY) { 4787 rect.x = minX; 4788 rect.y = y + Tree_TotalHeight(tree); 4789 rect.width = maxX - rect.x; 4790 rect.height = maxY - rect.y; 4791 TkUnionRectWithRegion(&rect, wsRgn, wsRgn); 4792 } 4793 } 4794 4795 for (range = dInfo->rangeFirstD; 4796 range != NULL; 4797 range = range->next) { 4798 if (tree->vertical) { 4799 left = MAX(x + range->offset, minX); 4800 right = MIN(x + range->offset + range->totalWidth, maxX); 4801 top = MAX(y + range->totalHeight, minY); 4802 bottom = maxY; 4803 4804 /* Erase area below Range */ 4805 if (top < bottom) { 4806 rect.x = left; 4807 rect.y = top; 4808 rect.width = right - left; 4809 rect.height = bottom - top; 4810 TkUnionRectWithRegion(&rect, wsRgn, wsRgn); 4811 } 4812 } else { 4813 left = MAX(x + range->totalWidth, minX); 4814 right = maxX; 4815 top = MAX(y + range->offset, minY); 4816 bottom = MIN(y + range->offset + range->totalHeight, maxY); 4817 4818 /* Erase area to right of Range */ 4819 if (left < right) { 4820 rect.x = left; 4821 rect.y = top; 4822 rect.width = right - left; 4823 rect.height = bottom - top; 4824 TkUnionRectWithRegion(&rect, wsRgn, wsRgn); 4825 } 4826 } 4827 if (range == dInfo->rangeLastD) 4828 break; 4829 } 4830 return wsRgn; 4831} 4832 4833#ifdef COMPLEX_WHITESPACE 4834 4835/* 4836 *-------------------------------------------------------------- 4837 * 4838 * Tree_IntersectRect -- 4839 * 4840 * Determine the area of overlap between two rectangles. 4841 * 4842 * Results: 4843 * If the rectangles have non-zero size and overlap, resultPtr 4844 * holds the area of overlap, and the return value is 1. 4845 * Otherwise 0 is returned and resultPtr is untouched. 4846 * 4847 * Side effects: 4848 * None. 4849 * 4850 *-------------------------------------------------------------- 4851 */ 4852 4853static int 4854Tree_IntersectRect( 4855 XRectangle *resultPtr, /* Out: area of overlap. May be the same 4856 * as r1 or r2. */ 4857 CONST XRectangle *r1, /* First rectangle. */ 4858 CONST XRectangle *r2 /* Second rectangle. */ 4859 ) 4860{ 4861 XRectangle result; 4862 4863 if (r1->width == 0 || r1->height == 0) return 0; 4864 if (r2->width == 0 || r2->height == 0) return 0; 4865 if (r1->x >= r2->x + r2->width) return 0; 4866 if (r2->x >= r1->x + r1->width) return 0; 4867 if (r1->y >= r2->y + r2->height) return 0; 4868 if (r2->y >= r1->y + r1->height) return 0; 4869 4870 result.x = MAX(r1->x, r2->x); 4871 result.width = MIN(r1->x + r1->width, r2->x + r2->width) - result.x; 4872 result.y = MAX(r1->y, r2->y); 4873 result.height = MIN(r1->y + r1->height, r2->y + r2->height) - result.y; 4874 4875 *resultPtr = result; 4876 return 1; 4877} 4878 4879/* 4880 *-------------------------------------------------------------- 4881 * 4882 * GetItemBgIndex -- 4883 * 4884 * Determine the index used to pick an -itembackground color 4885 * for a displayed item. 4886 * This is only valid for tree->vertical=1. 4887 * 4888 * Results: 4889 * Integer index. 4890 * 4891 * Side effects: 4892 * None. 4893 * 4894 *-------------------------------------------------------------- 4895 */ 4896 4897static int 4898GetItemBgIndex( 4899 TreeCtrl *tree, /* Widget info. */ 4900 RItem *rItem /* Range info for an item. */ 4901 ) 4902{ 4903 Range *range = rItem->range; 4904 int index, indexVis; 4905 4906 TreeItem_ToIndex(tree, rItem->item, &index, &indexVis); 4907 switch (tree->backgroundMode) { 4908#ifdef DEPRECATED 4909 case BG_MODE_INDEX: 4910#endif 4911 case BG_MODE_ORDER: 4912 break; 4913#ifdef DEPRECATED 4914 case BG_MODE_VISINDEX: 4915#endif 4916 case BG_MODE_ORDERVIS: 4917 index = indexVis; 4918 break; 4919 case BG_MODE_COLUMN: 4920 index = range->index; /* always zero */ 4921 break; 4922 case BG_MODE_ROW: 4923 index = rItem->index; 4924 break; 4925 } 4926 return index; 4927} 4928 4929/* 4930 *-------------------------------------------------------------- 4931 * 4932 * DrawColumnBackground -- 4933 * 4934 * Draws rows of -itembackground colors in a column in the 4935 * whitespace region. 4936 * 4937 * Results: 4938 * None. 4939 * 4940 * Side effects: 4941 * None. 4942 * 4943 *-------------------------------------------------------------- 4944 */ 4945 4946static void 4947DrawColumnBackground( 4948 TreeCtrl *tree, /* Widget info. */ 4949 Drawable drawable, /* Where to draw. */ 4950 TreeColumn treeColumn, /* Column to get background colors from. */ 4951 TkRegion dirtyRgn, /* Area that needs painting. Will be 4952 * inside 'bounds' and inside borders. */ 4953 XRectangle *bounds, /* Window coords of column to paint. */ 4954 RItem *rItem, /* Item(s) to get row heights from when drawing 4955 * in the tail column, otherwise NULL. */ 4956 int height, /* Height of each row below actual items. */ 4957 int index /* Used for alternating background colors. */ 4958 ) 4959{ 4960 int bgCount = TreeColumn_BackgroundCount(treeColumn); 4961 GC gc = None, backgroundGC; 4962 XRectangle dirtyBox, drawBox, rowBox; 4963 int top, bottom; 4964 4965 TkClipBox(dirtyRgn, &dirtyBox); 4966 if (!dirtyBox.width || !dirtyBox.height) 4967 return; 4968 4969 backgroundGC = Tk_3DBorderGC(tree->tkwin, tree->border, TK_3D_FLAT_GC); 4970 4971 /* If the column has zero -itembackground colors, paint with the 4972 * treectrl's -background color. If a single -itembackground color 4973 * is specified, then paint with it. */ 4974 if (bgCount < 2) { 4975 if (bgCount == 1) 4976 gc = TreeColumn_BackgroundGC(treeColumn, 0); 4977 if (gc == None) 4978 gc = backgroundGC; 4979 Tree_FillRegion(tree->display, drawable, gc, dirtyRgn); 4980 return; 4981 } 4982 4983 top = bounds->y; 4984 bottom = dirtyBox.y + dirtyBox.height; 4985 while (top < bottom) { 4986 /* Can't use clipping regions with XFillRectangle 4987 * because the clip region is ignored on Win32. */ 4988 rowBox.x = bounds->x; 4989 rowBox.y = top; 4990 rowBox.width = bounds->width; 4991 rowBox.height = rItem ? rItem->size : height; 4992 if (Tree_IntersectRect(&drawBox, &rowBox, &dirtyBox)) { 4993 if (rItem != NULL) { 4994 index = GetItemBgIndex(tree, rItem); 4995 } 4996 gc = TreeColumn_BackgroundGC(treeColumn, index); 4997 if (gc == None) 4998 gc = backgroundGC; 4999 XFillRectangle(tree->display, drawable, gc, 5000 drawBox.x, drawBox.y, drawBox.width, drawBox.height); 5001 } 5002 if (rItem != NULL && rItem == rItem->range->last) { 5003 index = GetItemBgIndex(tree, rItem); 5004 rItem = NULL; 5005 } 5006 if (rItem != NULL) { 5007 rItem++; 5008 } 5009 index++; 5010 top += rowBox.height; 5011 } 5012} 5013 5014/* 5015 *-------------------------------------------------------------- 5016 * 5017 * DrawWhitespaceBelowItem -- 5018 * 5019 * Draws rows of -itembackground colors in each column in the 5020 * whitespace region. 5021 * 5022 * Results: 5023 * None. 5024 * 5025 * Side effects: 5026 * None. 5027 * 5028 *-------------------------------------------------------------- 5029 */ 5030 5031static void 5032DrawWhitespaceBelowItem( 5033 TreeCtrl *tree, /* Widget info. */ 5034 Drawable drawable, /* Where to draw. */ 5035 int lock, /* Which columns to draw. */ 5036 int bounds[4], /* TREE_AREA_xxx bounds. */ 5037 int left, /* Window coord of first column's left edge. */ 5038 int top, /* Window coord just below the last item. */ 5039 TkRegion dirtyRgn, /* Area of whitespace that needs painting. */ 5040 TkRegion columnRgn, /* Existing region to set and use. */ 5041 int height, /* Height of each row. */ 5042 int index /* Used for alternating background colors. */ 5043 ) 5044{ 5045 int i = 0, width; 5046 TreeColumn treeColumn = NULL; 5047 XRectangle boundsBox, columnBox; 5048 5049 switch (lock) { 5050 case COLUMN_LOCK_LEFT: 5051 treeColumn = tree->columnLockLeft; 5052 break; 5053 case COLUMN_LOCK_NONE: 5054 treeColumn = tree->columnLockNone; 5055 break; 5056 case COLUMN_LOCK_RIGHT: 5057 treeColumn = tree->columnLockRight; 5058 break; 5059 } 5060 5061 boundsBox.x = bounds[0]; 5062 boundsBox.y = bounds[1]; 5063 boundsBox.width = bounds[2] - bounds[0]; 5064 boundsBox.height = bounds[3] - bounds[1]; 5065 5066 for (i = TreeColumn_Index(treeColumn); i < tree->columnCount; i++) { 5067 if (TreeColumn_Lock(treeColumn) != lock) 5068 break; 5069 width = TreeColumn_GetDInfo(treeColumn)->width; 5070 if (width == 0) /* also handles hidden columns */ 5071 goto next; 5072 columnBox.x = left; 5073 columnBox.y = top; 5074 columnBox.width = width; 5075 columnBox.height = bounds[3] - top; 5076 if (Tree_IntersectRect(&columnBox, &boundsBox, &columnBox)) { 5077 Tree_SetRectRegion(columnRgn, &columnBox); 5078 TkIntersectRegion(dirtyRgn, columnRgn, columnRgn); 5079 DrawColumnBackground(tree, drawable, treeColumn, 5080 columnRgn, &columnBox, (RItem *) NULL, height, index); 5081 } 5082 left += width; 5083next: 5084 treeColumn = TreeColumn_Next(treeColumn); 5085 } 5086} 5087 5088/* 5089 *-------------------------------------------------------------- 5090 * 5091 * ComplexWhitespace -- 5092 * 5093 * Return 1 if -itembackground colors should be drawn into the 5094 * whitespace region. 5095 * 5096 * Results: 5097 * None. 5098 * 5099 * Side effects: 5100 * None. 5101 * 5102 *-------------------------------------------------------------- 5103 */ 5104 5105static int 5106ComplexWhitespace( 5107 TreeCtrl *tree 5108 ) 5109{ 5110 if (tree->columnBgCnt == 0 && 5111 TreeColumn_BackgroundCount(tree->columnTail) == 0) 5112 return 0; 5113 5114#if 1 5115 if (!tree->vertical || (tree->wrapMode != TREE_WRAP_NONE) || 5116 (tree->itemWrapCount > 0)) 5117#else 5118 if (!tree->vertical || tree->wrapMode != TREE_WRAP_NONE) 5119#endif 5120 return 0; 5121 5122 if (tree->itemHeight <= 0 && tree->minItemHeight <= 0) 5123 return 0; 5124 5125 return 1; 5126} 5127 5128/* 5129 *-------------------------------------------------------------- 5130 * 5131 * DrawWhitespace -- 5132 * 5133 * Paints part of the whitespace region. 5134 * 5135 * Results: 5136 * If -itembackground colors are not being drawn into the 5137 * whitespace region, the dirtyRgn is filled with the treectrl's 5138 * -background color. Otherwise rows of color are drawn below 5139 * the last item and in the tail column if those columns have 5140 * any -itembackground colors specified. 5141 * 5142 * Side effects: 5143 * None. 5144 * 5145 *-------------------------------------------------------------- 5146 */ 5147 5148static void 5149DrawWhitespace( 5150 TreeCtrl *tree, 5151 Drawable drawable, 5152 TkRegion dirtyRgn 5153 ) 5154{ 5155 TreeDInfo dInfo = tree->dInfo; 5156 int x, y, minX, minY, maxX, maxY; 5157 int top, bottom; 5158 int height, index; 5159 XRectangle columnBox; 5160 TkRegion columnRgn; 5161 Range *range; 5162 RItem *rItem; 5163 5164 /* If we aren't drawing -itembackground colors in the whitespace region, 5165 * then just paint the entire dirty area with the treectrl's -background 5166 * color. */ 5167 if (!ComplexWhitespace(tree)) { 5168 GC gc = Tk_3DBorderGC(tree->tkwin, tree->border, TK_3D_FLAT_GC); 5169 Tree_FillRegion(tree->display, drawable, gc, dirtyRgn); 5170 return; 5171 } 5172 5173 x = 0 - tree->xOrigin; 5174 y = 0 - tree->yOrigin; 5175 5176 top = MAX(y + Tree_TotalHeight(tree), Tree_ContentTop(tree)); 5177 bottom = Tree_ContentBottom(tree); 5178 5179 /* Figure out the height of each row of color below the items. */ 5180 if (tree->backgroundMode == BG_MODE_COLUMN) 5181 height = bottom - top; /* solid block of color */ 5182 else if (tree->itemHeight > 0) 5183 height = tree->itemHeight; 5184 else 5185 height = tree->minItemHeight; 5186 5187 columnRgn = Tree_GetRegion(tree); 5188 5189 range = dInfo->rangeFirst; 5190 if (range == NULL) 5191 range = dInfo->rangeLock; 5192 5193 if (!dInfo->empty) { 5194 minX = dInfo->bounds[0]; 5195 minY = dInfo->bounds[1]; 5196 maxX = dInfo->bounds[2]; 5197 maxY = dInfo->bounds[3]; 5198 5199 /* Draw to the right of the items using the tail 5200 * column's -itembackground colors. The height of each row matches 5201 * the height of the adjacent item. */ 5202 if (x + Tree_TotalWidth(tree) < maxX) { 5203 columnBox.y = Tree_ContentTop(tree); 5204 if (range == NULL) { 5205 rItem = NULL; 5206 index = 0; 5207 } else { 5208 /* Get the item at the top of the screen. */ 5209 if (range->totalHeight == 0) { 5210 rItem = range->last; /* all items have zero height */ 5211 } else { 5212 int y2 = minY + tree->yOrigin; /* Window -> canvas */ 5213 rItem = Range_ItemUnderPoint(tree, range, NULL, &y2); 5214 columnBox.y -= y2; 5215 } 5216 index = GetItemBgIndex(tree, rItem); 5217 } 5218 columnBox.x = x + Tree_TotalWidth(tree); 5219 columnBox.width = maxX - columnBox.x; 5220 columnBox.height = maxY - columnBox.y; 5221 Tree_SetRectRegion(columnRgn, &columnBox); 5222 TkIntersectRegion(dirtyRgn, columnRgn, columnRgn); 5223 DrawColumnBackground(tree, drawable, tree->columnTail, 5224 columnRgn, &columnBox, rItem, height, index); 5225 } 5226 } 5227 5228 if (top < bottom) { 5229 5230 /* Get the display index of the last visible item. */ 5231 if (range == NULL) { 5232 index = 0; 5233 } else { 5234 rItem = range->last; 5235 index = GetItemBgIndex(tree, rItem); 5236 if (tree->backgroundMode != BG_MODE_COLUMN) { 5237 index++; 5238 } 5239 } 5240 5241 /* Draw below non-locked columns. */ 5242 if (!dInfo->empty && Tree_TotalWidth(tree)/* && dInfo->rangeFirst != NULL */) { 5243 DrawWhitespaceBelowItem(tree, drawable, COLUMN_LOCK_NONE, 5244 dInfo->bounds, x, top, dirtyRgn, columnRgn, 5245 height, index); 5246 } 5247 5248 /* Draw below the left columns. */ 5249 if (!dInfo->emptyL) { 5250 minX = dInfo->boundsL[0]; 5251 DrawWhitespaceBelowItem(tree, drawable, COLUMN_LOCK_LEFT, 5252 dInfo->boundsL, 5253 minX, top, dirtyRgn, columnRgn, 5254 height, index); 5255 } 5256 5257 /* Draw below the right columns. */ 5258 if (!dInfo->emptyR) { 5259 minX = dInfo->boundsR[0]; 5260 DrawWhitespaceBelowItem(tree, drawable, COLUMN_LOCK_RIGHT, 5261 dInfo->boundsR, 5262 minX, top, dirtyRgn, columnRgn, 5263 height, index); 5264 } 5265 } 5266 5267 Tree_FreeRegion(tree, columnRgn); 5268} 5269 5270#endif /* COMPLEX_WHITESPACE */ 5271 5272/* 5273 *---------------------------------------------------------------------- 5274 * 5275 * Tree_DrawTiledImage -- 5276 * 5277 * This procedure draws a tiled image in the indicated box. 5278 * 5279 * Results: 5280 * None. 5281 * 5282 * Side effects: 5283 * Stuff is drawn. 5284 * 5285 *---------------------------------------------------------------------- 5286 */ 5287 5288void 5289Tree_DrawTiledImage( 5290 TreeCtrl *tree, /* Widget info. */ 5291 Drawable drawable, /* Where to draw. */ 5292 Tk_Image image, /* The image to draw. */ 5293 int x1, int y1, /* Left & top of area to fill with the image. */ 5294 int x2, int y2, /* Right & bottom, of area to fill with the 5295 * image. */ 5296 int xOffset, int yOffset /* Used to keep the image aligned with an 5297 * origin. */ 5298 ) 5299{ 5300 int imgWidth, imgHeight; 5301 int srcX, srcY; 5302 int srcW, srcH; 5303 int dstX, dstY; 5304 5305 Tk_SizeOfImage(image, &imgWidth, &imgHeight); 5306 5307 /* xOffset can be < 0 for left-locked columns. */ 5308 if (xOffset < 0) xOffset = imgWidth + xOffset % imgWidth; 5309 5310 srcX = (x1 + xOffset) % imgWidth; 5311 dstX = x1; 5312 while (dstX < x2) { 5313 srcW = imgWidth - srcX; 5314 if (dstX + srcW > x2) { 5315 srcW = x2 - dstX; 5316 } 5317 5318 srcY = (y1 + yOffset) % imgHeight; 5319 dstY = y1; 5320 while (dstY < y2) { 5321 srcH = imgHeight - srcY; 5322 if (dstY + srcH > y2) { 5323 srcH = y2 - dstY; 5324 } 5325 Tk_RedrawImage(image, srcX, srcY, srcW, srcH, drawable, dstX, dstY); 5326 srcY = 0; 5327 dstY += srcH; 5328 } 5329 srcX = 0; 5330 5331 /* the last tile gives dstX == x2 which ends the while loop; same 5332 * for dstY above */ 5333 dstX += srcW; 5334 }; 5335} 5336 5337/* 5338 *---------------------------------------------------------------------- 5339 * 5340 * DisplayDItem -- 5341 * 5342 * Draw a single item. 5343 * 5344 * Results: 5345 * None. 5346 * 5347 * Side effects: 5348 * Stuff is drawn. 5349 * 5350 *---------------------------------------------------------------------- 5351 */ 5352 5353static int 5354DisplayDItem( 5355 TreeCtrl *tree, /* Widget info. */ 5356 DItem *dItem, 5357 DItemArea *area, 5358 int lock, /* Which set of columns. */ 5359 int bounds[4], /* TREE_AREA_xxx bounds of drawing. */ 5360 TreeDrawable pixmap, /* Where to draw. */ 5361 TreeDrawable drawable /* Where to copy to. */ 5362 ) 5363{ 5364 Tk_Window tkwin = tree->tkwin; 5365 int left, top, right, bottom; 5366 5367 left = area->x; 5368 right = left + area->width; 5369 top = dItem->y; 5370 bottom = top + dItem->height; 5371 5372 if (!(area->flags & DITEM_ALL_DIRTY)) { 5373 left += area->dirty[LEFT]; 5374 right = area->x + area->dirty[RIGHT]; 5375 top += area->dirty[TOP]; 5376 bottom = dItem->y + area->dirty[BOTTOM]; 5377 } 5378 5379 area->flags &= ~(DITEM_DIRTY | DITEM_ALL_DIRTY); 5380 5381 if (left < bounds[0]) 5382 left = bounds[0]; 5383 if (right > bounds[2]) 5384 right = bounds[2]; 5385 if (top < bounds[1]) 5386 top = bounds[1]; 5387 if (bottom > bounds[3]) 5388 bottom = bounds[3]; 5389 5390 if (right <= left || bottom <= top) 5391 return 0; 5392 5393 if (tree->debug.enable && tree->debug.display && tree->debug.drawColor) { 5394 XFillRectangle(tree->display, Tk_WindowId(tkwin), 5395 tree->debug.gcDraw, left, top, right - left, bottom - top); 5396 DisplayDelay(tree); 5397 } 5398 5399 if (tree->doubleBuffer != DOUBLEBUFFER_NONE) { 5400 5401 if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) { 5402 DblBufWinDirty(tree, left, top, right, bottom); 5403 } 5404 5405 /* The top-left corner of the drawable is at this 5406 * point in the canvas */ 5407 tree->drawableXOrigin = left + tree->xOrigin; 5408 tree->drawableYOrigin = top + tree->yOrigin; 5409 5410 TreeItem_Draw(tree, dItem->item, lock, 5411 area->x - left, dItem->y - top, 5412 area->width, dItem->height, 5413 pixmap, 5414 0, right - left, 5415 dItem->index); 5416 XCopyArea(tree->display, pixmap.drawable, drawable.drawable, 5417 tree->copyGC, 5418 0, 0, 5419 right - left, bottom - top, 5420 left, top); 5421 } else { 5422 5423 /* The top-left corner of the drawable is at this 5424 * point in the canvas */ 5425 tree->drawableXOrigin = tree->xOrigin; 5426 tree->drawableYOrigin = tree->yOrigin; 5427 5428 TreeItem_Draw(tree, dItem->item, 5429 lock, 5430 area->x, 5431 dItem->y, 5432 area->width, dItem->height, 5433 drawable, 5434 left, right, 5435 dItem->index); 5436 } 5437 5438#ifdef REDRAW_RGN 5439 AddRectToRedrawRgn(tree, left, top, right, bottom); 5440#endif /* REDRAW_RGN */ 5441 5442 return 1; 5443} 5444 5445static void 5446DebugDrawBorder( 5447 TreeCtrl *tree, 5448 int inset, 5449 int left, 5450 int top, 5451 int right, 5452 int bottom 5453 ) 5454{ 5455 Tk_Window tkwin = tree->tkwin; 5456 5457 if (tree->debug.enable && tree->debug.display && tree->debug.drawColor) { 5458 if (left > 0) { 5459 XFillRectangle(tree->display, Tk_WindowId(tkwin), 5460 tree->debug.gcDraw, 5461 inset, inset, 5462 left, Tk_Height(tkwin) - inset * 2); 5463 } 5464 if (top > 0) { 5465 XFillRectangle(tree->display, Tk_WindowId(tkwin), 5466 tree->debug.gcDraw, 5467 inset, inset, 5468 Tk_Width(tkwin) - inset * 2, top); 5469 } 5470 if (right > 0) { 5471 XFillRectangle(tree->display, Tk_WindowId(tkwin), 5472 tree->debug.gcDraw, 5473 Tk_Width(tkwin) - inset - right, inset, 5474 right, Tk_Height(tkwin) - inset * 2); 5475 } 5476 if (bottom > 0) { 5477 XFillRectangle(tree->display, Tk_WindowId(tkwin), 5478 tree->debug.gcDraw, 5479 inset, Tk_Height(tkwin) - inset - bottom, 5480 Tk_Width(tkwin) - inset * 2, bottom); 5481 } 5482 DisplayDelay(tree); 5483 } 5484} 5485 5486/* 5487 *-------------------------------------------------------------- 5488 * 5489 * TreeDisplay_GetReadyForTrouble -- 5490 * TreeDisplay_WasThereTrouble -- 5491 * 5492 * These 2 procedures are used to detect when something happens 5493 * during a display update that requests another display update. 5494 * If that happens, then the current display is aborted and we 5495 * try again (unless the window was destroyed). 5496 * 5497 * Results: 5498 * None. 5499 * 5500 * Side effects: 5501 * None. 5502 * 5503 *-------------------------------------------------------------- 5504 */ 5505 5506void 5507TreeDisplay_GetReadyForTrouble( 5508 TreeCtrl *tree, 5509 int *requestsPtr 5510 ) 5511{ 5512 TreeDInfo dInfo = tree->dInfo; 5513 5514 *requestsPtr = dInfo->requests; 5515} 5516 5517int 5518TreeDisplay_WasThereTrouble( 5519 TreeCtrl *tree, 5520 int requests 5521 ) 5522{ 5523 TreeDInfo dInfo = tree->dInfo; 5524 5525 if (tree->deleted || (requests != dInfo->requests)) { 5526 if (tree->debug.enable) 5527 dbwin("TreeDisplay_WasThereTrouble: %p\n", tree); 5528 return 1; 5529 } 5530 return 0; 5531} 5532 5533/* 5534 *---------------------------------------------------------------------- 5535 * 5536 * DisplayGetPixmap -- 5537 * 5538 * Allocate or reallocate a pixmap of needed size. 5539 * 5540 * Results: 5541 * None. 5542 * 5543 * Side effects: 5544 * None. 5545 * 5546 *---------------------------------------------------------------------- 5547 */ 5548 5549static Pixmap 5550DisplayGetPixmap( 5551 TreeCtrl *tree, 5552 TreeDrawable *dPixmap, 5553 int width, 5554 int height 5555 ) 5556{ 5557 Tk_Window tkwin = tree->tkwin; 5558 5559 if (dPixmap->drawable == None) { 5560 dPixmap->drawable = Tk_GetPixmap(tree->display, 5561 Tk_WindowId(tkwin), width, height, Tk_Depth(tkwin)); 5562 dPixmap->width = width; 5563 dPixmap->height = height; 5564 5565 } else if ((dPixmap->width < width) || (dPixmap->height < height)) { 5566 Tk_FreePixmap(tree->display, dPixmap->drawable); 5567 dPixmap->drawable = Tk_GetPixmap(tree->display, 5568 Tk_WindowId(tkwin), width, height, Tk_Depth(tkwin)); 5569 dPixmap->width = width; 5570 dPixmap->height = height; 5571 } 5572 return dPixmap->drawable; 5573} 5574 5575/* 5576 *---------------------------------------------------------------------- 5577 * 5578 * SetBuffering -- 5579 * 5580 * Chooses the appropriate level of offscreen buffering depending 5581 * on whether we need to draw the dragimage|marquee|proxies in non-XOR. 5582 * 5583 * Results: 5584 * tree->doubleBuffer is possibly updated. 5585 * 5586 * Side effects: 5587 * If the buffering level changes then the whole list is redrawn. 5588 * 5589 *---------------------------------------------------------------------- 5590 */ 5591 5592static void 5593SetBuffering( 5594 TreeCtrl *tree) 5595{ 5596 TreeDInfo dInfo = tree->dInfo; 5597 int overlays = FALSE; 5598 5599 if ((TreeDragImage_IsVisible(tree->dragImage) && 5600 !TreeDragImage_IsXOR(tree->dragImage)) || 5601 (TreeMarquee_IsVisible(tree->marquee) && 5602 !TreeMarquee_IsXOR(tree->marquee)) || 5603 ((tree->columnProxy.xObj || tree->rowProxy.yObj) && 5604 !Proxy_IsXOR())) { 5605 5606 overlays = TRUE; 5607 } 5608 5609 if (overlays) { 5610 tree->doubleBuffer = DOUBLEBUFFER_WINDOW; 5611 } else { 5612 tree->doubleBuffer = DOUBLEBUFFER_ITEM; 5613 } 5614 5615 if (overlays != dInfo->overlays) { 5616 dInfo->flags |= 5617 DINFO_DRAW_HEADER | 5618 DINFO_INVALIDATE | 5619 DINFO_DRAW_WHITESPACE; 5620 dInfo->overlays = overlays; 5621 } 5622} 5623 5624/* 5625 *-------------------------------------------------------------- 5626 * 5627 * Tree_Display -- 5628 * 5629 * This procedure is called at idle time when something has happened 5630 * that might require the list to be redisplayed. An effort is made 5631 * to only redraw what is needed. 5632 * 5633 * Results: 5634 * None. 5635 * 5636 * Side effects: 5637 * Stuff is drawn in the TreeCtrl window. 5638 * 5639 *-------------------------------------------------------------- 5640 */ 5641 5642static void 5643Tree_Display( 5644 ClientData clientData /* Widget info. */ 5645 ) 5646{ 5647 TreeCtrl *tree = clientData; 5648 TreeDInfo dInfo = tree->dInfo; 5649 DItem *dItem; 5650 Tk_Window tkwin = tree->tkwin; 5651 Drawable drawable; 5652 TreeDrawable tdrawable; 5653 int minX, minY, maxX, maxY; 5654 int count; 5655 int numCopy = 0, numDraw = 0; 5656 TkRegion wsRgnNew, wsRgnDif; 5657#ifdef COMPLEX_WHITESPACE 5658 int complexWhitespace; 5659#endif 5660 XRectangle wsBox; 5661 int requests; 5662 5663 if (tree->debug.enable && tree->debug.display && 0) 5664 dbwin("Tree_Display %s\n", Tk_PathName(tkwin)); 5665 5666 if (tree->deleted) { 5667 dInfo->flags &= ~(DINFO_REDRAW_PENDING); 5668 return; 5669 } 5670 5671 /* */ 5672 Tcl_Preserve((ClientData) tree); 5673 Tree_PreserveItems(tree); 5674 5675displayRetry: 5676 5677 SetBuffering(tree); 5678 5679 /* Some change requires selection changes */ 5680 if (dInfo->flags & DINFO_REDO_SELECTION) { 5681#ifdef SELECTION_VISIBLE 5682 /* Possible <Selection> event. */ 5683 Tree_DeselectHidden(tree); 5684 if (tree->deleted) 5685 goto displayExit; 5686#endif 5687 dInfo->flags &= ~(DINFO_REDO_SELECTION); 5688 } 5689 5690 /* DINFO_REDO_COLUMN_WIDTH - A column was created or deleted. */ 5691 /* DINFO_CHECK_COLUMN_WIDTH - The width, offset or visibility of one or 5692 * more columns *might* have changed. */ 5693 if (dInfo->flags & (DINFO_REDO_COLUMN_WIDTH | DINFO_CHECK_COLUMN_WIDTH)) { 5694 TreeColumn treeColumn = tree->columns; 5695 TreeColumnDInfo dColumn; 5696 int force = (dInfo->flags & DINFO_REDO_COLUMN_WIDTH) != 0; 5697 int redoRanges = force, drawItems = force, drawHeader = force; 5698 int offset, width; 5699 5700 /* Set max -itembackground as well. */ 5701 tree->columnBgCnt = 0; 5702 5703 while (treeColumn != NULL) { 5704 offset = TreeColumn_Offset(treeColumn); 5705 width = TreeColumn_UseWidth(treeColumn); 5706 dColumn = TreeColumn_GetDInfo(treeColumn); 5707 5708 /* Haven't seen this column before. */ 5709 if (dColumn == NULL) { 5710 dColumn = (TreeColumnDInfo) ckalloc(sizeof(TreeColumnDInfo_)); 5711 TreeColumn_SetDInfo(treeColumn, dColumn); 5712 if (width > 0) 5713 redoRanges = drawItems = drawHeader = TRUE; 5714 } else { 5715 /* Changes to observed width also detects column visibililty 5716 * changing. */ 5717 if (dColumn->width != width) { 5718 redoRanges = drawItems = drawHeader = TRUE; 5719 } else if ((dColumn->offset != offset) && (width > 0)) { 5720 drawItems = drawHeader = TRUE; 5721 } 5722 } 5723 dColumn->offset = offset; 5724 dColumn->width = width; 5725 if (TreeColumn_Visible(treeColumn) && 5726 (TreeColumn_BackgroundCount(treeColumn) > tree->columnBgCnt)) 5727 tree->columnBgCnt = TreeColumn_BackgroundCount(treeColumn); 5728 treeColumn = TreeColumn_Next(treeColumn); 5729 } 5730 if (redoRanges) dInfo->flags |= DINFO_REDO_RANGES | DINFO_OUT_OF_DATE; 5731 if (drawHeader) dInfo->flags |= DINFO_DRAW_HEADER; 5732 if (drawItems) dInfo->flags |= DINFO_INVALIDATE; 5733 dInfo->flags &= ~(DINFO_REDO_COLUMN_WIDTH | DINFO_CHECK_COLUMN_WIDTH); 5734 } 5735 if (dInfo->headerHeight != Tree_HeaderHeight(tree)) { 5736 dInfo->headerHeight = Tree_HeaderHeight(tree); 5737 dInfo->flags |= 5738 DINFO_OUT_OF_DATE | 5739 DINFO_SET_ORIGIN_Y | 5740 DINFO_UPDATE_SCROLLBAR_Y | 5741 DINFO_DRAW_HEADER; 5742 if (tree->vertical && (tree->wrapMode == TREE_WRAP_WINDOW)) 5743 dInfo->flags |= DINFO_REDO_RANGES; 5744 } 5745 if (dInfo->widthOfColumnsLeft != Tree_WidthOfLeftColumns(tree) || 5746 dInfo->widthOfColumnsRight != Tree_WidthOfRightColumns(tree)) { 5747 dInfo->widthOfColumnsLeft = Tree_WidthOfLeftColumns(tree); 5748 dInfo->widthOfColumnsRight = Tree_WidthOfRightColumns(tree); 5749 dInfo->flags |= 5750 DINFO_SET_ORIGIN_X | 5751 DINFO_UPDATE_SCROLLBAR_X/* | 5752 DINFO_OUT_OF_DATE | 5753 DINFO_REDO_RANGES | 5754 DINFO_DRAW_HEADER*/; 5755 } 5756 Range_RedoIfNeeded(tree); 5757 Increment_RedoIfNeeded(tree); 5758 if (dInfo->xOrigin != tree->xOrigin) { 5759 dInfo->flags |= 5760 DINFO_UPDATE_SCROLLBAR_X | 5761 DINFO_OUT_OF_DATE | 5762 DINFO_DRAW_HEADER; 5763 } 5764 if (dInfo->yOrigin != tree->yOrigin) { 5765 dInfo->flags |= 5766 DINFO_UPDATE_SCROLLBAR_Y | 5767 DINFO_OUT_OF_DATE; 5768 } 5769 if (dInfo->totalWidth != Tree_TotalWidth(tree)) { 5770 dInfo->totalWidth = Tree_TotalWidth(tree); 5771 dInfo->flags |= 5772 DINFO_SET_ORIGIN_X | 5773 DINFO_UPDATE_SCROLLBAR_X | 5774 DINFO_OUT_OF_DATE; 5775 } 5776 if (dInfo->totalHeight != Tree_TotalHeight(tree)) { 5777 dInfo->totalHeight = Tree_TotalHeight(tree); 5778 dInfo->flags |= 5779 DINFO_SET_ORIGIN_Y | 5780 DINFO_UPDATE_SCROLLBAR_Y | 5781 DINFO_OUT_OF_DATE; 5782 } 5783 if (dInfo->flags & DINFO_SET_ORIGIN_X) { 5784 Tree_SetOriginX(tree, tree->xOrigin); 5785 dInfo->flags &= ~DINFO_SET_ORIGIN_X; 5786 } 5787 if (dInfo->flags & DINFO_SET_ORIGIN_Y) { 5788 Tree_SetOriginY(tree, tree->yOrigin); 5789 dInfo->flags &= ~DINFO_SET_ORIGIN_Y; 5790 } 5791#ifdef COMPLEX_WHITESPACE 5792 /* If -itembackground colors are being drawn in the whitespace region, 5793 * then redraw all the whitespace if: 5794 * a) scrolling occurs, or 5795 * b) all the display info was marked as invalid (such as when 5796 * -itembackground colors change, or a column moves), or 5797 * c) item/column sizes change (handled by Range_RedoIfNeeded). 5798 */ 5799 complexWhitespace = ComplexWhitespace(tree); 5800 if (complexWhitespace) { 5801 if ((dInfo->xOrigin != tree->xOrigin) || 5802 (dInfo->yOrigin != tree->yOrigin) || 5803 (dInfo->flags & DINFO_INVALIDATE)) { 5804 dInfo->flags |= DINFO_DRAW_WHITESPACE; 5805 } 5806 } 5807 /* If tree->columnBgCnt was > 0 but is now 0, redraw whitespace. */ 5808 if (complexWhitespace != dInfo->complexWhitespace) { 5809 dInfo->complexWhitespace = complexWhitespace; 5810 dInfo->flags |= DINFO_DRAW_WHITESPACE; 5811 } 5812#endif 5813 /* 5814 * dInfo->requests counts the number of calls to Tree_EventuallyRedraw(). 5815 * If binding scripts do something that causes a redraw to be requested, 5816 * then we abort the current draw and start again. 5817 */ 5818 TreeDisplay_GetReadyForTrouble(tree, &requests); 5819 if (dInfo->flags & DINFO_UPDATE_SCROLLBAR_X) { 5820 /* Possible <Scroll-x> event. */ 5821 Tree_UpdateScrollbarX(tree); 5822 dInfo->flags &= ~DINFO_UPDATE_SCROLLBAR_X; 5823 } 5824 if (dInfo->flags & DINFO_UPDATE_SCROLLBAR_Y) { 5825 /* Possible <Scroll-y> event. */ 5826 Tree_UpdateScrollbarY(tree); 5827 dInfo->flags &= ~DINFO_UPDATE_SCROLLBAR_Y; 5828 } 5829 if (tree->deleted || !Tk_IsMapped(tkwin)) 5830 goto displayExit; 5831 if (TreeDisplay_WasThereTrouble(tree, requests)) { 5832 goto displayRetry; 5833 } 5834 if (dInfo->flags & DINFO_OUT_OF_DATE) { 5835 Tree_UpdateDInfo(tree); 5836 dInfo->flags &= ~DINFO_OUT_OF_DATE; 5837 } 5838 if (dInfo->flags & DINFO_INVALIDATE) { 5839 for (dItem = dInfo->dItem; dItem != NULL; dItem = dItem->next) { 5840 dItem->area.flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 5841 dItem->left.flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 5842 dItem->right.flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; 5843 } 5844 dInfo->flags &= ~DINFO_INVALIDATE; 5845 } 5846 5847 /* 5848 * When an item goes from visible to hidden, "window" elements in the 5849 * item must be hidden. An item may become hidden because of scrolling, 5850 * or because an ancestor was collapsed, or because the -visible option 5851 * of the item changed. 5852 */ 5853 { 5854 Tcl_HashEntry *hPtr; 5855 Tcl_HashSearch search; 5856 TreeItemList newV, newH; 5857 TreeItem item; 5858 int isNew, i, count; 5859 5860 TreeItemList_Init(tree, &newV, 0); 5861 TreeItemList_Init(tree, &newH, 0); 5862 5863 for (dItem = dInfo->dItem; 5864 dItem != NULL; 5865 dItem = dItem->next) { 5866 5867 hPtr = Tcl_FindHashEntry(&dInfo->itemVisHash, (char *) dItem->item); 5868 if (hPtr == NULL) { 5869 /* This item is now visible, wasn't before */ 5870 TreeItemList_Append(&newV, dItem->item); 5871 TreeItem_OnScreen(tree, dItem->item, TRUE); 5872 } 5873#ifdef DCOLUMN 5874 /* The item was onscreen and still is. Figure out which 5875 * item-columns have become visible or hidden. */ 5876 else { 5877 TrackOnScreenColumnsForItem(tree, dItem->item, hPtr); 5878 } 5879#endif /* DCOLUMN */ 5880 } 5881 5882 hPtr = Tcl_FirstHashEntry(&dInfo->itemVisHash, &search); 5883 while (hPtr != NULL) { 5884 item = (TreeItem) Tcl_GetHashKey(&dInfo->itemVisHash, hPtr); 5885 if (TreeItem_GetDInfo(tree, item) == NULL) { 5886 /* This item was visible but isn't now */ 5887 TreeItemList_Append(&newH, item); 5888 TreeItem_OnScreen(tree, item, FALSE); 5889 } 5890 hPtr = Tcl_NextHashEntry(&search); 5891 } 5892 5893 /* Remove newly-hidden items from itemVisHash */ 5894 count = TreeItemList_Count(&newH); 5895 for (i = 0; i < count; i++) { 5896 item = TreeItemList_Nth(&newH, i); 5897 hPtr = Tcl_FindHashEntry(&dInfo->itemVisHash, (char *) item); 5898#ifdef DCOLUMN 5899 TrackOnScreenColumnsForItem(tree, item, hPtr); 5900 ckfree((char *) Tcl_GetHashValue(hPtr)); 5901#endif 5902 Tcl_DeleteHashEntry(hPtr); 5903 } 5904 5905 /* Add newly-visible items to itemVisHash */ 5906 count = TreeItemList_Count(&newV); 5907 for (i = 0; i < count; i++) { 5908 item = TreeItemList_Nth(&newV, i); 5909 hPtr = Tcl_CreateHashEntry(&dInfo->itemVisHash, (char *) item, &isNew); 5910#ifdef DCOLUMN 5911 TrackOnScreenColumnsForItem(tree, item, hPtr); 5912#endif /* DCOLUMN */ 5913 } 5914 5915 /* 5916 * Generate an <ItemVisibility> event here. This can be used to set 5917 * an item's styles when the item is about to be displayed, and to 5918 * clear an item's styles when the item is no longer displayed. 5919 */ 5920 if (TreeItemList_Count(&newV) || TreeItemList_Count(&newH)) { 5921 TreeNotify_ItemVisibility(tree, &newV, &newH); 5922 } 5923 5924 TreeItemList_Free(&newV); 5925 TreeItemList_Free(&newH); 5926 5927 if (tree->deleted || !Tk_IsMapped(tkwin)) 5928 goto displayExit; 5929 5930 if (TreeDisplay_WasThereTrouble(tree, requests)) 5931 goto displayRetry; 5932 } 5933 5934 tdrawable.width = Tk_Width(tkwin); 5935 tdrawable.height = Tk_Height(tkwin); 5936 if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) { 5937 tdrawable.drawable = DisplayGetPixmap(tree, &dInfo->pixmapW, 5938 tdrawable.width, tdrawable.height); 5939 } else { 5940 tdrawable.drawable = Tk_WindowId(tkwin); 5941 } 5942 drawable = tdrawable.drawable; 5943 5944 /* XOR off */ 5945 if (Proxy_IsXOR()) 5946 TreeColumnProxy_Undisplay(tree); 5947 if (Proxy_IsXOR()) 5948 TreeRowProxy_Undisplay(tree); 5949 if (TreeDragImage_IsXOR(tree->dragImage)) 5950 TreeDragImage_Undisplay(tree->dragImage); 5951 if (TreeMarquee_IsXOR(tree->marquee)) 5952 TreeMarquee_Undisplay(tree->marquee); 5953 5954#ifdef REDRAW_RGN 5955 /* Collect all the pixels that are redrawn below into the redrawRgn. 5956 * The redrawRgn is used to clip drawing of the marquee and dragimage. */ 5957 Tree_SetEmptyRegion(dInfo->redrawRgn); 5958#endif /* REDRAW_RGN */ 5959 5960 if (dInfo->flags & DINFO_DRAW_HEADER) { 5961 if (Tree_AreaBbox(tree, TREE_AREA_HEADER, &minX, &minY, &maxX, &maxY)) { 5962 if (tree->debug.enable && tree->debug.display && tree->debug.drawColor) { 5963 XFillRectangle(tree->display, Tk_WindowId(tkwin), 5964 tree->debug.gcDraw, minX, minY, maxX - minX, maxY - minY); 5965 DisplayDelay(tree); 5966 } 5967 Tree_DrawHeader(tree, tdrawable, 0 - tree->xOrigin, Tree_HeaderTop(tree)); 5968 if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) { 5969 DblBufWinDirty(tree, minX, minY, maxX, maxY); 5970 } 5971#ifdef REDRAW_RGN 5972/* AddRectToRedrawRgn(tree, minX, minY, maxX, maxY); */ 5973#endif /* REDRAW_RGN */ 5974 } 5975 dInfo->flags &= ~DINFO_DRAW_HEADER; 5976 } 5977 5978 if (tree->vertical) { 5979 numCopy = ScrollVerticalComplex(tree); 5980 ScrollHorizontalSimple(tree); 5981 } 5982 else { 5983 ScrollVerticalSimple(tree); 5984 numCopy = ScrollHorizontalComplex(tree); 5985 } 5986 5987 /* If we scrolled, then copy the entire pixmap, plus the header 5988 * if needed. */ 5989 if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) { 5990 if ((dInfo->xOrigin != tree->xOrigin) || 5991 (dInfo->yOrigin != tree->yOrigin)) { 5992 DblBufWinDirty(tree, Tree_BorderLeft(tree), Tree_ContentTop(tree), 5993 Tree_BorderRight(tree), Tree_ContentBottom(tree)); 5994 } 5995 } 5996 5997 if (dInfo->flags & DINFO_DRAW_WHITESPACE) { 5998 Tree_SetEmptyRegion(dInfo->wsRgn); 5999 dInfo->flags &= ~DINFO_DRAW_WHITESPACE; 6000 } 6001 6002 if (tree->backgroundImage != NULL) { 6003 wsRgnNew = CalcWhiteSpaceRegion(tree); 6004 6005 /* If we scrolled, redraw entire whitespace area */ 6006 if (dInfo->xOrigin != tree->xOrigin || 6007 dInfo->yOrigin != tree->yOrigin) { 6008 wsRgnDif = wsRgnNew; 6009 } else { 6010 wsRgnDif = Tree_GetRegion(tree); 6011 TkSubtractRegion(wsRgnNew, dInfo->wsRgn, wsRgnDif); 6012 } 6013 TkClipBox(wsRgnDif, &wsBox); 6014 if ((wsBox.width > 0) && (wsBox.height > 0)) { 6015 Drawable pixmap = Tk_GetPixmap(tree->display, Tk_WindowId(tkwin), 6016 wsBox.width, wsBox.height, Tk_Depth(tkwin)); 6017 GC gc = Tk_3DBorderGC(tkwin, tree->border, TK_3D_FLAT_GC); 6018 6019 if (tree->debug.enable && tree->debug.display && tree->debug.drawColor) { 6020 Tree_FillRegion(tree->display, Tk_WindowId(tkwin), 6021 tree->debug.gcDraw, wsRgnDif); 6022 DisplayDelay(tree); 6023 } 6024 6025 /* FIXME: only if backgroundImage is transparent */ 6026 Tree_OffsetRegion(wsRgnDif, -wsBox.x, -wsBox.y); 6027 Tree_FillRegion(tree->display, pixmap, gc, wsRgnDif); 6028 Tree_OffsetRegion(wsRgnDif, wsBox.x, wsBox.y); 6029 6030/* tree->drawableXOrigin = tree->xOrigin + wsBox.x; 6031 tree->drawableYOrigin = tree->yOrigin + wsBox.y;*/ 6032 6033 Tree_DrawTiledImage(tree, pixmap, tree->backgroundImage, 6034 0, 0, wsBox.width, wsBox.height, 6035 tree->xOrigin + wsBox.x, tree->yOrigin + wsBox.y); 6036 6037 TkSetRegion(tree->display, tree->copyGC, wsRgnNew); 6038/* XSetClipOrigin(tree->display, tree->copyGC, 0, 6039 0);*/ 6040 XCopyArea(tree->display, pixmap, drawable, tree->copyGC, 6041 0, 0, wsBox.width, wsBox.height, 6042 wsBox.x, wsBox.y); 6043 XSetClipMask(tree->display, tree->copyGC, None); 6044/* XSetClipOrigin(tree->display, tree->copyGC, 0, 0);*/ 6045 6046 Tk_FreePixmap(tree->display, pixmap); 6047 6048 if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) { 6049 DblBufWinDirty(tree, wsBox.x, wsBox.y, wsBox.x + wsBox.width, 6050 wsBox.y + wsBox.height); 6051 } 6052#ifdef REDRAW_RGN 6053 AddRgnToRedrawRgn(tree, wsRgnDif); 6054#endif /* REDRAW_RGN */ 6055 } 6056 if (wsRgnDif != wsRgnNew) 6057 Tree_FreeRegion(tree, wsRgnDif); 6058 Tree_FreeRegion(tree, dInfo->wsRgn); 6059 dInfo->wsRgn = wsRgnNew; 6060 } 6061 6062 dInfo->xOrigin = tree->xOrigin; 6063 dInfo->yOrigin = tree->yOrigin; 6064 6065 /* Does this need to be here? */ 6066 dInfo->flags &= ~(DINFO_REDRAW_PENDING); 6067 6068 if (tree->backgroundImage == NULL) { 6069 /* Calculate the current whitespace region, subtract the old whitespace 6070 * region, and fill the difference with the background color. */ 6071 wsRgnNew = CalcWhiteSpaceRegion(tree); 6072 wsRgnDif = Tree_GetRegion(tree); 6073 TkSubtractRegion(wsRgnNew, dInfo->wsRgn, wsRgnDif); 6074 TkClipBox(wsRgnDif, &wsBox); 6075 if ((wsBox.width > 0) && (wsBox.height > 0)) { 6076#ifndef COMPLEX_WHITESPACE 6077 GC gc = Tk_3DBorderGC(tkwin, tree->border, TK_3D_FLAT_GC); 6078#endif 6079 if (tree->debug.enable && tree->debug.display && tree->debug.drawColor) { 6080 Tree_FillRegion(tree->display, Tk_WindowId(tkwin), 6081 tree->debug.gcDraw, wsRgnDif); 6082 DisplayDelay(tree); 6083 } 6084#ifdef COMPLEX_WHITESPACE 6085 DrawWhitespace(tree, drawable, wsRgnDif); 6086#else 6087 Tree_FillRegion(tree->display, drawable, gc, wsRgnDif); 6088#endif 6089 if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) { 6090 DblBufWinDirty(tree, wsBox.x, wsBox.y, wsBox.x + wsBox.width, 6091 wsBox.y + wsBox.height); 6092 } 6093#ifdef REDRAW_RGN 6094 AddRgnToRedrawRgn(tree, wsRgnDif); 6095#endif /* REDRAW_RGN */ 6096 } 6097 Tree_FreeRegion(tree, wsRgnDif); 6098 Tree_FreeRegion(tree, dInfo->wsRgn); 6099 dInfo->wsRgn = wsRgnNew; 6100 } 6101 6102 /* See if there are any dirty items */ 6103 count = 0; 6104 for (dItem = dInfo->dItem; 6105 dItem != NULL; 6106 dItem = dItem->next) { 6107 if ((!dInfo->empty && dInfo->rangeFirst != NULL) && 6108 (dItem->area.flags & DITEM_DIRTY)) { 6109 count++; 6110 break; 6111 } 6112 if (!dInfo->emptyL && (dItem->left.flags & DITEM_DIRTY)) { 6113 count++; 6114 break; 6115 } 6116 if (!dInfo->emptyR && (dItem->right.flags & DITEM_DIRTY)) { 6117 count++; 6118 break; 6119 } 6120 } 6121 6122 /* Display dirty items */ 6123 if (count > 0) { 6124 TreeDrawable tpixmap = tdrawable; 6125 6126 if (tree->doubleBuffer != DOUBLEBUFFER_NONE) { 6127 /* Allocate pixmap for largest item */ 6128 tpixmap.width = MIN(Tk_Width(tkwin), dInfo->itemWidth); 6129 tpixmap.height = MIN(Tk_Height(tkwin), dInfo->itemHeight); 6130 tpixmap.drawable = DisplayGetPixmap(tree, &dInfo->pixmapI, 6131 tpixmap.width, tpixmap.height); 6132 } 6133 6134 for (dItem = dInfo->dItem; 6135 dItem != NULL; 6136 dItem = dItem->next) { 6137 6138 int drawn = 0; 6139 if (!dInfo->empty && dInfo->rangeFirst != NULL) { 6140 tree->drawableXOrigin = tree->xOrigin; 6141 tree->drawableYOrigin = tree->yOrigin; 6142 TreeItem_UpdateWindowPositions(tree, dItem->item, COLUMN_LOCK_NONE, 6143 dItem->area.x, dItem->y, dItem->area.width, dItem->height); 6144 if (TreeDisplay_WasThereTrouble(tree, requests)) { 6145 if (tree->deleted || !Tk_IsMapped(tree->tkwin)) 6146 goto displayExit; 6147 goto displayRetry; 6148 } 6149 if (dItem->area.flags & DITEM_DIRTY) { 6150 drawn += DisplayDItem(tree, dItem, &dItem->area, 6151 COLUMN_LOCK_NONE, dInfo->bounds, tpixmap, tdrawable); 6152 } 6153 } 6154 if (!dInfo->emptyL) { 6155 tree->drawableXOrigin = tree->xOrigin; 6156 tree->drawableYOrigin = tree->yOrigin; 6157 TreeItem_UpdateWindowPositions(tree, dItem->item, 6158 COLUMN_LOCK_LEFT, dItem->left.x, dItem->y, 6159 dItem->left.width, dItem->height); 6160 if (TreeDisplay_WasThereTrouble(tree, requests)) { 6161 if (tree->deleted || !Tk_IsMapped(tree->tkwin)) 6162 goto displayExit; 6163 goto displayRetry; 6164 } 6165 if (dItem->left.flags & DITEM_DIRTY) { 6166 drawn += DisplayDItem(tree, dItem, &dItem->left, COLUMN_LOCK_LEFT, 6167 dInfo->boundsL, tpixmap, tdrawable); 6168 } 6169 } 6170 if (!dInfo->emptyR) { 6171 tree->drawableXOrigin = tree->xOrigin; 6172 tree->drawableYOrigin = tree->yOrigin; 6173 TreeItem_UpdateWindowPositions(tree, dItem->item, 6174 COLUMN_LOCK_RIGHT, dItem->right.x, dItem->y, 6175 dItem->right.width, dItem->height); 6176 if (TreeDisplay_WasThereTrouble(tree, requests)) { 6177 if (tree->deleted || !Tk_IsMapped(tree->tkwin)) 6178 goto displayExit; 6179 goto displayRetry; 6180 } 6181 if (dItem->right.flags & DITEM_DIRTY) { 6182 drawn += DisplayDItem(tree, dItem, &dItem->right, COLUMN_LOCK_RIGHT, 6183 dInfo->boundsR, tpixmap, tdrawable); 6184 } 6185 } 6186 numDraw += drawn ? 1 : 0; 6187 6188 dItem->oldX = dItem->area.x; /* FIXME: could have dInfo->empty */ 6189 dItem->oldY = dItem->y; 6190 dItem->oldIndex = dItem->index; 6191 } 6192 } 6193 6194 if (tree->debug.enable && tree->debug.display) 6195 dbwin("copy %d draw %d %s\n", numCopy, numDraw, Tk_PathName(tkwin)); 6196 6197#ifdef REDRAW_RGNxxx 6198 tree->drawableXOrigin = tree->xOrigin; 6199 tree->drawableYOrigin = tree->yOrigin; 6200 if (TreeDragImage_IsXOR(tree->dragImage) == FALSE) 6201 TreeDragImage_DrawClipped(tree->dragImage, tdrawable, dInfo->redrawRgn); 6202 if (TreeMarquee_IsXOR(tree->marquee) == FALSE) 6203 TreeMarquee_DrawClipped(tree->marquee, tdrawable, dInfo->redrawRgn); 6204 Tree_SetEmptyRegion(dInfo->redrawRgn); 6205#endif /* REDRAW_RGN */ 6206 6207#if 1 6208 if (dInfo->overlays) { 6209 6210 tdrawable.width = Tk_Width(tkwin); 6211 tdrawable.height = Tk_Height(tkwin); 6212 6213 if (TreeTheme_IsDesktopComposited(tree)) { 6214 tdrawable.drawable = Tk_WindowId(tkwin); 6215 } else { 6216 tdrawable.drawable = DisplayGetPixmap(tree, &dInfo->pixmapT, 6217 Tk_Width(tree->tkwin), Tk_Height(tree->tkwin)); 6218 } 6219 6220 /* Copy double-buffer */ 6221 /* FIXME: only copy what is in dirtyRgn plus overlays */ 6222 XCopyArea(tree->display, dInfo->pixmapW.drawable, 6223 tdrawable.drawable, 6224 tree->copyGC, 6225 Tree_BorderLeft(tree), Tree_BorderTop(tree), 6226 Tree_BorderRight(tree) - Tree_BorderLeft(tree), 6227 Tree_BorderBottom(tree) - Tree_BorderTop(tree), 6228 Tree_BorderLeft(tree), Tree_BorderTop(tree)); 6229 6230 /* Draw dragimage|marquee|proxies */ 6231 tree->drawableXOrigin = tree->xOrigin; 6232 tree->drawableYOrigin = tree->yOrigin; 6233 if (TreeDragImage_IsXOR(tree->dragImage) == FALSE) 6234 TreeDragImage_Draw(tree->dragImage, tdrawable); 6235 if (TreeMarquee_IsXOR(tree->marquee) == FALSE) 6236 TreeMarquee_Draw(tree->marquee, tdrawable); 6237 if (Proxy_IsXOR() == FALSE) 6238 TreeColumnProxy_Draw(tree, tdrawable); 6239 if (Proxy_IsXOR() == FALSE) 6240 TreeRowProxy_Draw(tree, tdrawable); 6241 6242 if (TreeTheme_IsDesktopComposited(tree) == FALSE) { 6243 6244 /* Copy tripple-buffer to window */ 6245 /* FIXME: only copy what is in dirtyRgn plus overlays */ 6246 XCopyArea(tree->display, dInfo->pixmapT.drawable, 6247 Tk_WindowId(tkwin), 6248 tree->copyGC, 6249 Tree_BorderLeft(tree), Tree_BorderTop(tree), 6250 Tree_BorderRight(tree) - Tree_BorderLeft(tree), 6251 Tree_BorderBottom(tree) - Tree_BorderTop(tree), 6252 Tree_BorderLeft(tree), Tree_BorderTop(tree)); 6253 } 6254 6255 Tree_SetEmptyRegion(dInfo->dirtyRgn); 6256 DisplayDelay(tree); 6257 } 6258 else 6259#endif 6260 if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) { 6261 XRectangle box; 6262 6263 drawable = Tk_WindowId(tkwin); 6264 6265 TkClipBox(dInfo->dirtyRgn, &box); 6266 if (box.width > 0 && box.height > 0) { 6267 TkSetRegion(tree->display, tree->copyGC, dInfo->dirtyRgn); 6268 XCopyArea(tree->display, dInfo->pixmapW.drawable, drawable, 6269 tree->copyGC, 6270 box.x, box.y, 6271 box.width, box.height, 6272 box.x, box.y); 6273 XSetClipMask(tree->display, tree->copyGC, None); 6274 } 6275 Tree_SetEmptyRegion(dInfo->dirtyRgn); 6276 DisplayDelay(tree); 6277 } 6278 6279 /* XOR on */ 6280 if (TreeMarquee_IsXOR(tree->marquee)) 6281 TreeMarquee_Display(tree->marquee); 6282 if (TreeDragImage_IsXOR(tree->dragImage)) 6283 TreeDragImage_Display(tree->dragImage); 6284 if (Proxy_IsXOR()) 6285 TreeRowProxy_Display(tree); 6286 if (Proxy_IsXOR()) 6287 TreeColumnProxy_Display(tree); 6288 6289 if (tree->doubleBuffer == DOUBLEBUFFER_NONE) 6290 dInfo->flags |= DINFO_DRAW_HIGHLIGHT | DINFO_DRAW_BORDER; 6291 6292 if (dInfo->flags & (DINFO_DRAW_BORDER | DINFO_DRAW_HIGHLIGHT)) { 6293 drawable = Tk_WindowId(tkwin); 6294 if (tree->useTheme && TreeTheme_DrawBorders(tree, drawable) == TCL_OK) { 6295 /* nothing */ 6296 } else { 6297 6298 /* Draw focus rectangle (outside of 3D-border) */ 6299 if ((dInfo->flags & DINFO_DRAW_HIGHLIGHT) && 6300 (tree->highlightWidth > 0)) { 6301 GC fgGC, bgGC; 6302 6303 DebugDrawBorder(tree, 0, tree->highlightWidth, 6304 tree->highlightWidth, tree->highlightWidth, 6305 tree->highlightWidth); 6306 6307 bgGC = Tk_GCForColor(tree->highlightBgColorPtr, drawable); 6308 if (tree->gotFocus) 6309 fgGC = Tk_GCForColor(tree->highlightColorPtr, drawable); 6310 else 6311 fgGC = bgGC; 6312 TkpDrawHighlightBorder(tkwin, fgGC, bgGC, tree->highlightWidth, 6313 drawable); 6314 dInfo->flags &= ~DINFO_DRAW_HIGHLIGHT; 6315 } 6316 6317 /* Draw 3D-border (inside of focus rectangle) */ 6318 if ((dInfo->flags & DINFO_DRAW_BORDER) && (tree->borderWidth > 0)) { 6319 DebugDrawBorder(tree, tree->highlightWidth, 6320 tree->borderWidth, tree->borderWidth, 6321 tree->borderWidth, tree->borderWidth); 6322 6323 Tk_Draw3DRectangle(tkwin, drawable, tree->border, 6324 tree->highlightWidth, tree->highlightWidth, 6325 Tk_Width(tkwin) - tree->highlightWidth * 2, 6326 Tk_Height(tkwin) - tree->highlightWidth * 2, 6327 tree->borderWidth, tree->relief); 6328 dInfo->flags &= ~DINFO_DRAW_BORDER; 6329 } 6330 } 6331 dInfo->flags &= ~(DINFO_DRAW_BORDER | DINFO_DRAW_HIGHLIGHT); 6332 } 6333 6334displayExit: 6335 dInfo->flags &= ~(DINFO_REDRAW_PENDING); 6336 Tree_ReleaseItems(tree); 6337 Tcl_Release((ClientData) tree); 6338} 6339 6340/* 6341 *-------------------------------------------------------------- 6342 * 6343 * A_IncrementFindX -- 6344 * 6345 * Return a horizontal scroll position nearest to the given 6346 * offset. 6347 * 6348 * Results: 6349 * Index of the nearest increment <= the given offset. 6350 * 6351 * Side effects: 6352 * None. 6353 * 6354 *-------------------------------------------------------------- 6355 */ 6356 6357static int 6358A_IncrementFindX( 6359 TreeCtrl *tree, /* Widget info. */ 6360 int offset /* Canvas x-coordinate. */ 6361 ) 6362{ 6363 int totWidth = Tree_TotalWidth(tree); 6364 int xIncr = tree->xScrollIncrement; 6365 int index, indexMax; 6366 6367 indexMax = totWidth / xIncr; 6368 if (totWidth % xIncr == 0) 6369 indexMax--; 6370 if (offset < 0) 6371 offset = 0; 6372 index = offset / xIncr; 6373 if (index > indexMax) 6374 index = indexMax; 6375 return index; 6376} 6377 6378/* 6379 *-------------------------------------------------------------- 6380 * 6381 * A_IncrementFindY -- 6382 * 6383 * Return a vertical scroll position nearest to the given 6384 * offset. 6385 * 6386 * Results: 6387 * Index of the nearest increment <= the given offset. 6388 * 6389 * Side effects: 6390 * None. 6391 * 6392 *-------------------------------------------------------------- 6393 */ 6394 6395static int 6396A_IncrementFindY( 6397 TreeCtrl *tree, /* Widget info. */ 6398 int offset /* Canvas y-coordinate. */ 6399 ) 6400{ 6401 int totHeight = Tree_TotalHeight(tree); 6402 int yIncr = tree->yScrollIncrement; 6403 int index, indexMax; 6404 6405 indexMax = totHeight / yIncr; 6406 if (totHeight % yIncr == 0) 6407 indexMax--; 6408 if (offset < 0) 6409 offset = 0; 6410 index = offset / yIncr; 6411 if (index > indexMax) 6412 index = indexMax; 6413 return index; 6414} 6415 6416/* 6417 *-------------------------------------------------------------- 6418 * 6419 * Increment_FindX -- 6420 * 6421 * Return a horizontal scroll position nearest to the given 6422 * offset. 6423 * 6424 * Results: 6425 * Index of the nearest increment <= the given offset. 6426 * 6427 * Side effects: 6428 * None. 6429 * 6430 *-------------------------------------------------------------- 6431 */ 6432 6433int 6434Increment_FindX( 6435 TreeCtrl *tree, /* Widget info. */ 6436 int offset /* Canvas x-coordinate. */ 6437 ) 6438{ 6439 if (tree->xScrollIncrement <= 0) { 6440 Increment_RedoIfNeeded(tree); 6441 return B_IncrementFindX(tree, offset); 6442 } 6443 return A_IncrementFindX(tree, offset); 6444} 6445 6446/* 6447 *-------------------------------------------------------------- 6448 * 6449 * Increment_FindY -- 6450 * 6451 * Return a vertical scroll position nearest to the given 6452 * offset. 6453 * 6454 * Results: 6455 * Index of the nearest increment <= the given offset. 6456 * 6457 * Side effects: 6458 * None. 6459 * 6460 *-------------------------------------------------------------- 6461 */ 6462 6463int 6464Increment_FindY( 6465 TreeCtrl *tree, /* Widget info. */ 6466 int offset /* Canvas y-coordinate. */ 6467 ) 6468{ 6469 if (tree->yScrollIncrement <= 0) { 6470 Increment_RedoIfNeeded(tree); 6471 return B_IncrementFindY(tree, offset); 6472 } 6473 return A_IncrementFindY(tree, offset); 6474} 6475 6476/* 6477 *-------------------------------------------------------------- 6478 * 6479 * Increment_ToOffsetX -- 6480 * 6481 * Return the canvas coordinate for a scroll position. 6482 * 6483 * Results: 6484 * Pixel distance. 6485 * 6486 * Side effects: 6487 * None. 6488 * 6489 *-------------------------------------------------------------- 6490 */ 6491 6492int 6493Increment_ToOffsetX( 6494 TreeCtrl *tree, /* Widget info. */ 6495 int index /* Index of the increment. */ 6496 ) 6497{ 6498 TreeDInfo dInfo = tree->dInfo; 6499 6500 if (tree->xScrollIncrement <= 0) { 6501 if (index < 0 || index >= dInfo->xScrollIncrementCount) 6502 panic("Increment_ToOffsetX: bad index %d (must be 0-%d)", 6503 index, dInfo->xScrollIncrementCount-1); 6504 return dInfo->xScrollIncrements[index]; 6505 } 6506 return index * tree->xScrollIncrement; 6507} 6508 6509/* 6510 *-------------------------------------------------------------- 6511 * 6512 * Increment_ToOffsetY -- 6513 * 6514 * Return the canvas coordinate for a scroll position. 6515 * 6516 * Results: 6517 * Pixel distance. 6518 * 6519 * Side effects: 6520 * None. 6521 * 6522 *-------------------------------------------------------------- 6523 */ 6524 6525int 6526Increment_ToOffsetY( 6527 TreeCtrl *tree, /* Widget info. */ 6528 int index /* Index of the increment. */ 6529 ) 6530{ 6531 TreeDInfo dInfo = tree->dInfo; 6532 6533 if (tree->yScrollIncrement <= 0) { 6534 if (index < 0 || index >= dInfo->yScrollIncrementCount) { 6535 panic("Increment_ToOffsetY: bad index %d (must be 0-%d)\ntotHeight %d visHeight %d", 6536 index, dInfo->yScrollIncrementCount - 1, 6537 Tree_TotalHeight(tree), Tree_ContentHeight(tree)); 6538 } 6539 return dInfo->yScrollIncrements[index]; 6540 } 6541 return index * tree->yScrollIncrement; 6542} 6543 6544/* 6545 *-------------------------------------------------------------- 6546 * 6547 * GetScrollFractions -- 6548 * 6549 * Return the fractions that may be passed to a scrollbar "set" 6550 * command. 6551 * 6552 * Results: 6553 * Two fractions from 0.0 to 1.0. 6554 * 6555 * Side effects: 6556 * None. 6557 * 6558 *-------------------------------------------------------------- 6559 */ 6560 6561static void 6562GetScrollFractions( 6563 int screen1, int screen2, /* Min/max coordinates that are visible in 6564 * the window. */ 6565 int object1, int object2, /* Min/max coordinates of the scrollable 6566 * content (usually 0 to N where N is the 6567 * total width or height of the canvas). */ 6568 double fractions[2] /* Returned values. */ 6569 ) 6570{ 6571 double range, f1, f2; 6572 6573 range = object2 - object1; 6574 if (range <= 0) { 6575 f1 = 0; 6576 f2 = 1.0; 6577 } 6578 else { 6579 f1 = (screen1 - object1) / range; 6580 if (f1 < 0) 6581 f1 = 0.0; 6582 f2 = (screen2 - object1) / range; 6583 if (f2 > 1.0) 6584 f2 = 1.0; 6585 if (f2 < f1) 6586 f2 = f1; 6587 } 6588 6589 fractions[0] = f1; 6590 fractions[1] = f2; 6591} 6592 6593/* 6594 *-------------------------------------------------------------- 6595 * 6596 * Tree_GetScrollFractionsX -- 6597 * 6598 * Return the fractions that may be passed to a scrollbar "set" 6599 * command for a horizontal scrollbar. 6600 * 6601 * Results: 6602 * Two fractions from 0 to 1.0. 6603 * 6604 * Side effects: 6605 * None. 6606 * 6607 *-------------------------------------------------------------- 6608 */ 6609 6610void 6611Tree_GetScrollFractionsX( 6612 TreeCtrl *tree, /* Widget info. */ 6613 double fractions[2] /* Returned values. */ 6614 ) 6615{ 6616 int left = tree->xOrigin + Tree_ContentLeft(tree); 6617 int visWidth = Tree_ContentWidth(tree); 6618 int totWidth = Tree_TotalWidth(tree); 6619 int index, offset; 6620 6621 /* The tree is empty, or everything fits in the window */ 6622 if (visWidth < 0) 6623 visWidth = 0; 6624 if (totWidth <= visWidth) { 6625 fractions[0] = 0.0; 6626 fractions[1] = 1.0; 6627 return; 6628 } 6629 6630 if (visWidth <= 1) { 6631 GetScrollFractions(left, left + 1, 0, totWidth, fractions); 6632 return; 6633 } 6634 6635 /* Find incrementLeft when scrolled to extreme right */ 6636 index = Increment_FindX(tree, totWidth - visWidth); 6637 offset = Increment_ToOffsetX(tree, index); 6638 if (offset < totWidth - visWidth) { 6639 index++; 6640 offset = Increment_ToOffsetX(tree, index); 6641 } 6642 6643 /* Add some fake content to right */ 6644 if (offset + visWidth > totWidth) 6645 totWidth = offset + visWidth; 6646 6647 GetScrollFractions(left, left + visWidth, 0, totWidth, fractions); 6648} 6649 6650/* 6651 *-------------------------------------------------------------- 6652 * 6653 * Tree_GetScrollFractionsY -- 6654 * 6655 * Return the fractions that may be passed to a scrollbar "set" 6656 * command for a vertical scrollbar. 6657 * 6658 * Results: 6659 * Two fractions from 0 to 1.0. 6660 * 6661 * Side effects: 6662 * None. 6663 * 6664 *-------------------------------------------------------------- 6665 */ 6666 6667void 6668Tree_GetScrollFractionsY( 6669 TreeCtrl *tree, /* Widget info. */ 6670 double fractions[2] /* Returned values. */ 6671 ) 6672{ 6673 int top = Tree_ContentTop(tree) + tree->yOrigin; /* canvas coords */ 6674 int visHeight = Tree_ContentHeight(tree); 6675 int totHeight = Tree_TotalHeight(tree); 6676 int index, offset; 6677 6678 /* The tree is empty, or everything fits in the window */ 6679 if (visHeight < 0) 6680 visHeight = 0; 6681 if (totHeight <= visHeight) { 6682 fractions[0] = 0.0; 6683 fractions[1] = 1.0; 6684 return; 6685 } 6686 6687 if (visHeight <= 1) { 6688 GetScrollFractions(top, top + 1, 0, totHeight, fractions); 6689 return; 6690 } 6691 6692 /* Find incrementTop when scrolled to bottom */ 6693 index = Increment_FindY(tree, totHeight - visHeight); 6694 offset = Increment_ToOffsetY(tree, index); 6695 if (offset < totHeight - visHeight) { 6696 index++; 6697 offset = Increment_ToOffsetY(tree, index); 6698 } 6699 6700 /* Add some fake content to bottom */ 6701 if (offset + visHeight > totHeight) 6702 totHeight = offset + visHeight; 6703 6704 GetScrollFractions(top, top + visHeight, 0, totHeight, fractions); 6705} 6706 6707/* 6708 *-------------------------------------------------------------- 6709 * 6710 * Tree_SetOriginX -- 6711 * 6712 * Change the horizontal scroll position. 6713 * 6714 * Results: 6715 * None. 6716 * 6717 * Side effects: 6718 * If the horizontal scroll position changes, then the widget is 6719 * redisplayed at idle time. 6720 * 6721 *-------------------------------------------------------------- 6722 */ 6723 6724void 6725Tree_SetOriginX( 6726 TreeCtrl *tree, /* Widget info. */ 6727 int xOrigin /* The desired offset from the left edge 6728 * of the window to the left edge of the 6729 * canvas. The actual value will be clipped 6730 * to the nearest scroll increment. */ 6731 ) 6732{ 6733 TreeDInfo dInfo = tree->dInfo; 6734 int totWidth = Tree_TotalWidth(tree); 6735 int visWidth = Tree_ContentWidth(tree); 6736 int index, indexMax, offset; 6737 6738 /* The tree is empty, or everything fits in the window */ 6739 if (visWidth < 0) 6740 visWidth = 0; 6741 if (totWidth <= visWidth) { 6742 xOrigin = 0 - Tree_ContentLeft(tree); 6743 if (xOrigin != tree->xOrigin) { 6744 tree->xOrigin = xOrigin; 6745 dInfo->incrementLeft = 0; 6746 Tree_EventuallyRedraw(tree); 6747 } 6748 return; 6749 } 6750 6751 if (visWidth > 1) { 6752 /* Find incrementLeft when scrolled to extreme right */ 6753 indexMax = Increment_FindX(tree, totWidth - visWidth); 6754 offset = Increment_ToOffsetX(tree, indexMax); 6755 if (offset < totWidth - visWidth) { 6756 indexMax++; 6757 offset = Increment_ToOffsetX(tree, indexMax); 6758 } 6759 6760 /* Add some fake content to right */ 6761 if (offset + visWidth > totWidth) 6762 totWidth = offset + visWidth; 6763 } else 6764 indexMax = Increment_FindX(tree, totWidth); 6765 6766 xOrigin += Tree_ContentLeft(tree); /* origin -> canvas */ 6767 index = Increment_FindX(tree, xOrigin); 6768 6769 /* Don't scroll too far left */ 6770 if (index < 0) 6771 index = 0; 6772 6773 /* Don't scroll too far right */ 6774 if (index > indexMax) 6775 index = indexMax; 6776 6777 offset = Increment_ToOffsetX(tree, index); 6778 xOrigin = offset - Tree_ContentLeft(tree); 6779 6780 if (xOrigin == tree->xOrigin) 6781 return; 6782 6783 tree->xOrigin = xOrigin; 6784 dInfo->incrementLeft = index; 6785 6786 Tree_EventuallyRedraw(tree); 6787} 6788 6789/* 6790 *-------------------------------------------------------------- 6791 * 6792 * Tree_SetOriginY -- 6793 * 6794 * Change the vertical scroll position. 6795 * 6796 * Results: 6797 * None. 6798 * 6799 * Side effects: 6800 * If the vertical scroll position changes, then the widget is 6801 * redisplayed at idle time. 6802 * 6803 *-------------------------------------------------------------- 6804 */ 6805 6806void 6807Tree_SetOriginY( 6808 TreeCtrl *tree, /* Widget info. */ 6809 int yOrigin /* The desired offset from the top edge 6810 * of the window to the top edge of the 6811 * canvas. The actual value will be clipped 6812 * to the nearest scroll increment. */ 6813 ) 6814{ 6815 TreeDInfo dInfo = tree->dInfo; 6816 int visHeight = Tree_ContentHeight(tree); 6817 int totHeight = Tree_TotalHeight(tree); 6818 int index, indexMax, offset; 6819 6820 /* The tree is empty, or everything fits in the window */ 6821 if (visHeight < 0) 6822 visHeight = 0; 6823 if (totHeight <= visHeight) { 6824 yOrigin = 0 - Tree_ContentTop(tree); 6825 if (yOrigin != tree->yOrigin) { 6826 tree->yOrigin = yOrigin; 6827 dInfo->incrementTop = 0; 6828 Tree_EventuallyRedraw(tree); 6829 } 6830 return; 6831 } 6832 6833 if (visHeight > 1) { 6834 /* Find incrementTop when scrolled to bottom */ 6835 indexMax = Increment_FindY(tree, totHeight - visHeight); 6836 offset = Increment_ToOffsetY(tree, indexMax); 6837 if (offset < totHeight - visHeight) { 6838 indexMax++; 6839 offset = Increment_ToOffsetY(tree, indexMax); 6840 } 6841 6842 /* Add some fake content to bottom */ 6843 if (offset + visHeight > totHeight) 6844 totHeight = offset + visHeight; 6845 } else 6846 indexMax = Increment_FindY(tree, totHeight); 6847 6848 yOrigin += Tree_ContentTop(tree); /* origin -> canvas */ 6849 index = Increment_FindY(tree, yOrigin); 6850 6851 /* Don't scroll too far up */ 6852 if (index < 0) 6853 index = 0; 6854 6855 /* Don't scroll too far down */ 6856 if (index > indexMax) 6857 index = indexMax; 6858 6859 offset = Increment_ToOffsetY(tree, index); 6860 yOrigin = offset - Tree_ContentTop(tree); 6861 if (yOrigin == tree->yOrigin) 6862 return; 6863 6864 tree->yOrigin = yOrigin; 6865 dInfo->incrementTop = index; 6866 6867 Tree_EventuallyRedraw(tree); 6868} 6869 6870/* 6871 *-------------------------------------------------------------- 6872 * 6873 * Tree_GetOriginX -- 6874 * 6875 * Return the horizontal scroll position. 6876 * 6877 * Results: 6878 * None. 6879 * 6880 * Side effects: 6881 * May update the horizontal scroll position. 6882 * If the horizontal scroll position changes, then the widget is 6883 * redisplayed at idle time. 6884 * 6885 *-------------------------------------------------------------- 6886 */ 6887 6888int 6889Tree_GetOriginX( 6890 TreeCtrl *tree /* Widget info. */ 6891 ) 6892{ 6893 /* Update the value if needed. */ 6894 Tree_SetOriginX(tree, tree->xOrigin); 6895 6896 return tree->xOrigin; 6897} 6898 6899/* 6900 *-------------------------------------------------------------- 6901 * 6902 * Tree_GetOriginY -- 6903 * 6904 * Return the vertical scroll position. 6905 * 6906 * Results: 6907 * None. 6908 * 6909 * Side effects: 6910 * May update the vertical scroll position. 6911 * If the vertical scroll position changes, then the widget is 6912 * redisplayed at idle time. 6913 * 6914 *-------------------------------------------------------------- 6915 */ 6916 6917int 6918Tree_GetOriginY( 6919 TreeCtrl *tree /* Widget info. */ 6920 ) 6921{ 6922 /* Update the value if needed. */ 6923 Tree_SetOriginY(tree, tree->yOrigin); 6924 6925 return tree->yOrigin; 6926} 6927 6928/* 6929 *-------------------------------------------------------------- 6930 * 6931 * Tree_EventuallyRedraw -- 6932 * 6933 * Schedule an idle task to redisplay the widget, if one is not 6934 * already scheduled and the widget is mapped and the widget 6935 * hasn't been deleted. 6936 * 6937 * Results: 6938 * None. 6939 * 6940 * Side effects: 6941 * The widget may be redisplayed at idle time. 6942 * 6943 *-------------------------------------------------------------- 6944 */ 6945 6946void 6947Tree_EventuallyRedraw( 6948 TreeCtrl *tree /* Widget info. */ 6949 ) 6950{ 6951 TreeDInfo dInfo = tree->dInfo; 6952 6953 dInfo->requests++; 6954 if ((dInfo->flags & DINFO_REDRAW_PENDING) || 6955 tree->deleted || 6956 !Tk_IsMapped(tree->tkwin)) { 6957 return; 6958 } 6959 dInfo->flags |= DINFO_REDRAW_PENDING; 6960 Tcl_DoWhenIdle(Tree_Display, (ClientData) tree); 6961} 6962 6963/* 6964 *-------------------------------------------------------------- 6965 * 6966 * Tree_RelayoutWindow -- 6967 * 6968 * Invalidate all the layout info for the widget and schedule a 6969 * redisplay at idle time. This gets called when certain config 6970 * options change and when the size of the widget changes. 6971 * 6972 * Results: 6973 * None. 6974 * 6975 * Side effects: 6976 * The widget will be redisplayed at idle time. 6977 * 6978 *-------------------------------------------------------------- 6979 */ 6980 6981void 6982Tree_RelayoutWindow( 6983 TreeCtrl *tree /* Widget info. */ 6984 ) 6985{ 6986 TreeDInfo dInfo = tree->dInfo; 6987 6988 FreeDItems(tree, dInfo->dItem, NULL, 0); 6989 dInfo->dItem = NULL; 6990 dInfo->flags |= 6991 DINFO_REDO_RANGES | 6992 DINFO_OUT_OF_DATE | 6993 DINFO_CHECK_COLUMN_WIDTH | 6994 DINFO_DRAW_HEADER | 6995 DINFO_DRAW_HIGHLIGHT | 6996 DINFO_DRAW_BORDER | 6997 DINFO_SET_ORIGIN_X | 6998 DINFO_SET_ORIGIN_Y | 6999 DINFO_UPDATE_SCROLLBAR_X | 7000 DINFO_UPDATE_SCROLLBAR_Y; 7001 dInfo->xOrigin = tree->xOrigin; 7002 dInfo->yOrigin = tree->yOrigin; 7003 7004 /* Needed if -background color changes. */ 7005 dInfo->flags |= DINFO_DRAW_WHITESPACE; 7006 7007 if (tree->doubleBuffer != DOUBLEBUFFER_WINDOW) { 7008 if (dInfo->pixmapW.drawable != None) { 7009 Tk_FreePixmap(tree->display, dInfo->pixmapW.drawable); 7010 dInfo->pixmapW.drawable = None; 7011 } 7012 } 7013 if (tree->doubleBuffer == DOUBLEBUFFER_NONE) { 7014 if (dInfo->pixmapI.drawable != None) { 7015 Tk_FreePixmap(tree->display, dInfo->pixmapI.drawable); 7016 dInfo->pixmapI.drawable = None; 7017 } 7018 } 7019 7020 if (tree->useTheme) { 7021 TreeTheme_Relayout(tree); 7022 TreeTheme_SetBorders(tree); 7023 } 7024 7025 Tree_EventuallyRedraw(tree); 7026} 7027 7028/* 7029 *-------------------------------------------------------------- 7030 * 7031 * Tree_FocusChanged -- 7032 * 7033 * This procedure handles the widget gaining or losing the input 7034 * focus. The state of every item has STATE_FOCUS toggled on or 7035 * off. 7036 * 7037 * Results: 7038 * None. 7039 * 7040 * Side effects: 7041 * The widget may be redisplayed at idle time if -highlightthickness 7042 * is > 0, or if any Elements change appearance because of the 7043 * state change. 7044 * 7045 *-------------------------------------------------------------- 7046 */ 7047 7048void 7049Tree_FocusChanged( 7050 TreeCtrl *tree, /* Widget info. */ 7051 int gotFocus /* TRUE if the widget has the focus, 7052 * otherwise FALSE. */ 7053 ) 7054{ 7055 TreeDInfo dInfo = tree->dInfo; 7056 TreeItem item; 7057 Tcl_HashEntry *hPtr; 7058 Tcl_HashSearch search; 7059 int stateOn, stateOff; 7060 7061 tree->gotFocus = gotFocus; 7062 7063 if (gotFocus) 7064 stateOff = 0, stateOn = STATE_FOCUS; 7065 else 7066 stateOff = STATE_FOCUS, stateOn = 0; 7067 7068 /* Slow. Change state of every item */ 7069 hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); 7070 while (hPtr != NULL) { 7071 item = (TreeItem) Tcl_GetHashValue(hPtr); 7072 TreeItem_ChangeState(tree, item, stateOff, stateOn); 7073 hPtr = Tcl_NextHashEntry(&search); 7074 } 7075 7076#ifdef USE_TTK 7077 dInfo->flags |= DINFO_DRAW_HIGHLIGHT; 7078 Tree_EventuallyRedraw(tree); 7079#else 7080 if (tree->highlightWidth > 0) { 7081 dInfo->flags |= DINFO_DRAW_HIGHLIGHT; 7082 Tree_EventuallyRedraw(tree); 7083 } 7084#endif 7085} 7086 7087/* 7088 *-------------------------------------------------------------- 7089 * 7090 * Tree_Activate -- 7091 * 7092 * This procedure handles the widget's toplevel being the "active" 7093 * foreground window (on Macintosh and Windows). Currently it just 7094 * redraws the header if -usetheme is TRUE and the header is 7095 * visible. 7096 * 7097 * Results: 7098 * None. 7099 * 7100 * Side effects: 7101 * The widget may be redisplayed at idle time. 7102 * 7103 *-------------------------------------------------------------- 7104 */ 7105 7106void 7107Tree_Activate( 7108 TreeCtrl *tree, /* Widget info. */ 7109 int isActive /* TRUE if the widget's toplevel is the 7110 * active window, otherwise FALSE. */ 7111 ) 7112{ 7113 TreeDInfo dInfo = tree->dInfo; 7114 7115 tree->isActive = isActive; 7116 7117 /* TODO: Like Tree_FocusChanged, change state of every item. */ 7118 /* Would need a new item state STATE_ACTIVEWINDOW or something. */ 7119 /* Would want to merge this with Tree_FocusChanged code to avoid 7120 * double-iteration of items. */ 7121 7122 /* Aqua column header looks different when window is not active */ 7123 if (tree->useTheme && tree->showHeader) { 7124 dInfo->flags |= DINFO_DRAW_HEADER; 7125 Tree_EventuallyRedraw(tree); 7126 } 7127} 7128 7129/* 7130 *-------------------------------------------------------------- 7131 * 7132 * Tree_FreeItemDInfo -- 7133 * 7134 * Free any DItem associated with each item in a range of items. 7135 * This is called when the size of an item changed or an item is 7136 * deleted. 7137 * 7138 * Results: 7139 * None. 7140 * 7141 * Side effects: 7142 * The widget will be redisplayed at idle time. 7143 * 7144 *-------------------------------------------------------------- 7145 */ 7146 7147void 7148Tree_FreeItemDInfo( 7149 TreeCtrl *tree, /* Widget info. */ 7150 TreeItem item1, /* First item in the range. */ 7151 TreeItem item2 /* Last item in the range, or NULL. */ 7152 ) 7153{ 7154 TreeDInfo dInfo = tree->dInfo; 7155 DItem *dItem; 7156 TreeItem item = item1; 7157 int changed = 0; 7158 7159 while (item != NULL) { 7160 dItem = (DItem *) TreeItem_GetDInfo(tree, item); 7161 if (dItem != NULL) { 7162 FreeDItems(tree, dItem, dItem->next, 1); 7163 changed = 1; 7164 } 7165 if (item == item2 || item2 == NULL) 7166 break; 7167 item = TreeItem_Next(tree, item); 7168 } 7169 changed = 1; 7170 if (changed) { 7171 dInfo->flags |= DINFO_OUT_OF_DATE; 7172 Tree_EventuallyRedraw(tree); 7173 } 7174} 7175 7176/* 7177 *-------------------------------------------------------------- 7178 * 7179 * Tree_InvalidateItemDInfo -- 7180 * 7181 * Mark as dirty any DItem associated with each item in a range 7182 * of items. This is called when the appearance of an item changed 7183 * (but not its size). 7184 * 7185 * Results: 7186 * None. 7187 * 7188 * Side effects: 7189 * The widget will be redisplayed at idle time if any of the items 7190 * had a DItem. 7191 * 7192 *-------------------------------------------------------------- 7193 */ 7194 7195void 7196Tree_InvalidateItemDInfo( 7197 TreeCtrl *tree, /* Widget info. */ 7198 TreeColumn column, /* Column to invalidate, or NULL for all. */ 7199 TreeItem item1, /* First item in the range. */ 7200 TreeItem item2 /* Last item in the range, or NULL. */ 7201 ) 7202{ 7203 TreeDInfo dInfo = tree->dInfo; 7204 TreeColumn column2; 7205 DItem *dItem; 7206 TreeItem item = item1; 7207 int changed = 0; 7208 7209 if (dInfo->flags & (DINFO_INVALIDATE | DINFO_REDO_COLUMN_WIDTH)) 7210 return; 7211 7212 while (item != NULL) { 7213 dItem = (DItem *) TreeItem_GetDInfo(tree, item); 7214 if ((dItem == NULL) || DItemAllDirty(tree, dItem)) 7215 goto next; 7216 7217 if (column == NULL) { 7218 dItem->area.flags |= (DITEM_DIRTY | DITEM_ALL_DIRTY); 7219 dItem->left.flags |= (DITEM_DIRTY | DITEM_ALL_DIRTY); 7220 dItem->right.flags |= (DITEM_DIRTY | DITEM_ALL_DIRTY); 7221 changed = 1; 7222 } else { 7223 TreeColumnDInfo dColumn = TreeColumn_GetDInfo(column); 7224 int columnIndex, left, width, i; 7225 DItemArea *area = NULL; 7226 7227 switch (TreeColumn_Lock(column)) { 7228 case COLUMN_LOCK_NONE: 7229 area = &dItem->area; 7230 break; 7231 case COLUMN_LOCK_LEFT: 7232 area = &dItem->left; 7233 break; 7234 case COLUMN_LOCK_RIGHT: 7235 area = &dItem->right; 7236 break; 7237 } 7238 7239 if (area->flags & DITEM_ALL_DIRTY) 7240 goto next; 7241 7242 columnIndex = TreeColumn_Index(column); 7243 left = dColumn->offset; 7244 7245 /* If only one column is visible, the width may be 7246 * different than the column width. */ 7247 if ((TreeColumn_Lock(column) == COLUMN_LOCK_NONE) && 7248 (tree->columnCountVis == 1)) { 7249 width = area->width; 7250 7251 /* All spans are 1. */ 7252 } else if (dItem->spans == NULL) { 7253 width = dColumn->width; 7254 7255 /* If the column being redrawn is not the first in the span, 7256 * then do nothing. */ 7257 } else if (columnIndex != dItem->spans[columnIndex]) { 7258 goto next; 7259 7260 /* Calculate the width of the entire span. */ 7261 /* Do NOT call TreeColumn_UseWidth() or another routine 7262 * that calls Tree_WidthOfColumns() because that may end 7263 * up recalculating the size of items whose display info 7264 * is currently being invalidated. */ 7265 } else { 7266 width = 0; 7267 column2 = column; 7268 i = columnIndex; 7269 while (dItem->spans[i] == columnIndex) { 7270 width += TreeColumn_GetDInfo(column2)->width; 7271 if (++i == tree->columnCount) 7272 break; 7273 column2 = TreeColumn_Next(column2); 7274 } 7275 } 7276 7277 if (width > 0) { 7278 InvalidateDItemX(dItem, area, 0, left, width); 7279 InvalidateDItemY(dItem, area, 0, 0, dItem->height); 7280 area->flags |= DITEM_DIRTY; 7281 changed = 1; 7282 } 7283 } 7284next: 7285 if (item == item2 || item2 == NULL) 7286 break; 7287 item = TreeItem_Next(tree, item); 7288 } 7289 if (changed) { 7290 Tree_EventuallyRedraw(tree); 7291 } 7292} 7293 7294/* 7295 *-------------------------------------------------------------- 7296 * 7297 * TreeDisplay_ItemDeleted -- 7298 * 7299 * Removes an item from the hash table of on-screen items. 7300 * 7301 * Results: 7302 * None. 7303 * 7304 * Side effects: 7305 * None. 7306 * 7307 *-------------------------------------------------------------- 7308 */ 7309 7310void 7311TreeDisplay_ItemDeleted( 7312 TreeCtrl *tree, /* Widget info. */ 7313 TreeItem item /* Item to remove. */ 7314 ) 7315{ 7316 TreeDInfo dInfo = tree->dInfo; 7317 Tcl_HashEntry *hPtr; 7318 7319 hPtr = Tcl_FindHashEntry(&dInfo->itemVisHash, (char *) item); 7320 if (hPtr != NULL) { 7321#ifdef DCOLUMN 7322 ckfree((char *) Tcl_GetHashValue(hPtr)); 7323#endif 7324 Tcl_DeleteHashEntry(hPtr); 7325 } 7326} 7327 7328/* 7329 *-------------------------------------------------------------- 7330 * 7331 * TreeDisplay_ColumnDeleted -- 7332 * 7333 * Removes a column from the list of on-screen columns for 7334 * all on-screen items. 7335 * 7336 * Results: 7337 * None. 7338 * 7339 * Side effects: 7340 * None. 7341 * 7342 *-------------------------------------------------------------- 7343 */ 7344 7345void 7346TreeDisplay_ColumnDeleted( 7347 TreeCtrl *tree, /* Widget info. */ 7348 TreeColumn column /* Column to remove. */ 7349 ) 7350{ 7351#ifdef DCOLUMN 7352 TreeDInfo dInfo = tree->dInfo; 7353 Tcl_HashSearch search; 7354 Tcl_HashEntry *hPtr; 7355 TreeColumn *value; 7356 int i; 7357 7358 hPtr = Tcl_FirstHashEntry(&dInfo->itemVisHash, &search); 7359 while (hPtr != NULL) { 7360 value = (TreeColumn *) Tcl_GetHashValue(hPtr); 7361 for (i = 0; value[i] != NULL; i++) { 7362 if (value[i] == column) { 7363 while (value[i] != NULL) { 7364 value[i] = value[i + 1]; 7365 ++i; 7366 } 7367 if (tree->debug.enable && tree->debug.span) 7368 dbwin("TreeDisplay_ColumnDeleted item %d column %d\n", 7369 TreeItem_GetID(tree, (TreeItem) Tcl_GetHashKey( 7370 &dInfo->itemVisHash, hPtr)), 7371 TreeColumn_GetID(column)); 7372 break; 7373 } 7374 } 7375 hPtr = Tcl_NextHashEntry(&search); 7376 } 7377#endif 7378} 7379 7380/* 7381 *-------------------------------------------------------------- 7382 * 7383 * TreeDisplay_FreeColumnDInfo -- 7384 * 7385 * Free any display info associated with a column when it is 7386 * deleted. 7387 * 7388 * Results: 7389 * None. 7390 * 7391 * Side effects: 7392 * None. 7393 * 7394 *-------------------------------------------------------------- 7395 */ 7396 7397void 7398TreeDisplay_FreeColumnDInfo( 7399 TreeCtrl *tree, /* Widget info. */ 7400 TreeColumn column /* Column info. */ 7401 ) 7402{ 7403 TreeColumnDInfo dColumn = TreeColumn_GetDInfo(column); 7404 7405 if (dColumn != NULL) 7406 ckfree((char *) dColumn); 7407} 7408 7409/* 7410 *-------------------------------------------------------------- 7411 * 7412 * Tree_ShouldDisplayLockedColumns -- 7413 * 7414 * Figure out if we are allowed to draw any locked columns. 7415 * 7416 * Results: 7417 * TRUE if locked columns should be displayed, otherwise FALSE. 7418 * 7419 * Side effects: 7420 * None. 7421 * 7422 *-------------------------------------------------------------- 7423 */ 7424 7425int 7426Tree_ShouldDisplayLockedColumns( 7427 TreeCtrl *tree /* Widget info. */ 7428 ) 7429{ 7430 if (!tree->vertical) 7431 return 0; 7432 7433 if (tree->wrapMode != TREE_WRAP_NONE) 7434 return 0; 7435 7436 Tree_UpdateItemIndex(tree); /* update tree->itemWrapCount */ 7437 if (tree->itemWrapCount > 0) 7438 return 0; 7439 7440 return 1; 7441} 7442 7443/* 7444 *-------------------------------------------------------------- 7445 * 7446 * Tree_DInfoChanged -- 7447 * 7448 * Set some DINFO_xxx flags and schedule a redisplay. 7449 * 7450 * Results: 7451 * None. 7452 * 7453 * Side effects: 7454 * The widget will be redisplayed at idle time. 7455 * 7456 *-------------------------------------------------------------- 7457 */ 7458 7459void 7460Tree_DInfoChanged( 7461 TreeCtrl *tree, /* Widget info. */ 7462 int flags /* DINFO_xxx flags. */ 7463 ) 7464{ 7465 TreeDInfo dInfo = tree->dInfo; 7466 7467 dInfo->flags |= flags; 7468 Tree_EventuallyRedraw(tree); 7469} 7470 7471/* 7472 *-------------------------------------------------------------- 7473 * 7474 * Tree_InvalidateArea -- 7475 * 7476 * Mark as dirty parts of any DItems in the given area. If the given 7477 * area overlaps the borders they are marked as needing to be 7478 * redrawn. 7479 * 7480 * Results: 7481 * None. 7482 * 7483 * Side effects: 7484 * None. 7485 * 7486 *-------------------------------------------------------------- 7487 */ 7488 7489void 7490Tree_InvalidateArea( 7491 TreeCtrl *tree, /* Widget info. */ 7492 int x1, int y1, /* Left & top of dirty area in window 7493 * coordinates. */ 7494 int x2, int y2 /* Right & bottom of dirty area in window 7495 * coordinates. */ 7496 ) 7497{ 7498 TreeDInfo dInfo = tree->dInfo; 7499 DItem *dItem; 7500 7501 if (x1 >= x2 || y1 >= y2) 7502 return; 7503 7504 if ((y2 > Tree_HeaderTop(tree)) && (y1 < Tree_HeaderBottom(tree))) 7505 dInfo->flags |= DINFO_DRAW_HEADER; 7506 7507 dItem = dInfo->dItem; 7508 while (dItem != NULL) { 7509 if ((!dInfo->empty && dInfo->rangeFirst != NULL) && 7510 !(dItem->area.flags & DITEM_ALL_DIRTY) && 7511 (x2 > dItem->area.x) && (x1 < dItem->area.x + dItem->area.width) && 7512 (y2 > dItem->y) && (y1 < dItem->y + dItem->height)) { 7513 InvalidateDItemX(dItem, &dItem->area, dItem->area.x, x1, x2 - x1); 7514 InvalidateDItemY(dItem, &dItem->area, dItem->y, y1, y2 - y1); 7515 dItem->area.flags |= DITEM_DIRTY; 7516 } 7517 if (!dInfo->emptyL && !(dItem->left.flags & DITEM_ALL_DIRTY) && 7518 (x2 > dInfo->boundsL[0]) && (x1 < dInfo->boundsL[2]) && 7519 (y2 > dItem->y) && (y1 < dItem->y + dItem->height)) { 7520 InvalidateDItemX(dItem, &dItem->left, dItem->left.x, x1, x2 - x1); 7521 InvalidateDItemY(dItem, &dItem->left, dItem->y, y1, y2 - y1); 7522 dItem->left.flags |= DITEM_DIRTY; 7523 } 7524 if (!dInfo->emptyR && !(dItem->right.flags & DITEM_ALL_DIRTY) && 7525 (x2 > dInfo->boundsR[0]) && (x1 < dInfo->boundsR[2]) && 7526 (y2 > dItem->y) && (y1 < dItem->y + dItem->height)) { 7527 InvalidateDItemX(dItem, &dItem->right, dItem->right.x, x1, x2 - x1); 7528 InvalidateDItemY(dItem, &dItem->right, dItem->y, y1, y2 - y1); 7529 dItem->right.flags |= DITEM_DIRTY; 7530 } 7531 dItem = dItem->next; 7532 } 7533 7534 if ((x1 < Tree_BorderLeft(tree)) || 7535 (y1 < Tree_BorderTop(tree)) || 7536 (x2 > Tree_BorderRight(tree)) || 7537 (y2 > Tree_BorderBottom(tree))) { 7538 dInfo->flags |= DINFO_DRAW_HIGHLIGHT; 7539 dInfo->flags |= DINFO_DRAW_BORDER; 7540 } 7541 7542 /* Invalidate part of the whitespace */ 7543 InvalidateWhitespace(tree, x1, y1, x2, y2); 7544 7545 if (tree->debug.enable && tree->debug.display && tree->debug.eraseColor) { 7546 XFillRectangle(tree->display, Tk_WindowId(tree->tkwin), 7547 tree->debug.gcErase, x1, y1, x2 - x1, y2 - y1); 7548 DisplayDelay(tree); 7549 } 7550} 7551 7552/* 7553 *-------------------------------------------------------------- 7554 * 7555 * Tree_InvalidateRegion -- 7556 * 7557 * Mark as dirty parts of any DItems in the given area. If the given 7558 * area overlaps the borders they are marked as needing to be 7559 * redrawn. 7560 * 7561 * Results: 7562 * None. 7563 * 7564 * Side effects: 7565 * None. 7566 * 7567 *-------------------------------------------------------------- 7568 */ 7569 7570void 7571Tree_InvalidateRegion( 7572 TreeCtrl *tree, /* Widget info. */ 7573 TkRegion region /* Region to mark as dirty, in window 7574 * coordinates. */ 7575 ) 7576{ 7577 TreeDInfo dInfo = tree->dInfo; 7578 DItem *dItem; 7579 int minX, minY, maxX, maxY; 7580 XRectangle rect; 7581 int x1, x2, y1, y2; 7582 TkRegion rgn; 7583 7584 TkClipBox(region, &rect); 7585 if (!rect.width || !rect.height) 7586 return; 7587 7588 if (Tree_AreaBbox(tree, TREE_AREA_HEADER, &minX, &minY, &maxX, &maxY) && 7589 TkRectInRegion(region, minX, minY, maxX - minX, maxY - minY) 7590 != RectangleOut) 7591 dInfo->flags |= DINFO_DRAW_HEADER; 7592 7593 rgn = Tree_GetRegion(tree); 7594 7595 dItem = dInfo->dItem; 7596 while (dItem != NULL) { 7597 if ((!dInfo->empty && dInfo->rangeFirst != NULL) && !(dItem->area.flags & DITEM_ALL_DIRTY)) { 7598 rect.x = dItem->area.x; 7599 rect.y = dItem->y; 7600 rect.width = dItem->area.width; 7601 rect.height = dItem->height; 7602 Tree_SetRectRegion(rgn, &rect); 7603 TkIntersectRegion(region, rgn, rgn); 7604 TkClipBox(rgn, &rect); 7605 if (rect.width > 0 && rect.height > 0) { 7606 InvalidateDItemX(dItem, &dItem->area, dItem->area.x, rect.x, rect.width); 7607 InvalidateDItemY(dItem, &dItem->area, dItem->y, rect.y, rect.height); 7608 dItem->area.flags |= DITEM_DIRTY; 7609 } 7610 } 7611 if (!dInfo->emptyL && !(dItem->left.flags & DITEM_ALL_DIRTY)) { 7612 rect.x = dItem->left.x; 7613 rect.y = dItem->y; 7614 rect.width = dItem->left.width; 7615 rect.height = dItem->height; 7616 Tree_SetRectRegion(rgn, &rect); 7617 TkIntersectRegion(region, rgn, rgn); 7618 TkClipBox(rgn, &rect); 7619 if (rect.width > 0 && rect.height > 0) { 7620 InvalidateDItemX(dItem, &dItem->left, dItem->left.x, rect.x, rect.width); 7621 InvalidateDItemY(dItem, &dItem->left, dItem->y, rect.y, rect.height); 7622 dItem->left.flags |= DITEM_DIRTY; 7623 } 7624 } 7625 if (!dInfo->emptyR && !(dItem->right.flags & DITEM_ALL_DIRTY)) { 7626 rect.x = dItem->right.x; 7627 rect.y = dItem->y; 7628 rect.width = dItem->right.width; 7629 rect.height = dItem->height; 7630 Tree_SetRectRegion(rgn, &rect); 7631 TkIntersectRegion(region, rgn, rgn); 7632 TkClipBox(rgn, &rect); 7633 if (rect.width > 0 && rect.height > 0) { 7634 InvalidateDItemX(dItem, &dItem->right, dItem->right.x, rect.x, rect.width); 7635 InvalidateDItemY(dItem, &dItem->right, dItem->y, rect.y, rect.height); 7636 dItem->right.flags |= DITEM_DIRTY; 7637 } 7638 } 7639 dItem = dItem->next; 7640 } 7641 7642 TkClipBox(region, &rect); 7643 x1 = rect.x, x2 = rect.x + rect.width; 7644 y1 = rect.y, y2 = rect.y + rect.height; 7645 if ((x1 < Tree_BorderLeft(tree)) || 7646 (y1 < Tree_BorderTop(tree)) || 7647 (x2 > Tree_BorderRight(tree)) || 7648 (y2 > Tree_BorderBottom(tree))) { 7649 dInfo->flags |= DINFO_DRAW_HIGHLIGHT; 7650 dInfo->flags |= DINFO_DRAW_BORDER; 7651 } 7652 7653 /* Invalidate part of the whitespace */ 7654 TkSubtractRegion(dInfo->wsRgn, region, dInfo->wsRgn); 7655 7656 Tree_FreeRegion(tree, rgn); 7657 7658 if (tree->debug.enable && tree->debug.display && tree->debug.eraseColor) { 7659 Tree_FillRegion(tree->display, Tk_WindowId(tree->tkwin), 7660 tree->debug.gcErase, region); 7661 DisplayDelay(tree); 7662 } 7663} 7664 7665/* 7666 *-------------------------------------------------------------- 7667 * 7668 * Tree_InvalidateItemArea -- 7669 * 7670 * Mark as dirty parts of any DItems in the given area. This is 7671 * like Tree_InvalidateArea() but the given area is clipped inside 7672 * the borders/header. 7673 * 7674 * Results: 7675 * None. 7676 * 7677 * Side effects: 7678 * None. 7679 * 7680 *-------------------------------------------------------------- 7681 */ 7682 7683void 7684Tree_InvalidateItemArea( 7685 TreeCtrl *tree, /* Widget info. */ 7686 int x1, int y1, /* Left & top of dirty area in window 7687 * coordinates. */ 7688 int x2, int y2 /* Right & bottom of dirty area in window 7689 * coordinates. */ 7690 ) 7691{ 7692 if (x1 < Tree_ContentLeft(tree)) 7693 x1 = Tree_ContentLeft(tree); 7694 if (y1 < Tree_ContentTop(tree)) 7695 y1 = Tree_ContentTop(tree); 7696 if (x2 > Tree_ContentRight(tree)) 7697 x2 = Tree_ContentRight(tree); 7698 if (y2 > Tree_ContentBottom(tree)) 7699 y2 = Tree_ContentBottom(tree); 7700 Tree_InvalidateArea(tree, x1, y1, x2, y2); 7701} 7702 7703/* 7704 *-------------------------------------------------------------- 7705 * 7706 * Tree_RedrawArea -- 7707 * 7708 * Mark as dirty parts of any DItems in the given area. If the given 7709 * area overlaps the borders they are marked as needing to be 7710 * redrawn. The given area is subtracted from the whitespace region 7711 * so that that part of the whitespace region will be redrawn. 7712 * 7713 * Results: 7714 * None. 7715 * 7716 * Side effects: 7717 * The widget will be redisplayed at idle time. 7718 * 7719 *-------------------------------------------------------------- 7720 */ 7721 7722void 7723Tree_RedrawArea( 7724 TreeCtrl *tree, /* Widget info. */ 7725 int x1, int y1, /* Left & top of dirty area in window 7726 * coordinates. */ 7727 int x2, int y2 /* Right & bottom of dirty area in window 7728 * coordinates. */ 7729 ) 7730{ 7731 Tree_InvalidateArea(tree, x1, y1, x2, y2); 7732 Tree_EventuallyRedraw(tree); 7733} 7734 7735/* 7736 *-------------------------------------------------------------- 7737 * 7738 * Tree_ExposeArea -- 7739 * 7740 * Called in response to <Expose> events. Causes part of the window 7741 * to be redisplayed. With "-doublebuffer window", part of the 7742 * offscreen pixmap is marked as needing to be copied but no redrawing 7743 * of items is done. Without "-doublebuffer window", items will be 7744 * redrawn. 7745 * 7746 * Results: 7747 * None. 7748 * 7749 * Side effects: 7750 * The widget will be redisplayed at idle time. 7751 * 7752 *-------------------------------------------------------------- 7753 */ 7754 7755void 7756Tree_ExposeArea( 7757 TreeCtrl *tree, /* Widget info. */ 7758 int x1, int y1, /* Left & top of dirty area in window 7759 * coordinates. */ 7760 int x2, int y2 /* Right & bottom of dirty area in window 7761 * coordinates. */ 7762 ) 7763{ 7764 TreeDInfo dInfo = tree->dInfo; 7765 7766 if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) { 7767 if ((x1 < Tree_BorderLeft(tree)) || 7768 (y1 < Tree_BorderTop(tree)) || 7769 (x2 > Tree_BorderRight(tree)) || 7770 (y2 > Tree_BorderBottom(tree))) { 7771 dInfo->flags |= DINFO_DRAW_HIGHLIGHT; 7772 dInfo->flags |= DINFO_DRAW_BORDER; 7773 } 7774 if (x1 < Tree_BorderLeft(tree)) 7775 x1 = Tree_BorderLeft(tree); 7776 if (x2 > Tree_BorderRight(tree)) 7777 x2 = Tree_BorderRight(tree); 7778 if (y1 < Tree_BorderTop(tree)) 7779 y1 = Tree_BorderTop(tree); 7780 if (y2 > Tree_BorderBottom(tree)) 7781 y2 = Tree_BorderBottom(tree); 7782 DblBufWinDirty(tree, x1, y1, x2, y2); 7783 } else { 7784 Tree_InvalidateArea(tree, x1, y1, x2, y2); 7785 } 7786 Tree_EventuallyRedraw(tree); 7787} 7788 7789/* 7790 *-------------------------------------------------------------- 7791 * 7792 * TreeDInfo_Init -- 7793 * 7794 * Perform display-related initialization when a new TreeCtrl is 7795 * created. 7796 * 7797 * Results: 7798 * None. 7799 * 7800 * Side effects: 7801 * Memory is allocated. 7802 * 7803 *-------------------------------------------------------------- 7804 */ 7805 7806void 7807TreeDInfo_Init( 7808 TreeCtrl *tree /* Widget info. */ 7809 ) 7810{ 7811 TreeDInfo dInfo; 7812 XGCValues gcValues; 7813 7814 dInfo = (TreeDInfo) ckalloc(sizeof(TreeDInfo_)); 7815 memset(dInfo, '\0', sizeof(TreeDInfo_)); 7816 gcValues.graphics_exposures = True; 7817 dInfo->scrollGC = Tk_GetGC(tree->tkwin, GCGraphicsExposures, &gcValues); 7818 dInfo->flags = DINFO_OUT_OF_DATE; 7819 dInfo->wsRgn = Tree_GetRegion(tree); 7820 dInfo->dirtyRgn = TkCreateRegion(); 7821 Tcl_InitHashTable(&dInfo->itemVisHash, TCL_ONE_WORD_KEYS); 7822#ifdef REDRAW_RGN 7823 dInfo->redrawRgn = TkCreateRegion(); 7824#endif /* REDRAW_RGN */ 7825 tree->dInfo = dInfo; 7826} 7827 7828/* 7829 *-------------------------------------------------------------- 7830 * 7831 * TreeDInfo_Free -- 7832 * 7833 * Free display-related resources for a deleted TreeCtrl. 7834 * 7835 * Results: 7836 * None. 7837 * 7838 * Side effects: 7839 * Memory is allocated. 7840 * 7841 *-------------------------------------------------------------- 7842 */ 7843 7844void 7845TreeDInfo_Free( 7846 TreeCtrl *tree /* Widget info. */ 7847 ) 7848{ 7849 TreeDInfo dInfo = tree->dInfo; 7850 Range *range = dInfo->rangeFirst; 7851 Tcl_HashEntry *hPtr; 7852 Tcl_HashSearch search; 7853 7854 if (dInfo->rItem != NULL) 7855 ckfree((char *) dInfo->rItem); 7856 if (dInfo->rangeLock != NULL) 7857 ckfree((char *) dInfo->rangeLock); 7858 while (dInfo->dItem != NULL) { 7859 DItem *next = dInfo->dItem->next; 7860 WFREE(dInfo->dItem, DItem); 7861 dInfo->dItem = next; 7862 } 7863 while (dInfo->dItemFree != NULL) { 7864 DItem *next = dInfo->dItemFree->next; 7865 WFREE(dInfo->dItemFree, DItem); 7866 dInfo->dItemFree = next; 7867 } 7868 while (range != NULL) 7869 range = Range_Free(tree, range); 7870 Tk_FreeGC(tree->display, dInfo->scrollGC); 7871 if (dInfo->flags & DINFO_REDRAW_PENDING) 7872 Tcl_CancelIdleCall(Tree_Display, (ClientData) tree); 7873 if (dInfo->pixmapW.drawable != None) 7874 Tk_FreePixmap(tree->display, dInfo->pixmapW.drawable); 7875 if (dInfo->pixmapI.drawable != None) 7876 Tk_FreePixmap(tree->display, dInfo->pixmapI.drawable); 7877 if (dInfo->pixmapT.drawable != None) 7878 Tk_FreePixmap(tree->display, dInfo->pixmapT.drawable); 7879 if (dInfo->xScrollIncrements != NULL) 7880 ckfree((char *) dInfo->xScrollIncrements); 7881 if (dInfo->yScrollIncrements != NULL) 7882 ckfree((char *) dInfo->yScrollIncrements); 7883 Tree_FreeRegion(tree, dInfo->wsRgn); 7884 TkDestroyRegion(dInfo->dirtyRgn); 7885#ifdef DCOLUMN 7886 hPtr = Tcl_FirstHashEntry(&dInfo->itemVisHash, &search); 7887 while (hPtr != NULL) { 7888 ckfree((char *) Tcl_GetHashValue(hPtr)); 7889 hPtr = Tcl_NextHashEntry(&search); 7890 } 7891#endif 7892 Tcl_DeleteHashTable(&dInfo->itemVisHash); 7893#ifdef REDRAW_RGN 7894 TkDestroyRegion(dInfo->redrawRgn); 7895#endif /* REDRAW_RGN */ 7896 WFREE(dInfo, TreeDInfo_); 7897} 7898 7899int 7900Tree_DumpDInfo( 7901 TreeCtrl *tree, /* Widget info. */ 7902 int objc, /* Number of arguments. */ 7903 Tcl_Obj *CONST objv[] /* Argument values. */ 7904 ) 7905{ 7906 Tcl_Interp *interp = tree->interp; 7907 TreeDInfo dInfo = tree->dInfo; 7908 Tcl_DString dString; 7909 DItem *dItem; 7910 Range *range; 7911 RItem *rItem; 7912 int index; 7913 7914 static CONST char *optionNames[] = { 7915 "alloc", "ditem", "onscreen", "range", (char *) NULL 7916 }; 7917#undef DUMP_ALLOC /* [BUG 2233922] SunOS: build error */ 7918 enum { DUMP_ALLOC, DUMP_DITEM, DUMP_ONSCREEN, DUMP_RANGE }; 7919 7920 if (objc != 4) { 7921 Tcl_WrongNumArgs(interp, 3, objv, "option"); 7922 return TCL_ERROR; 7923 } 7924 7925 if (Tcl_GetIndexFromObj(interp, objv[3], optionNames, "option", 0, 7926 &index) != TCL_OK) { 7927 return TCL_ERROR; 7928 } 7929 7930 Tcl_DStringInit(&dString); 7931 7932 if (index == DUMP_ALLOC) { 7933 int count = 0, size = 0; 7934 for (dItem = dInfo->dItem; dItem != NULL; dItem = dItem->next) { 7935 count += 1; 7936 } 7937 for (dItem = dInfo->dItemFree; dItem != NULL; dItem = dItem->next) { 7938 count += 1; 7939 } 7940 size = count * sizeof(DItem); 7941 DStringAppendf(&dString, "%-20s: %8d : %8d B %5d KB\n", 7942 "DItem", count, size, (size + 1023) / 1024); 7943 7944 count = dInfo->rItemMax; 7945 size = count * sizeof(RItem); 7946 DStringAppendf(&dString, "%-20s: %8d : %8d B %5d KB\n", 7947 "RItem", count, size, (size + 1023) / 1024); 7948 } 7949 7950 if (index == DUMP_DITEM) { 7951 DStringAppendf(&dString, "DumpDInfo: itemW,H %d,%d totalW,H %d,%d flags 0x%0x vertical %d itemVisCount %d\n", 7952 dInfo->itemWidth, dInfo->itemHeight, 7953 dInfo->totalWidth, dInfo->totalHeight, 7954 dInfo->flags, tree->vertical, tree->itemVisCount); 7955 DStringAppendf(&dString, " empty=%d bounds=%d,%d,%d,%d\n", dInfo->empty, 7956 dInfo->bounds[0], dInfo->bounds[1], 7957 dInfo->bounds[2], dInfo->bounds[3]); 7958 DStringAppendf(&dString, " emptyL=%d boundsL=%d,%d,%d,%d\n", dInfo->emptyL, 7959 dInfo->boundsL[0], dInfo->boundsL[1], 7960 dInfo->boundsL[2], dInfo->boundsL[3]); 7961 DStringAppendf(&dString, " emptyR=%d boundsR=%d,%d,%d,%d\n", dInfo->emptyR, 7962 dInfo->boundsR[0], dInfo->boundsR[1], 7963 dInfo->boundsR[2], dInfo->boundsR[3]); 7964 dItem = dInfo->dItem; 7965 while (dItem != NULL) { 7966 if (dItem->item == NULL) { 7967 DStringAppendf(&dString, " item NULL\n"); 7968 } else { 7969 DStringAppendf(&dString, " item %d x,y,w,h %d,%d,%d,%d dirty %d,%d,%d,%d flags %0X\n", 7970 TreeItem_GetID(tree, dItem->item), 7971 dItem->area.x, dItem->y, dItem->area.width, dItem->height, 7972 dItem->area.dirty[LEFT], dItem->area.dirty[TOP], 7973 dItem->area.dirty[RIGHT], dItem->area.dirty[BOTTOM], 7974 dItem->area.flags); 7975 DStringAppendf(&dString, " left: dirty %d,%d,%d,%d flags %0X\n", 7976 dItem->left.dirty[LEFT], dItem->left.dirty[TOP], 7977 dItem->left.dirty[RIGHT], dItem->left.dirty[BOTTOM], 7978 dItem->left.flags); 7979 DStringAppendf(&dString, " right: dirty %d,%d,%d,%d flags %0X\n", 7980 dItem->right.dirty[LEFT], dItem->right.dirty[TOP], 7981 dItem->right.dirty[RIGHT], dItem->right.dirty[BOTTOM], 7982 dItem->right.flags); 7983 } 7984 dItem = dItem->next; 7985 } 7986 } 7987 7988 if (index == DUMP_ONSCREEN) { 7989 dItem = dInfo->dItem; 7990 while (dItem != NULL) { 7991 Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&dInfo->itemVisHash, (char *) dItem->item); 7992 TreeColumn *value = (TreeColumn *) Tcl_GetHashValue(hPtr); 7993 DStringAppendf(&dString, "item %d:", TreeItem_GetID(tree, dItem->item)); 7994 while (*value != NULL) { 7995 DStringAppendf(&dString, " %d", TreeColumn_GetID(*value)); 7996 ++value; 7997 } 7998 DStringAppendf(&dString, "\n"); 7999 dItem = dItem->next; 8000 } 8001 } 8002 8003 if (index == DUMP_RANGE) { 8004 DStringAppendf(&dString, " dInfo.rangeFirstD %p dInfo.rangeLastD %p\n", 8005 dInfo->rangeFirstD, dInfo->rangeLastD); 8006 for (range = dInfo->rangeFirstD; 8007 range != NULL; 8008 range = range->next) { 8009 DStringAppendf(&dString, " Range: totalW,H %d,%d offset %d\n", range->totalWidth, 8010 range->totalHeight, range->offset); 8011 if (range == dInfo->rangeLastD) 8012 break; 8013 } 8014 8015 DStringAppendf(&dString, " dInfo.rangeFirst %p dInfo.rangeLast %p\n", 8016 dInfo->rangeFirst, dInfo->rangeLast); 8017 for (range = dInfo->rangeFirst; 8018 range != NULL; 8019 range = range->next) { 8020 DStringAppendf(&dString, " Range: first %p last %p totalW,H %d,%d offset %d\n", 8021 range->first, range->last, 8022 range->totalWidth, range->totalHeight, range->offset); 8023 rItem = range->first; 8024 while (1) { 8025 DStringAppendf(&dString, " RItem: item %d index %d offset %d size %d\n", 8026 TreeItem_GetID(tree, rItem->item), rItem->index, rItem->offset, rItem->size); 8027 if (rItem == range->last) 8028 break; 8029 rItem++; 8030 } 8031 } 8032 } 8033 8034 Tcl_DStringResult(tree->interp, &dString); 8035 return TCL_OK; 8036} 8037 8038