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