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