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