1/*
2 * tkTreeStyle.c --
3 *
4 *	This module implements styles for treectrl widgets.
5 *
6 * Copyright (c) 2002-2009 Tim Baker
7 *
8 * RCS: @(#) $Id: tkTreeStyle.c,v 1.80 2010/03/21 20:47:06 treectrl Exp $
9 */
10
11#include "tkTreeCtrl.h"
12#include "tkTreeElem.h"
13
14/* This is the roundUp argument to TreeAlloc_CAlloc. */
15#define ELEMENT_LINK_ROUND 1
16
17/* Define this for performance gain and increased memory usage. */
18/* When undefined, there is quite a performance hit when elements are
19 * squeezable and a style has less than its needed width. The memory
20 * savings are not too great when undefined. */
21#define CACHE_STYLE_SIZE
22
23/* Define this for performance gain and increased memory usage. */
24#define CACHE_ELEM_SIZE
25
26typedef struct MStyle MStyle;
27typedef struct IStyle IStyle;
28typedef struct MElementLink MElementLink;
29typedef struct IElementLink IElementLink;
30
31/*
32 * A data structure of the following type is kept for each master style.
33 * Master styles are created by the [style create] widget command.
34 */
35struct MStyle
36{
37    MStyle *master;		/* Always NULL. Needed to distinguish between
38				 * an MStyle and IStyle. */
39    Tk_Uid name;		/* Unique identifier. */
40    int numElements;		/* Size of elements[]. */
41    MElementLink *elements;	/* Array of master elements. */
42    int vertical;		/* -orient */
43};
44
45/*
46 * A data structure of the following type is kept for each instance style.
47 * Instance styles are created when a style is assigned to an item-column.
48 */
49struct IStyle
50{
51    MStyle *master;		/* Always non-NULL. */
52    IElementLink *elements;	/* Array of master or instance elements. */
53    int neededWidth;		/* Requested size of this style based on */
54    int neededHeight;		/* layout of the elements. */
55#ifdef TREECTRL_DEBUG
56    int neededState;
57#endif
58#ifdef CACHE_STYLE_SIZE
59    int minWidth;
60    int minHeight;
61    int layoutWidth;
62    int layoutHeight;
63#endif
64};
65
66#define ELF_eEXPAND_W 0x0001 /* expand Layout.ePadX[0] */
67#define ELF_eEXPAND_N 0x0002
68#define ELF_eEXPAND_E 0x0004
69#define ELF_eEXPAND_S 0x0008
70#define ELF_iEXPAND_W 0x0010 /* expand Layout.iPadX[0] */
71#define ELF_iEXPAND_N 0x0020
72#define ELF_iEXPAND_E 0x0040
73#define ELF_iEXPAND_S 0x0080
74#define ELF_SQUEEZE_X 0x0100 /* shrink Layout.useWidth if needed */
75#define ELF_SQUEEZE_Y 0x0200
76#define ELF_DETACH 0x0400
77#define ELF_INDENT 0x0800 /* don't layout under button&line area */
78#define ELF_STICKY_W 0x1000
79#define ELF_STICKY_N 0x2000
80#define ELF_STICKY_E 0x4000
81#define ELF_STICKY_S 0x8000
82#define ELF_iEXPAND_X 0x00010000 /* expand Layout.useWidth */
83#define ELF_iEXPAND_Y 0x00020000
84
85#define ELF_eEXPAND_WE (ELF_eEXPAND_W | ELF_eEXPAND_E)
86#define ELF_eEXPAND_NS (ELF_eEXPAND_N | ELF_eEXPAND_S)
87#define ELF_eEXPAND (ELF_eEXPAND_WE | ELF_eEXPAND_NS)
88#define ELF_iEXPAND_WE (ELF_iEXPAND_W | ELF_iEXPAND_E)
89#define ELF_iEXPAND_NS (ELF_iEXPAND_N | ELF_iEXPAND_S)
90#define ELF_iEXPAND (ELF_iEXPAND_WE | ELF_iEXPAND_NS)
91#define ELF_EXPAND_WE (ELF_eEXPAND_WE | ELF_iEXPAND_WE)
92#define ELF_EXPAND_NS (ELF_eEXPAND_NS | ELF_iEXPAND_NS)
93#define ELF_EXPAND_W (ELF_eEXPAND_W | ELF_iEXPAND_W)
94#define ELF_EXPAND_N (ELF_eEXPAND_N | ELF_iEXPAND_N)
95#define ELF_EXPAND_E (ELF_eEXPAND_E | ELF_iEXPAND_E)
96#define ELF_EXPAND_S (ELF_eEXPAND_S | ELF_iEXPAND_S)
97#define ELF_STICKY (ELF_STICKY_W | ELF_STICKY_N | ELF_STICKY_E | ELF_STICKY_S)
98
99#define DETACH_OR_UNION(e) (((e)->onion != NULL) || ((e)->flags & ELF_DETACH))
100
101/*
102 * An array of these is kept for each master style, one per element.
103 * Most of the fields are set by the [style layout] widget command.
104 */
105struct MElementLink
106{
107    TreeElement elem;		/* Master element. */
108    int ePadX[2]; /* external horizontal padding */
109    int ePadY[2]; /* external vertical padding */
110    int iPadX[2]; /* internal horizontal padding */
111    int iPadY[2]; /* internal vertical padding */
112    int flags; /* ELF_xxx */
113    int *onion, onionCount; /* -union option info */
114    int minWidth, fixedWidth, maxWidth;
115    int minHeight, fixedHeight, maxHeight;
116    PerStateInfo draw; /* -draw */
117    PerStateInfo visible; /* -visible */
118};
119
120/*
121 * An array of these is kept for each instance style, one per element.
122 */
123struct IElementLink
124{
125    TreeElement elem;		/* Master or instance element. */
126#ifdef CACHE_ELEM_SIZE
127    int neededWidth;
128    int neededHeight;
129    int layoutWidth;
130    int layoutHeight;
131#endif
132};
133
134static CONST char *MStyleUid = "MStyle", *IStyleUid = "IStyle",
135    *MElementLinkUid = "MElementLink", *IElementLinkUid = "IElementLink";
136
137static char *orientStringTable[] = { "horizontal", "vertical", (char *) NULL };
138
139static Tk_OptionSpec styleOptionSpecs[] = {
140    {TK_OPTION_STRING_TABLE, "-orient", (char *) NULL, (char *) NULL,
141	"horizontal", -1, Tk_Offset(MStyle, vertical),
142	0, (ClientData) orientStringTable, 0},
143    {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
144	(char *) NULL, 0, -1, 0, (ClientData) NULL, 0}
145};
146
147/*
148 * The following structure is used to hold layout information about a
149 * single element. This information is not cached anywhere.
150 */
151struct Layout
152{
153    MElementLink *master;
154    IElementLink *eLink;
155    int useWidth;
156    int useHeight;
157#ifndef CACHE_ELEM_SIZE
158    int neededWidth;
159    int neededHeight;
160#endif
161    int x;		/* left of ePad */
162    int y;		/* above ePad */
163    int eWidth;		/* ePad + iPad + useWidth + iPad + ePad */
164    int eHeight;	/* ePad + iPad + useHeight + iPad + ePad */
165    int iWidth;		/* iPad + useWidth + iPad */
166    int iHeight;	/* iPad + useHeight + iPad */
167    int ePadX[2];	/* external horizontal padding */
168    int ePadY[2];	/* external vertical padding */
169    int iPadX[2];	/* internal horizontal padding */
170    int iPadY[2];	/* internal vertical padding */
171    int uPadX[2];	/* padding due to -union */
172    int uPadY[2];	/* padding due to -union */
173    int temp;
174    int visible;	/* TRUE if the element should be displayed. */
175};
176
177#define IS_HIDDEN(L) ((L)->visible == 0)
178
179/*
180 *----------------------------------------------------------------------
181 *
182 * Style_DoExpandH --
183 *
184 *	Add extra horizontal space to an element. The space is
185 *	distributed from right to left until all available space
186 *	is used or expansion is not possible.
187 *
188 * Results:
189 *	Layout.ePadX, Layout.iPadX, and Layout.useWidth may be
190 *	updated. The amount of available space that was used is
191 *	returned.
192 *
193 * Side effects:
194 *	None.
195 *
196 *----------------------------------------------------------------------
197 */
198
199static int
200Style_DoExpandH(
201    struct Layout *layout, 	/* Layout to be adjusted. */
202    int right			/* Limit of expansion. */
203    )
204{
205    MElementLink *eLink1 = layout->master;
206    int flags = eLink1->flags;
207    int numExpand = 0, spaceRemaining, spaceUsed = 0;
208    int *ePadX, *iPadX, *uPadX;
209
210    if (!(flags & (ELF_EXPAND_WE | ELF_iEXPAND_X)))
211	return 0;
212
213    ePadX = layout->ePadX;
214    iPadX = layout->iPadX;
215    uPadX = layout->uPadX;
216
217    spaceRemaining = right - (layout->x + ePadX[PAD_TOP_LEFT] +
218	layout->iWidth + MAX(ePadX[PAD_BOTTOM_RIGHT], uPadX[PAD_BOTTOM_RIGHT]));
219    if (spaceRemaining <= 0)
220	return 0;
221
222    if (layout->temp)
223	numExpand = layout->temp;
224    /* For -detach or vertical layout, just set layout->temp to zero */
225    else {
226	if (flags & ELF_eEXPAND_W) numExpand++;
227	if (flags & ELF_iEXPAND_W) numExpand++;
228	if (flags & ELF_iEXPAND_X) {
229	    if ((eLink1->maxWidth < 0) ||
230		(eLink1->maxWidth > layout->useWidth))
231		numExpand++;
232	}
233	if (flags & ELF_iEXPAND_E) numExpand++;
234	if (flags & ELF_eEXPAND_E) numExpand++;
235    }
236
237    while ((spaceRemaining > 0) && (numExpand > 0)) {
238	int each = (spaceRemaining >= numExpand) ? (spaceRemaining / numExpand) : 1;
239
240	numExpand = 0;
241
242	/* Allocate extra space to the *right* padding first so that any
243	 * extra single pixel is given to the right. */
244
245	if (flags & ELF_eEXPAND_E) {
246	    int add = each;
247	    ePadX[PAD_BOTTOM_RIGHT] += add;
248	    layout->eWidth += add;
249	    spaceRemaining -= add;
250	    spaceUsed += add;
251	    if (!spaceRemaining)
252		break;
253	    numExpand++;
254	}
255
256	if (flags & ELF_iEXPAND_E) {
257	    int add = each;
258	    iPadX[PAD_BOTTOM_RIGHT] += add;
259	    layout->iWidth += add;
260	    layout->eWidth += add;
261	    spaceRemaining -= add;
262	    spaceUsed += add;
263	    if (!spaceRemaining)
264		break;
265	    numExpand++;
266	}
267
268	if (flags & ELF_iEXPAND_X) {
269	    int max = eLink1->maxWidth;
270	    if ((max < 0) || (layout->useWidth < max)) {
271		int add = (max < 0) ? each : MIN(each, max - layout->useWidth);
272		layout->useWidth += add;
273		layout->iWidth += add;
274		layout->eWidth += add;
275		spaceRemaining -= add;
276		spaceUsed += add;
277		if ((max >= 0) && (max == layout->useWidth))
278		    layout->temp--;
279		if (!spaceRemaining)
280		    break;
281		if ((max < 0) || (max > layout->useWidth))
282		    numExpand++;
283	    }
284	}
285
286	if (flags & ELF_iEXPAND_W) {
287	    int add = each;
288	    iPadX[PAD_TOP_LEFT] += add;
289	    layout->iWidth += add;
290	    layout->eWidth += add;
291	    spaceRemaining -= add;
292	    spaceUsed += add;
293	    if (!spaceRemaining)
294		break;
295	    numExpand++;
296	}
297
298	if (flags & ELF_eEXPAND_W) {
299	    int add = each;
300	    ePadX[PAD_TOP_LEFT] += add;
301	    layout->eWidth += add;
302	    spaceRemaining -= add;
303	    spaceUsed += add;
304	    if (!spaceRemaining)
305		break;
306	    numExpand++;
307	}
308    }
309
310    return spaceUsed;
311}
312
313/*
314 *----------------------------------------------------------------------
315 *
316 * Style_DoExpandV --
317 *
318 *	Add extra vertical space to an element. The space is
319 *	distributed from bottom to top until all available space
320 *	is used or expansion is not possible.
321 *
322 * Results:
323 *	Layout.ePadY, Layout.iPadY, and Layout.useHeight may be
324 *	updated. The amount of available space that was used is
325 *	returned.
326 *
327 * Side effects:
328 *	None.
329 *
330 *----------------------------------------------------------------------
331 */
332
333static int
334Style_DoExpandV(
335    struct Layout *layout,	/* Layout to be adjusted. */
336    int bottom			/* Limit of expansion. */
337    )
338{
339    MElementLink *eLink1 = layout->master;
340    int flags = eLink1->flags;
341    int numExpand = 0, spaceRemaining, spaceUsed = 0;
342    int *ePadY, *iPadY, *uPadY;
343
344    if (!(flags & (ELF_EXPAND_NS | ELF_iEXPAND_Y)))
345	return 0;
346
347    ePadY = layout->ePadY;
348    iPadY = layout->iPadY;
349    uPadY = layout->uPadY;
350
351    spaceRemaining = bottom - (layout->y + ePadY[PAD_TOP_LEFT] +
352	layout->iHeight + MAX(ePadY[PAD_BOTTOM_RIGHT], uPadY[PAD_BOTTOM_RIGHT]));
353    if (spaceRemaining <= 0)
354	return 0;
355
356    if (layout->temp)
357	numExpand = layout->temp;
358    /* For -detach or vertical layout, just set layout->temp to zero */
359    else {
360	if (flags & ELF_eEXPAND_N) numExpand++;
361	if (flags & ELF_iEXPAND_N) numExpand++;
362	if (flags & ELF_iEXPAND_Y) {
363	    if ((eLink1->maxHeight < 0) ||
364		(eLink1->maxHeight > layout->useHeight))
365		numExpand++;
366	}
367	if (flags & ELF_iEXPAND_S) numExpand++;
368	if (flags & ELF_eEXPAND_S) numExpand++;
369    }
370
371    while ((spaceRemaining > 0) && (numExpand > 0)) {
372	int each = (spaceRemaining >= numExpand) ? (spaceRemaining / numExpand) : 1;
373
374	numExpand = 0;
375
376	/* Allocate extra space to the *bottom* padding first so that any
377	 * extra single pixel is given to the bottom. */
378
379	if (flags & ELF_eEXPAND_S) {
380	    int add = each;
381	    ePadY[PAD_BOTTOM_RIGHT] += add;
382	    layout->eHeight += add;
383	    spaceRemaining -= add;
384	    spaceUsed += add;
385	    if (!spaceRemaining)
386		break;
387	    numExpand++;
388	}
389
390	if (flags & ELF_iEXPAND_S) {
391	    int add = each;
392	    iPadY[PAD_BOTTOM_RIGHT] += add;
393	    layout->iHeight += add;
394	    layout->eHeight += add;
395	    spaceRemaining -= add;
396	    spaceUsed += add;
397	    if (!spaceRemaining)
398		break;
399	    numExpand++;
400	}
401
402	if (flags & ELF_iEXPAND_Y) {
403	    int max = eLink1->maxHeight;
404	    if ((max < 0) || (layout->useHeight < max)) {
405		int add = (max < 0) ? each : MIN(each, max - layout->useHeight);
406		layout->useHeight += add;
407		layout->iHeight += add;
408		layout->eHeight += add;
409		spaceRemaining -= add;
410		spaceUsed += add;
411		if ((max >= 0) && (max == layout->useHeight))
412		    layout->temp--;
413		if (!spaceRemaining)
414		    break;
415		if ((max < 0) || (max > layout->useHeight))
416		    numExpand++;
417	    }
418	}
419
420	if (flags & ELF_iEXPAND_N) {
421	    int add = each;
422	    iPadY[PAD_TOP_LEFT] += add;
423	    layout->iHeight += add;
424	    layout->eHeight += add;
425	    spaceRemaining -= add;
426	    spaceUsed += add;
427	    if (!spaceRemaining)
428		break;
429	    numExpand++;
430	}
431
432	if (flags & ELF_eEXPAND_N) {
433	    int add = each;
434	    ePadY[PAD_TOP_LEFT] += add;
435	    layout->eHeight += add;
436	    spaceRemaining -= add;
437	    spaceUsed += add;
438	    if (!spaceRemaining)
439		break;
440	    numExpand++;
441	}
442    }
443
444    return spaceUsed;
445}
446
447/*
448 *----------------------------------------------------------------------
449 *
450 * ElementLink_NeededSize --
451 *
452 *	Calculate the needed width and height of an element.
453 *
454 * Results:
455 *	None.
456 *
457 * Side effects:
458 *	None.
459 *
460 *----------------------------------------------------------------------
461 */
462
463static void
464Element_NeededSize(
465    TreeCtrl *tree,		/* Widget info. */
466    MElementLink *eLink1,	/* Master style layout info. */
467    TreeElement elem,		/* Master/Instance element. */
468    int state,			/* STATE_xxx flags. */
469    int *widthPtr,		/* Out: width */
470    int *heightPtr		/* Out: height */
471    )
472{
473    TreeElementArgs args;
474    int width, height;
475
476    if ((eLink1->fixedWidth >= 0) && (eLink1->fixedHeight >= 0)) {
477	width = eLink1->fixedWidth;
478	height = eLink1->fixedHeight;
479    } else {
480	args.tree = tree;
481	args.state = state;
482	args.elem = elem;
483	args.needed.fixedWidth = eLink1->fixedWidth;
484	args.needed.fixedHeight = eLink1->fixedHeight;
485	if (eLink1->maxWidth > eLink1->minWidth)
486	    args.needed.maxWidth = eLink1->maxWidth;
487	else
488	    args.needed.maxWidth = -1;
489	if (eLink1->maxHeight > eLink1->minHeight)
490	    args.needed.maxHeight = eLink1->maxHeight;
491	else
492	    args.needed.maxHeight = -1;
493	(*args.elem->typePtr->neededProc)(&args);
494	width = args.needed.width;
495	height = args.needed.height;
496
497	if (eLink1->fixedWidth >= 0)
498	    width = eLink1->fixedWidth;
499	else if ((eLink1->minWidth >= 0) &&
500	    (width < eLink1->minWidth))
501	    width = eLink1->minWidth;
502	else if ((eLink1->maxWidth >= 0) &&
503	    (width > eLink1->maxWidth))
504	    width = eLink1->maxWidth;
505
506	if (eLink1->fixedHeight >= 0)
507	    height = eLink1->fixedHeight;
508	else if ((eLink1->minHeight >= 0) &&
509	    (height < eLink1->minHeight))
510	    height = eLink1->minHeight;
511	else if ((eLink1->maxHeight >= 0) &&
512	    (height > eLink1->maxHeight))
513	    height = eLink1->maxHeight;
514    }
515
516    *widthPtr = width;
517    *heightPtr = height;
518}
519
520/*
521 *----------------------------------------------------------------------
522 *
523 * Style_DoLayoutH --
524 *
525 *	Calculate the horizontal size and position of each element.
526 *	This gets called if the style -orient option is horizontal or
527 *	vertical.
528 *
529 * Results:
530 *	layouts[] is updated.
531 *
532 * Side effects:
533 *	None.
534 *
535 *----------------------------------------------------------------------
536 */
537
538static void
539Style_DoLayoutH(
540    StyleDrawArgs *drawArgs,	/* Various args. */
541    struct Layout layouts[]	/* Array of layout records to be
542				 * filled in, one per element. Should be
543				 * uninitialized. */
544    )
545{
546    IStyle *style = (IStyle *) drawArgs->style;
547    MStyle *masterStyle = style->master;
548    MElementLink *eLinks1, *eLink1;
549    IElementLink *eLinks2, *eLink2;
550    int x = drawArgs->indent;
551    int w, e;
552    int *ePadX, *iPadX, *uPadX, *ePadY, *iPadY, *uPadY;
553    int numExpandWE = 0;
554    int numSqueezeX = 0;
555    int i, j, eLinkCount = 0;
556    int rightEdge = 0;
557
558    eLinks1 = masterStyle->elements;
559    eLinks2 = style->elements;
560    eLinkCount = masterStyle->numElements;
561
562    for (i = 0; i < eLinkCount; i++) {
563	struct Layout *layout = &layouts[i];
564
565	eLink1 = &eLinks1[i];
566	eLink2 = &eLinks2[i];
567
568	layout->visible = PerStateBoolean_ForState(drawArgs->tree,
569		&eLink1->visible, drawArgs->state, NULL) != 0;
570	if (IS_HIDDEN(layout))
571	    continue;
572
573	layout->eLink = eLink2;
574	layout->master = eLink1;
575
576	/* Width before squeezing/expanding */
577	if (eLink1->onion != NULL) {
578	    layout->useWidth = 0;
579	} else {
580#ifdef CACHE_ELEM_SIZE
581	    layout->useWidth = eLink2->neededWidth;
582#else
583	    Element_NeededSize(drawArgs->tree, eLink1, eLink2->elem,
584		    drawArgs->state, &layout->neededWidth, &layout->neededHeight);
585	    layout->useWidth = layout->neededWidth;
586#endif
587	}
588
589	for (j = 0; j < 2; j++) {
590	    /* Pad values before expansion */
591	    layout->ePadX[j] = eLink1->ePadX[j];
592	    layout->ePadY[j] = eLink1->ePadY[j];
593	    layout->iPadX[j] = eLink1->iPadX[j];
594	    layout->iPadY[j] = eLink1->iPadY[j];
595
596	    /* No -union padding yet */
597	    layout->uPadX[j] = 0;
598	    layout->uPadY[j] = 0;
599	}
600
601	/* Count all non-union, non-detach squeezeable elements */
602	if (DETACH_OR_UNION(eLink1))
603	    continue;
604	if (eLink1->flags & ELF_SQUEEZE_X)
605	    numSqueezeX++;
606    }
607
608    /* Calculate the padding around elements surrounded by -union elements */
609    for (i = 0; i < eLinkCount; i++) {
610	struct Layout *layout = &layouts[i];
611	int first = -1, last = -1;
612
613	if (IS_HIDDEN(layout))
614	    continue;
615
616	eLink1 = &eLinks1[i];
617
618	if (eLink1->onion == NULL)
619	    continue;
620
621	for (j = 0; j < eLink1->onionCount; j++) {
622	    struct Layout *layout = &layouts[eLink1->onion[j]];
623
624	    if (IS_HIDDEN(layout))
625		continue;
626
627	    /* Remember the first and last visible elements surrounded by
628	     * this -union element. */
629	    if (first == -1)
630		first = j;
631	    last = j;
632	}
633
634	/* If there are no visible elements surrounded by this -union
635	 * element, then hide it. */
636	if (first == -1) {
637	    layout->visible = 0;
638	    continue;
639	}
640
641	ePadX = eLink1->ePadX;
642	ePadY = eLink1->ePadY;
643	iPadX = eLink1->iPadX;
644	iPadY = eLink1->iPadY;
645
646	for (j = 0; j < eLink1->onionCount; j++) {
647	    struct Layout *layout = &layouts[eLink1->onion[j]];
648
649	    if (IS_HIDDEN(layout))
650		continue;
651
652	    uPadX = layout->uPadX;
653	    uPadY = layout->uPadY;
654
655	    if (masterStyle->vertical) {
656		uPadX[PAD_TOP_LEFT] = MAX(uPadX[PAD_TOP_LEFT], iPadX[PAD_TOP_LEFT] + ePadX[PAD_TOP_LEFT]);
657		uPadX[PAD_BOTTOM_RIGHT] = MAX(uPadX[PAD_BOTTOM_RIGHT], iPadX[PAD_BOTTOM_RIGHT] + ePadX[PAD_BOTTOM_RIGHT]);
658		if (j == first) /* topmost */
659		    uPadY[PAD_TOP_LEFT] = MAX(uPadY[PAD_TOP_LEFT], iPadY[PAD_TOP_LEFT] + ePadY[PAD_TOP_LEFT]);
660		if (j == last) /* bottommost */
661		    uPadY[PAD_BOTTOM_RIGHT] = MAX(uPadY[PAD_BOTTOM_RIGHT], iPadY[PAD_BOTTOM_RIGHT] + ePadY[PAD_BOTTOM_RIGHT]);
662	    } else {
663		if (j == first) /* leftmost */
664		    uPadX[PAD_TOP_LEFT] = MAX(uPadX[PAD_TOP_LEFT], iPadX[PAD_TOP_LEFT] + ePadX[PAD_TOP_LEFT]);
665		if (j == last) /* rightmost */
666		    uPadX[PAD_BOTTOM_RIGHT] = MAX(uPadX[PAD_BOTTOM_RIGHT], iPadX[PAD_BOTTOM_RIGHT] + ePadX[PAD_BOTTOM_RIGHT]);
667		uPadY[PAD_TOP_LEFT] = MAX(uPadY[PAD_TOP_LEFT], iPadY[PAD_TOP_LEFT] + ePadY[PAD_TOP_LEFT]);
668		uPadY[PAD_BOTTOM_RIGHT] = MAX(uPadY[PAD_BOTTOM_RIGHT], iPadY[PAD_BOTTOM_RIGHT] + ePadY[PAD_BOTTOM_RIGHT]);
669	    }
670	}
671    }
672
673    /* Left-to-right layout. Make the width of some elements less than they
674     * need */
675    if (!masterStyle->vertical &&
676	    (drawArgs->width < style->neededWidth + drawArgs->indent) &&
677	    (numSqueezeX > 0)) {
678	int numSqueeze = numSqueezeX;
679	int spaceRemaining  = (style->neededWidth + drawArgs->indent) - drawArgs->width;
680
681	while ((spaceRemaining > 0) && (numSqueeze > 0)) {
682	    int each = (spaceRemaining >= numSqueeze) ? (spaceRemaining / numSqueeze) : 1;
683
684	    numSqueeze = 0;
685	    for (i = 0; i < eLinkCount; i++) {
686		struct Layout *layout = &layouts[i];
687		int min = 0;
688
689		if (IS_HIDDEN(layout))
690		    continue;
691
692		eLink1 = &eLinks1[i];
693
694		if (DETACH_OR_UNION(eLink1))
695		    continue;
696
697		if (!(eLink1->flags & ELF_SQUEEZE_X))
698		    continue;
699
700		if (eLink1->minWidth >= 0)
701		    min = eLink1->minWidth;
702		if (layout->useWidth > min) {
703		    int sub = MIN(each, layout->useWidth - min);
704		    layout->useWidth -= sub;
705		    spaceRemaining -= sub;
706		    if (!spaceRemaining) break;
707		    if (layout->useWidth > min)
708			numSqueeze++;
709		}
710	    }
711	}
712    }
713
714    /* Reduce the width of all non-union elements, except for the
715     * cases handled above. */
716    for (i = 0; i < eLinkCount; i++) {
717	struct Layout *layout = &layouts[i];
718	int width, subtract;
719
720	if (IS_HIDDEN(layout))
721	    continue;
722
723	eLink1 = &eLinks1[i];
724
725	if (eLink1->onion != NULL)
726	    continue;
727
728	if (!(eLink1->flags & ELF_SQUEEZE_X))
729	    continue;
730
731	if (!(eLink1->flags & ELF_DETACH) && !masterStyle->vertical)
732	    continue;
733
734	ePadX = eLink1->ePadX;
735	iPadX = eLink1->iPadX;
736	uPadX = layout->uPadX;
737
738	width =
739	    MAX(ePadX[PAD_TOP_LEFT], uPadX[PAD_TOP_LEFT]) +
740	    iPadX[PAD_TOP_LEFT] + layout->useWidth + iPadX[PAD_BOTTOM_RIGHT] +
741	    MAX(ePadX[PAD_BOTTOM_RIGHT], uPadX[PAD_BOTTOM_RIGHT]);
742	subtract = width - drawArgs->width;
743
744	if (!(eLink1->flags & ELF_DETACH) || (eLink1->flags & ELF_INDENT))
745	    subtract += drawArgs->indent;
746
747	if (subtract > 0) {
748	    if ((eLink1->minWidth >= 0) &&
749		(eLink1->minWidth <= layout->useWidth) &&
750		(layout->useWidth - subtract < eLink1->minWidth))
751		layout->useWidth = eLink1->minWidth;
752	    else
753		layout->useWidth -= subtract;
754	}
755    }
756
757    /* Layout elements left-to-right */
758    for (i = 0; i < eLinkCount; i++) {
759	struct Layout *layout = &layouts[i];
760	int right;
761
762	if (IS_HIDDEN(layout))
763	    continue;
764
765	eLink1 = &eLinks1[i];
766	eLink2 = &eLinks2[i];
767
768	if (DETACH_OR_UNION(eLink1))
769	    continue;
770
771	ePadX = eLink1->ePadX;
772	iPadX = eLink1->iPadX;
773	uPadX = layout->uPadX;
774
775	layout->x = x + abs(ePadX[PAD_TOP_LEFT] - MAX(ePadX[PAD_TOP_LEFT], uPadX[PAD_TOP_LEFT]));
776	layout->iWidth = iPadX[PAD_TOP_LEFT] + layout->useWidth + iPadX[PAD_BOTTOM_RIGHT];
777	layout->eWidth = ePadX[PAD_TOP_LEFT] + layout->iWidth + ePadX[PAD_BOTTOM_RIGHT];
778
779	right = layout->x + layout->ePadX[PAD_TOP_LEFT] +
780	    layout->iWidth +
781	    MAX(ePadX[PAD_BOTTOM_RIGHT], uPadX[PAD_BOTTOM_RIGHT]);
782
783	if (masterStyle->vertical)
784	    rightEdge = MAX(rightEdge, right);
785	else {
786	    rightEdge = right;
787	    x = layout->x + layout->eWidth;
788	}
789
790	/* Count number that want to expand */
791	if (eLink1->flags & (ELF_EXPAND_WE | ELF_iEXPAND_X))
792	    numExpandWE++;
793    }
794
795    /* Left-to-right layout. Expand some elements horizontally if we have
796     * more space available horizontally than is needed by the Style. */
797    if (!masterStyle->vertical &&
798	(drawArgs->width > rightEdge) &&
799	(numExpandWE > 0)) {
800	int numExpand = 0;
801	int spaceRemaining = drawArgs->width - rightEdge;
802
803	/* Each element has 5 areas that can optionally expand. */
804	for (i = 0; i < eLinkCount; i++) {
805	    struct Layout *layout = &layouts[i];
806
807	    if (IS_HIDDEN(layout))
808		continue;
809
810	    eLink1 = &eLinks1[i];
811
812	    layout->temp = 0;
813
814	    if (DETACH_OR_UNION(eLink1))
815		continue;
816
817	    if (eLink1->flags & ELF_eEXPAND_W) layout->temp++;
818	    if (eLink1->flags & ELF_iEXPAND_W) layout->temp++;
819	    if (eLink1->flags & ELF_iEXPAND_X) {
820		if ((eLink1->maxWidth < 0) ||
821		    (eLink1->maxWidth > layout->useWidth))
822		    layout->temp++;
823	    }
824	    if (eLink1->flags & ELF_iEXPAND_E) layout->temp++;
825	    if (eLink1->flags & ELF_eEXPAND_E) layout->temp++;
826
827	    numExpand += layout->temp;
828	}
829
830	while ((spaceRemaining > 0) && (numExpand > 0)) {
831	    int each = (spaceRemaining >= numExpand) ? spaceRemaining / numExpand : 1;
832
833	    numExpand = 0;
834	    for (i = 0; i < eLinkCount; i++) {
835		struct Layout *layout = &layouts[i];
836		int spaceUsed;
837
838		if (IS_HIDDEN(layout))
839		    continue;
840
841		if (!layout->temp)
842		    continue;
843
844		eLink1 = &eLinks1[i];
845
846		spaceUsed = Style_DoExpandH(layout,
847		    layout->x + layout->ePadX[PAD_TOP_LEFT] + layout->iWidth +
848		    MAX(layout->ePadX[PAD_BOTTOM_RIGHT], layout->uPadX[PAD_BOTTOM_RIGHT]) +
849		    MIN(each * layout->temp, spaceRemaining));
850
851		if (spaceUsed) {
852		    /* Shift following elements to the right */
853		    for (j = i + 1; j < eLinkCount; j++)
854			if (!DETACH_OR_UNION(&eLinks1[j]))
855			    layouts[j].x += spaceUsed;
856
857		    rightEdge += spaceUsed;
858
859		    spaceRemaining -= spaceUsed;
860		    if (!spaceRemaining)
861			break;
862
863		    numExpand += layout->temp;
864		} else
865		    layout->temp = 0;
866	    }
867	}
868    }
869
870    /* Top-to-bottom layout. Expand some elements horizontally */
871    if (masterStyle->vertical && (numExpandWE > 0)) {
872	for (i = 0; i < eLinkCount; i++) {
873	    struct Layout *layout = &layouts[i];
874	    int right;
875
876	    if (IS_HIDDEN(layout))
877		continue;
878
879	    eLink1 = &eLinks1[i];
880
881	    if (DETACH_OR_UNION(eLink1))
882		continue;
883
884	    layout->temp = 0;
885	    Style_DoExpandH(layout, drawArgs->width);
886
887	    right = layout->x + layout->ePadX[PAD_TOP_LEFT] +
888		layout->iWidth +
889		MAX(layout->ePadX[PAD_BOTTOM_RIGHT], layout->uPadX[PAD_BOTTOM_RIGHT]);
890	    rightEdge = MAX(rightEdge, right);
891	}
892    }
893
894    /* Now handle column justification */
895    /* All the non-union, non-detach elements are moved as a group */
896    if (drawArgs->width > rightEdge) {
897	int dx = drawArgs->width - rightEdge;
898
899	for (i = 0; i < eLinkCount; i++) {
900	    struct Layout *layout = &layouts[i];
901
902	    if (IS_HIDDEN(layout))
903		continue;
904
905	    eLink1 = &eLinks1[i];
906
907	    if (DETACH_OR_UNION(eLink1))
908		continue;
909
910	    switch (drawArgs->justify) {
911		case TK_JUSTIFY_LEFT:
912		    break;
913		case TK_JUSTIFY_RIGHT:
914		    layout->x += dx;
915		    break;
916		case TK_JUSTIFY_CENTER:
917		    layout->x += dx / 2;
918		    break;
919	    }
920	}
921    }
922
923    /* Position and expand -detach elements */
924    for (i = 0; i < eLinkCount; i++) {
925	struct Layout *layout = &layouts[i];
926
927	if (IS_HIDDEN(layout))
928	    continue;
929
930	eLink1 = &eLinks1[i];
931	eLink2 = &eLinks2[i];
932
933	if (!(eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL))
934	    continue;
935
936	ePadX = eLink1->ePadX;
937	iPadX = eLink1->iPadX;
938	uPadX = layout->uPadX;
939
940	layout->x = abs(ePadX[PAD_TOP_LEFT] - MAX(ePadX[PAD_TOP_LEFT], uPadX[PAD_TOP_LEFT]));
941	if (eLink1->flags & ELF_INDENT)
942	    layout->x += drawArgs->indent;
943	layout->iWidth = iPadX[PAD_TOP_LEFT] + layout->useWidth + iPadX[PAD_BOTTOM_RIGHT];
944	layout->eWidth = ePadX[PAD_TOP_LEFT] + layout->iWidth + ePadX[PAD_BOTTOM_RIGHT];
945
946	layout->temp = 0;
947	Style_DoExpandH(layout, drawArgs->width);
948    }
949
950    /* Now calculate layout of -union elements. */
951    for (i = 0; i < eLinkCount; i++) {
952	struct Layout *layout = &layouts[i];
953
954	eLink1 = &eLinks1[i];
955	eLink2 = &eLinks2[i];
956
957	if (IS_HIDDEN(layout))
958	    continue;
959
960	if (eLink1->onion == NULL)
961	    continue;
962
963	ePadX = eLink1->ePadX;
964	iPadX = eLink1->iPadX;
965
966	w = 1000000, e = -1000000;
967
968	for (j = 0; j < eLink1->onionCount; j++) {
969	    struct Layout *layout2 = &layouts[eLink1->onion[j]];
970
971	    if (IS_HIDDEN(layout2))
972		continue;
973
974	    w = MIN(w, layout2->x + layout2->ePadX[PAD_TOP_LEFT]);
975	    e = MAX(e, layout2->x + layout2->ePadX[PAD_TOP_LEFT] + layout2->iWidth);
976	}
977
978	layout->x = w - iPadX[PAD_TOP_LEFT] - ePadX[PAD_TOP_LEFT];
979	layout->useWidth = (e - w);
980	layout->iWidth = iPadX[PAD_TOP_LEFT] + layout->useWidth + iPadX[PAD_BOTTOM_RIGHT];
981	layout->eWidth = ePadX[PAD_TOP_LEFT] + layout->iWidth + ePadX[PAD_BOTTOM_RIGHT];
982    }
983
984    /* Expand -union elements if needed: horizontal */
985    /* Expansion of "-union" elements is different than non-"-union" elements */
986    for (i = 0; i < eLinkCount; i++) {
987	struct Layout *layout = &layouts[i];
988	int extraWidth;
989
990	if (IS_HIDDEN(layout))
991	    continue;
992
993	eLink1 = &eLinks1[i];
994
995	if ((eLink1->onion == NULL) || !(eLink1->flags & ELF_EXPAND_WE))
996	    continue;
997
998	if (drawArgs->width - (layout->eWidth + drawArgs->indent) <= 0)
999	    continue;
1000
1001	/* External and internal expansion: W */
1002	extraWidth = layout->x - drawArgs->indent;
1003	if ((extraWidth > 0) && (eLink1->flags & ELF_EXPAND_W)) {
1004	    if ((eLink1->flags & ELF_EXPAND_W) == ELF_EXPAND_W) {
1005		int eExtra = extraWidth / 2;
1006		int iExtra = extraWidth - extraWidth / 2;
1007
1008		layout->x = drawArgs->indent;
1009
1010		/* External expansion */
1011		layout->ePadX[PAD_TOP_LEFT] += eExtra;
1012		layout->eWidth += extraWidth;
1013
1014		/* Internal expansion */
1015		layout->iPadX[PAD_TOP_LEFT] += iExtra;
1016		layout->iWidth += iExtra;
1017	    }
1018
1019	    /* External expansion only: W */
1020	    else if (eLink1->flags & ELF_eEXPAND_W) {
1021		layout->ePadX[PAD_TOP_LEFT] += extraWidth;
1022		layout->x = drawArgs->indent;
1023		layout->eWidth += extraWidth;
1024	    }
1025
1026	    /* Internal expansion only: W */
1027	    else {
1028		layout->iPadX[PAD_TOP_LEFT] += extraWidth;
1029		layout->x = drawArgs->indent;
1030		layout->iWidth += extraWidth;
1031		layout->eWidth += extraWidth;
1032	    }
1033	}
1034
1035	/* External and internal expansion: E */
1036	extraWidth = drawArgs->width - (layout->x + layout->eWidth);
1037	if ((extraWidth > 0) && (eLink1->flags & ELF_EXPAND_E)) {
1038	    if ((eLink1->flags & ELF_EXPAND_E) == ELF_EXPAND_E) {
1039		int eExtra = extraWidth / 2;
1040		int iExtra = extraWidth - extraWidth / 2;
1041
1042		/* External expansion */
1043		layout->ePadX[PAD_BOTTOM_RIGHT] += eExtra;
1044		layout->eWidth += extraWidth; /* all the space */
1045
1046		/* Internal expansion */
1047		layout->iPadX[PAD_BOTTOM_RIGHT] += iExtra;
1048		layout->iWidth += iExtra;
1049	    }
1050
1051	    /* External expansion only: E */
1052	    else if (eLink1->flags & ELF_eEXPAND_E) {
1053		layout->ePadX[PAD_BOTTOM_RIGHT] += extraWidth;
1054		layout->eWidth += extraWidth;
1055	    }
1056
1057	    /* Internal expansion only: E */
1058	    else {
1059		layout->iPadX[PAD_BOTTOM_RIGHT] += extraWidth;
1060		layout->iWidth += extraWidth;
1061		layout->eWidth += extraWidth;
1062	    }
1063	}
1064    }
1065
1066    /* Add internal padding to display area for -union elements */
1067    for (i = 0; i < eLinkCount; i++) {
1068	struct Layout *layout = &layouts[i];
1069
1070	if (IS_HIDDEN(layout))
1071	    continue;
1072
1073	eLink1 = &eLinks1[i];
1074
1075	if (eLink1->onion == NULL)
1076	    continue;
1077
1078	iPadX = layout->iPadX;
1079
1080	layout->useWidth += iPadX[PAD_TOP_LEFT] + iPadX[PAD_BOTTOM_RIGHT];
1081	iPadX[PAD_TOP_LEFT] = iPadX[PAD_BOTTOM_RIGHT] = 0;
1082    }
1083}
1084
1085/*
1086 *----------------------------------------------------------------------
1087 *
1088 * Style_DoLayoutV --
1089 *
1090 *	Calculate the vertical size and position of each element.
1091 *	This gets called if the style -orient option is horizontal or
1092 *	vertical.
1093 *
1094 * Results:
1095 *	layouts[] is updated.
1096 *
1097 * Side effects:
1098 *	None.
1099 *
1100 *----------------------------------------------------------------------
1101 */
1102
1103static void
1104Style_DoLayoutV(
1105    StyleDrawArgs *drawArgs,	/* Various args. */
1106    struct Layout layouts[]	/* Array of layout records to be updated,
1107				 * one per element. Should be initialized
1108				 * by Style_DoLayoutH(). */
1109    )
1110{
1111    IStyle *style = (IStyle *) drawArgs->style;
1112    MStyle *masterStyle = style->master;
1113    MElementLink *eLinks1, *eLink1;
1114    IElementLink *eLinks2, *eLink2;
1115    int y = 0;
1116    int n, s;
1117    int *ePadY, *iPadY, *uPadY;
1118    int numExpandNS = 0;
1119    int numSqueezeY = 0;
1120    int i, j, eLinkCount = 0;
1121    int bottomEdge = 0;
1122
1123    eLinks1 = masterStyle->elements;
1124    eLinks2 = style->elements;
1125    eLinkCount = masterStyle->numElements;
1126
1127    for (i = 0; i < eLinkCount; i++) {
1128	if (IS_HIDDEN(&layouts[i]))
1129	    continue;
1130
1131	eLink1 = &eLinks1[i];
1132
1133	/* Count all non-union, non-detach squeezeable elements */
1134	if (DETACH_OR_UNION(eLink1))
1135	    continue;
1136	if (eLink1->flags & ELF_SQUEEZE_Y)
1137	    numSqueezeY++;
1138    }
1139
1140    /* Top-top-bottom layout. Make the height of some elements less than they
1141     * need */
1142    if (masterStyle->vertical &&
1143	    (drawArgs->height < style->neededHeight) &&
1144	    (numSqueezeY > 0)) {
1145	int numSqueeze = numSqueezeY;
1146	int spaceRemaining  = style->neededHeight - drawArgs->height;
1147
1148	while ((spaceRemaining > 0) && (numSqueeze > 0)) {
1149	    int each = (spaceRemaining >= numSqueeze) ? (spaceRemaining / numSqueeze) : 1;
1150
1151	    numSqueeze = 0;
1152	    for (i = 0; i < eLinkCount; i++) {
1153		struct Layout *layout = &layouts[i];
1154		int min = 0;
1155
1156		if (IS_HIDDEN(layout))
1157		    continue;
1158
1159		eLink1 = &eLinks1[i];
1160
1161		if (DETACH_OR_UNION(eLink1))
1162		    continue;
1163
1164		if (!(eLink1->flags & ELF_SQUEEZE_Y))
1165		    continue;
1166
1167		if (eLink1->minHeight >= 0)
1168		    min = eLink1->minHeight;
1169		if (layout->useHeight > min) {
1170		    int sub = MIN(each, layout->useHeight - min);
1171		    layout->useHeight -= sub;
1172		    spaceRemaining -= sub;
1173		    if (!spaceRemaining) break;
1174		    if (layout->useHeight > min)
1175			numSqueeze++;
1176		}
1177	    }
1178	}
1179    }
1180
1181    /* Reduce the height of all non-union elements, except for the
1182     * cases handled above. */
1183    if (drawArgs->height < style->neededHeight) {
1184	for (i = 0; i < eLinkCount; i++) {
1185	    struct Layout *layout = &layouts[i];
1186	    int height, subtract;
1187
1188	    if (IS_HIDDEN(layout))
1189		continue;
1190
1191	    eLink1 = &eLinks1[i];
1192
1193	    if (eLink1->onion != NULL)
1194		continue;
1195
1196	    if (!(eLink1->flags & ELF_SQUEEZE_Y))
1197		continue;
1198
1199	    if (!(eLink1->flags & ELF_DETACH) && masterStyle->vertical)
1200		continue;
1201
1202	    ePadY = eLink1->ePadY;
1203	    iPadY = eLink1->iPadY;
1204	    uPadY = layout->uPadY;
1205
1206	    height =
1207		MAX(ePadY[PAD_TOP_LEFT], uPadY[PAD_TOP_LEFT]) +
1208		iPadY[PAD_TOP_LEFT] + layout->useHeight + iPadY[PAD_BOTTOM_RIGHT] +
1209		MAX(ePadY[PAD_BOTTOM_RIGHT], uPadY[PAD_BOTTOM_RIGHT]);
1210	    subtract = height - drawArgs->height;
1211
1212	    if (subtract > 0) {
1213		if ((eLink1->minHeight >= 0) &&
1214		    (eLink1->minHeight <= layout->useHeight) &&
1215		    (layout->useHeight - subtract < eLink1->minHeight))
1216		    layout->useHeight = eLink1->minHeight;
1217		else
1218		    layout->useHeight -= subtract;
1219	    }
1220	}
1221    }
1222
1223    /* Layout elements top-to-bottom */
1224    for (i = 0; i < eLinkCount; i++) {
1225	struct Layout *layout = &layouts[i];
1226
1227	if (IS_HIDDEN(layout))
1228	    continue;
1229
1230	eLink1 = &eLinks1[i];
1231	eLink2 = &eLinks2[i];
1232
1233	if (DETACH_OR_UNION(eLink1))
1234	    continue;
1235
1236	ePadY = eLink1->ePadY;
1237	iPadY = eLink1->iPadY;
1238	uPadY = layout->uPadY;
1239
1240	layout->y = y + abs(ePadY[PAD_TOP_LEFT] - MAX(ePadY[PAD_TOP_LEFT], uPadY[PAD_TOP_LEFT]));
1241	layout->iHeight = iPadY[PAD_TOP_LEFT] + layout->useHeight + iPadY[PAD_BOTTOM_RIGHT];
1242	layout->eHeight = ePadY[PAD_TOP_LEFT] + layout->iHeight + ePadY[PAD_BOTTOM_RIGHT];
1243
1244	if (masterStyle->vertical)
1245	    y = layout->y + layout->eHeight;
1246
1247	if (masterStyle->vertical) {
1248	    bottomEdge = layout->y + layout->ePadY[PAD_TOP_LEFT] +
1249		layout->iHeight +
1250		MAX(ePadY[PAD_BOTTOM_RIGHT], uPadY[PAD_BOTTOM_RIGHT]);
1251	}
1252
1253	/* Count number that want to expand */
1254	if (eLink1->flags & (ELF_EXPAND_NS | ELF_iEXPAND_Y))
1255	    numExpandNS++;
1256    }
1257
1258    /* Top-to-bottom layout. Expand some elements vertically if we have
1259     * more space available vertically than is needed by the Style. */
1260    if (masterStyle->vertical &&
1261	(drawArgs->height > bottomEdge) &&
1262	(numExpandNS > 0)) {
1263	int numExpand = 0;
1264	int spaceRemaining = drawArgs->height - bottomEdge;
1265
1266	for (i = 0; i < eLinkCount; i++) {
1267	    struct Layout *layout = &layouts[i];
1268
1269	    if (IS_HIDDEN(layout))
1270		continue;
1271
1272	    eLink1 = &eLinks1[i];
1273
1274	    layout->temp = 0;
1275
1276	    if (DETACH_OR_UNION(eLink1))
1277		continue;
1278
1279	    if (eLink1->flags & ELF_eEXPAND_N) layout->temp++;
1280	    if (eLink1->flags & ELF_iEXPAND_N) layout->temp++;
1281	    if (eLink1->flags & ELF_iEXPAND_Y) {
1282		if ((eLink1->maxHeight < 0) ||
1283		    (eLink1->maxHeight > layout->useHeight))
1284		    layout->temp++;
1285	    }
1286	    if (eLink1->flags & ELF_iEXPAND_S) layout->temp++;
1287	    if (eLink1->flags & ELF_eEXPAND_S) layout->temp++;
1288
1289	    numExpand += layout->temp;
1290	}
1291
1292	while ((spaceRemaining > 0) && (numExpand > 0)) {
1293	    int each = (spaceRemaining >= numExpand) ? spaceRemaining / numExpand : 1;
1294
1295	    numExpand = 0;
1296	    for (i = 0; i < eLinkCount; i++) {
1297		struct Layout *layout = &layouts[i];
1298		int spaceUsed;
1299
1300		if (IS_HIDDEN(layout))
1301		    continue;
1302
1303		if (!layout->temp)
1304		    continue;
1305
1306		eLink1 = &eLinks1[i];
1307
1308		spaceUsed = Style_DoExpandV(layout,
1309		    layout->y + layout->ePadY[PAD_TOP_LEFT] + layout->iHeight +
1310		    MAX(layout->ePadY[PAD_BOTTOM_RIGHT], layout->uPadY[PAD_BOTTOM_RIGHT]) +
1311		    MIN(each * layout->temp, spaceRemaining));
1312
1313		if (spaceUsed) {
1314		    /* Shift following elements down */
1315		    for (j = i + 1; j < eLinkCount; j++)
1316			if (!DETACH_OR_UNION(&eLinks1[j]))
1317			    layouts[j].y += spaceUsed;
1318
1319		    spaceRemaining -= spaceUsed;
1320		    if (!spaceRemaining)
1321			break;
1322
1323		    numExpand += layout->temp;
1324		} else
1325		    layout->temp = 0;
1326	    }
1327	}
1328    }
1329
1330    /* Left-to-right layout. Expand some elements vertically */
1331    if (!masterStyle->vertical && (numExpandNS > 0)) {
1332	for (i = 0; i < eLinkCount; i++) {
1333	    struct Layout *layout = &layouts[i];
1334
1335	    if (IS_HIDDEN(layout))
1336		continue;
1337
1338	    eLink1 = &eLinks1[i];
1339
1340	    if (DETACH_OR_UNION(eLink1))
1341		continue;
1342
1343	    layout->temp = 0;
1344	    Style_DoExpandV(layout, drawArgs->height);
1345	}
1346    }
1347
1348    /* Position and expand -detach elements */
1349    for (i = 0; i < eLinkCount; i++) {
1350	struct Layout *layout = &layouts[i];
1351
1352	if (IS_HIDDEN(layout))
1353	    continue;
1354
1355	eLink1 = &eLinks1[i];
1356	eLink2 = &eLinks2[i];
1357
1358	if (!(eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL))
1359	    continue;
1360
1361	ePadY = eLink1->ePadY;
1362	iPadY = eLink1->iPadY;
1363	uPadY = layout->uPadY;
1364
1365	layout->y = abs(ePadY[PAD_TOP_LEFT] - MAX(ePadY[PAD_TOP_LEFT], uPadY[PAD_TOP_LEFT]));
1366	layout->iHeight = iPadY[PAD_TOP_LEFT] + layout->useHeight + iPadY[PAD_BOTTOM_RIGHT];
1367	layout->eHeight = ePadY[PAD_TOP_LEFT] + layout->iHeight + ePadY[PAD_BOTTOM_RIGHT];
1368
1369	layout->temp = 0;
1370	Style_DoExpandV(layout, drawArgs->height);
1371    }
1372
1373    /* Now calculate layout of -union elements. */
1374    for (i = 0; i < eLinkCount; i++) {
1375	struct Layout *layout = &layouts[i];
1376
1377	if (IS_HIDDEN(layout))
1378	    continue;
1379
1380	eLink1 = &eLinks1[i];
1381	eLink2 = &eLinks2[i];
1382
1383	if (eLink1->onion == NULL)
1384	    continue;
1385
1386	ePadY = eLink1->ePadY;
1387	iPadY = eLink1->iPadY;
1388
1389	n = 1000000, s = -1000000;
1390
1391	for (j = 0; j < eLink1->onionCount; j++) {
1392	    struct Layout *layout2 = &layouts[eLink1->onion[j]];
1393
1394	    if (IS_HIDDEN(layout2))
1395		continue;
1396
1397	    n = MIN(n, layout2->y + layout2->ePadY[PAD_TOP_LEFT]);
1398	    s = MAX(s, layout2->y + layout2->ePadY[PAD_TOP_LEFT] + layout2->iHeight);
1399	}
1400
1401	layout->y = n - iPadY[PAD_TOP_LEFT] - ePadY[PAD_TOP_LEFT];
1402	layout->useHeight = (s - n);
1403	layout->iHeight = iPadY[PAD_TOP_LEFT] + layout->useHeight + iPadY[PAD_BOTTOM_RIGHT];
1404	layout->eHeight = ePadY[PAD_TOP_LEFT] + layout->iHeight + ePadY[PAD_BOTTOM_RIGHT];
1405    }
1406
1407    /* Expand -union elements if needed: vertical */
1408    for (i = 0; i < eLinkCount; i++) {
1409	struct Layout *layout = &layouts[i];
1410	int extraHeight;
1411
1412	if (IS_HIDDEN(layout))
1413	    continue;
1414
1415	eLink1 = &eLinks1[i];
1416
1417	if ((eLink1->onion == NULL) || !(eLink1->flags & ELF_EXPAND_NS))
1418	    continue;
1419
1420	if (drawArgs->height - layout->eHeight <= 0)
1421	    continue;
1422
1423	/* External and internal expansion: N */
1424	extraHeight = layout->y;
1425	if ((extraHeight > 0) && (eLink1->flags & ELF_EXPAND_N)) {
1426	    if ((eLink1->flags & ELF_EXPAND_N) == ELF_EXPAND_N) {
1427		int eExtra = extraHeight / 2;
1428		int iExtra = extraHeight - extraHeight / 2;
1429
1430		/* External expansion */
1431		layout->ePadY[PAD_TOP_LEFT] += eExtra;
1432		layout->y = 0;
1433		layout->eHeight += extraHeight;
1434
1435		/* Internal expansion */
1436		layout->iPadY[PAD_TOP_LEFT] += iExtra;
1437		layout->iHeight += iExtra;
1438	    }
1439
1440	    /* External expansion only: N */
1441	    else if (eLink1->flags & ELF_eEXPAND_N) {
1442		layout->ePadY[PAD_TOP_LEFT] += extraHeight;
1443		layout->y = 0;
1444		layout->eHeight += extraHeight;
1445	    }
1446
1447	    /* Internal expansion only: N */
1448	    else {
1449		layout->iPadY[PAD_TOP_LEFT] += extraHeight;
1450		layout->y = 0;
1451		layout->iHeight += extraHeight;
1452		layout->eHeight += extraHeight;
1453	    }
1454	}
1455
1456	/* External and internal expansion: S */
1457	extraHeight = drawArgs->height - (layout->y + layout->eHeight);
1458	if ((extraHeight > 0) && (eLink1->flags & ELF_EXPAND_S)) {
1459	    if ((eLink1->flags & ELF_EXPAND_S) == ELF_EXPAND_S) {
1460		int eExtra = extraHeight / 2;
1461		int iExtra = extraHeight - extraHeight / 2;
1462
1463		/* External expansion */
1464		layout->ePadY[PAD_BOTTOM_RIGHT] += eExtra;
1465		layout->eHeight += extraHeight; /* all the space */
1466
1467		/* Internal expansion */
1468		layout->iPadY[PAD_BOTTOM_RIGHT] += iExtra;
1469		layout->iHeight += iExtra;
1470	    }
1471
1472	    /* External expansion only: S */
1473	    else if (eLink1->flags & ELF_eEXPAND_S) {
1474		layout->ePadY[PAD_BOTTOM_RIGHT] += extraHeight;
1475		layout->eHeight += extraHeight;
1476	    }
1477
1478	    /* Internal expansion only */
1479	    else {
1480		layout->iPadY[PAD_BOTTOM_RIGHT] += extraHeight;
1481		layout->iHeight += extraHeight;
1482		layout->eHeight += extraHeight;
1483	    }
1484	}
1485    }
1486
1487    /* Add internal padding to display area for -union elements */
1488    for (i = 0; i < eLinkCount; i++) {
1489	struct Layout *layout = &layouts[i];
1490
1491	if (IS_HIDDEN(layout))
1492	    continue;
1493
1494	eLink1 = &eLinks1[i];
1495
1496	if (eLink1->onion == NULL)
1497	    continue;
1498
1499	iPadY = layout->iPadY;
1500
1501	layout->useHeight += iPadY[PAD_TOP_LEFT] + iPadY[PAD_BOTTOM_RIGHT];
1502	iPadY[PAD_TOP_LEFT] = iPadY[PAD_BOTTOM_RIGHT] = 0;
1503    }
1504}
1505
1506/*
1507 *----------------------------------------------------------------------
1508 *
1509 * Layout_Size --
1510 *
1511 *	Calculate the height and width of a style after all the
1512 *	elements have been arranged.
1513 *
1514 * Results:
1515 *	The height and width of the style.
1516 *
1517 * Side effects:
1518 *	None.
1519 *
1520 *----------------------------------------------------------------------
1521 */
1522
1523static void
1524Layout_Size(
1525    int vertical,		/* TRUE if elements are arranged from top
1526				 * to bottom. */
1527    int numLayouts,		/* Number of layout records. */
1528    struct Layout layouts[],	/* Initialized layout records. */
1529    int *widthPtr,		/* Returned width. */
1530    int *heightPtr		/* Returned height. */
1531    )
1532{
1533    int i, W, N, E, S;
1534    int width = 0, height = 0;
1535
1536    W = 1000000, N = 1000000, E = -1000000, S = -1000000;
1537
1538    for (i = 0; i < numLayouts; i++) {
1539	struct Layout *layout = &layouts[i];
1540	int w, n, e, s;
1541	int *ePadX, *iPadX, *uPadX, *ePadY, *iPadY, *uPadY;
1542
1543	if (IS_HIDDEN(layout))
1544	    continue;
1545
1546	ePadX = layout->ePadX, iPadX = layout->iPadX, uPadX = layout->uPadX;
1547	ePadY = layout->ePadY, iPadY = layout->iPadY, uPadY = layout->uPadY;
1548
1549	w = layout->x + ePadX[PAD_TOP_LEFT] - MAX(ePadX[PAD_TOP_LEFT], uPadX[PAD_TOP_LEFT]);
1550	n = layout->y + ePadY[PAD_TOP_LEFT] - MAX(ePadY[PAD_TOP_LEFT], uPadY[PAD_TOP_LEFT]);
1551	e = layout->x + layout->eWidth - ePadX[PAD_BOTTOM_RIGHT] + MAX(ePadX[PAD_BOTTOM_RIGHT], uPadX[PAD_BOTTOM_RIGHT]);
1552	s = layout->y + layout->eHeight - ePadY[PAD_BOTTOM_RIGHT] + MAX(ePadY[PAD_BOTTOM_RIGHT], uPadY[PAD_BOTTOM_RIGHT]);
1553
1554	if (vertical) {
1555	    N = MIN(N, n);
1556	    S = MAX(S, s);
1557	    width = MAX(width, e - w);
1558	} else {
1559	    W = MIN(W, w);
1560	    E = MAX(E, e);
1561	    height = MAX(height, s - n);
1562	}
1563    }
1564
1565    if (vertical)
1566	height = MAX(height, S - N);
1567    else
1568	width = MAX(width, E - W);
1569
1570    (*widthPtr) = width;
1571    (*heightPtr) = height;
1572}
1573
1574/*
1575 *----------------------------------------------------------------------
1576 *
1577 * Style_DoLayoutNeededV --
1578 *
1579 *	Calculate the vertical size and position of each element.
1580 *	This is similar to Style_DoLayoutV but without expansion or
1581 *	squeezing. Also, the size and position of -union elements
1582 *	is not calculated.
1583 *
1584 * Results:
1585 *	layouts[] is updated.
1586 *
1587 * Side effects:
1588 *	None.
1589 *
1590 *----------------------------------------------------------------------
1591 */
1592
1593static void
1594Style_DoLayoutNeededV(
1595    StyleDrawArgs *drawArgs,	/* Various args. */
1596    struct Layout layouts[]	/* Array of layout records to be updated,
1597				 * one per element. Should be initialized
1598				 * by Style_DoLayoutH(). */
1599    )
1600{
1601    IStyle *style = (IStyle *) drawArgs->style;
1602    MStyle *masterStyle = style->master;
1603    MElementLink *eLinks1, *eLink1;
1604    IElementLink *eLinks2, *eLink2;
1605    int *ePadY, *iPadY, *uPadY;
1606    int i;
1607    int y = 0;
1608
1609    eLinks1 = masterStyle->elements;
1610    eLinks2 = style->elements;
1611
1612    /* Layout elements left-to-right, or top-to-bottom */
1613    for (i = 0; i < masterStyle->numElements; i++) {
1614	struct Layout *layout = &layouts[i];
1615
1616	if (IS_HIDDEN(layout))
1617	    continue;
1618
1619	eLink1 = &eLinks1[i];
1620	eLink2 = &eLinks2[i];
1621
1622	/* The size of a -union element is determined by the elements
1623	 * it surrounds */
1624	if (eLink1->onion != NULL) {
1625	    /* I don't need good values because I'm only calculating the
1626	     * needed height */
1627	    layout->y = layout->iHeight = layout->eHeight = 0;
1628	    continue;
1629	}
1630
1631	/* -detach elements are positioned by themselves */
1632	if (eLink1->flags & ELF_DETACH)
1633	    continue;
1634
1635	ePadY = eLink1->ePadY;
1636	iPadY = eLink1->iPadY;
1637	uPadY = layout->uPadY;
1638
1639	layout->y = y + abs(ePadY[PAD_TOP_LEFT] - MAX(ePadY[PAD_TOP_LEFT], uPadY[PAD_TOP_LEFT]));
1640	layout->iHeight = iPadY[PAD_TOP_LEFT] + layout->useHeight + iPadY[PAD_BOTTOM_RIGHT];
1641	layout->eHeight = ePadY[PAD_TOP_LEFT] + layout->iHeight + ePadY[PAD_BOTTOM_RIGHT];
1642
1643	if (masterStyle->vertical)
1644	    y = layout->y + layout->eHeight;
1645    }
1646
1647    /* -detach elements */
1648    for (i = 0; i < masterStyle->numElements; i++) {
1649	struct Layout *layout = &layouts[i];
1650
1651	if (IS_HIDDEN(layout))
1652	    continue;
1653
1654	eLink1 = &eLinks1[i];
1655	eLink2 = &eLinks2[i];
1656
1657	if (!(eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL))
1658	    continue;
1659
1660	ePadY = eLink1->ePadY;
1661	iPadY = eLink1->iPadY;
1662	uPadY = layout->uPadY;
1663
1664	layout->y = abs(ePadY[PAD_TOP_LEFT] - MAX(ePadY[PAD_TOP_LEFT], uPadY[PAD_TOP_LEFT]));
1665	layout->iHeight = iPadY[PAD_TOP_LEFT] + layout->useHeight + iPadY[PAD_BOTTOM_RIGHT];
1666	layout->eHeight = ePadY[PAD_TOP_LEFT] + layout->iHeight + ePadY[PAD_BOTTOM_RIGHT];
1667    }
1668}
1669
1670/*
1671 *----------------------------------------------------------------------
1672 *
1673 * Style_DoLayout --
1674 *
1675 *	Calculate the size and position of each element.
1676 *
1677 * Results:
1678 *	layouts[] is updated.
1679 *
1680 * Side effects:
1681 *	None.
1682 *
1683 *----------------------------------------------------------------------
1684 */
1685
1686/* Arrange all the Elements considering drawArgs.width and maybe drawArgs.height */
1687static void
1688Style_DoLayout(
1689    StyleDrawArgs *drawArgs,	/* Various args. */
1690    struct Layout layouts[],	/* Uninitialized records to be filled in. */
1691    int neededV,		/* TRUE if drawArgs.height should be ignored. */
1692    char *file,			/* debug */
1693    int line			/* debug */
1694    )
1695{
1696    TreeCtrl *tree = drawArgs->tree;
1697    IStyle *style = (IStyle *) drawArgs->style;
1698    MStyle *masterStyle = style->master;
1699    int state = drawArgs->state;
1700    int i;
1701
1702    if (style->neededWidth == -1)
1703	panic("Style_DoLayout(file %s line %d): style.neededWidth == -1",
1704	    file, line);
1705#ifdef CACHE_STYLE_STYLE
1706    if (style->minWidth + drawArgs->indent > drawArgs->width)
1707	panic("Style_DoLayout(file %s line %d): style.minWidth + drawArgs->indent %d > drawArgs.width %d",
1708	    file, line, style->minWidth + drawArgs->indent, drawArgs->width);
1709#endif
1710    Style_DoLayoutH(drawArgs, layouts);
1711
1712    for (i = 0; i < masterStyle->numElements; i++) {
1713	struct Layout *layout = &layouts[i];
1714	MElementLink *eLink1 = layout->master;
1715	IElementLink *eLink2 = layout->eLink;
1716	TreeElementArgs args;
1717
1718	if (IS_HIDDEN(layout))
1719	    continue;
1720
1721	/* The size of a -union element is determined by the elements
1722	 * it surrounds */
1723	if (eLink1->onion != NULL) {
1724	    layout->useHeight = 0;
1725	    continue;
1726	}
1727
1728#ifdef CACHE_ELEM_SIZE
1729	layout->useHeight = eLink2->neededHeight;
1730#else
1731	layout->useHeight = layout->neededHeight;
1732#endif
1733
1734	/* If a Text Element is given less width than it needs (due to
1735	 * -squeeze x layout), then it may wrap lines. This means
1736	 * the height can vary depending on the width. */
1737	if (eLink2->elem->typePtr->heightProc == NULL)
1738	    continue;
1739
1740	if (eLink1->fixedHeight >= 0)
1741	    continue;
1742
1743#ifdef CACHE_ELEM_SIZE
1744	/* Not squeezed */
1745	if (layout->useWidth >= eLink2->neededWidth)
1746	    continue;
1747
1748	/* Already calculated the height at this width */
1749	if (layout->useWidth == eLink2->layoutWidth) {
1750	    layout->useHeight = eLink2->layoutHeight;
1751	    continue;
1752	}
1753#if 0
1754	/* */
1755	if ((eLink2->layoutWidth == -1) &&
1756	    (layout->useWidth >= eLink2->neededWidth))
1757	    continue;
1758#endif
1759#else
1760	/* Not squeezed */
1761	if (layout->useWidth >= layout->neededWidth)
1762	    continue;
1763#endif
1764	args.tree = tree;
1765	args.state = state;
1766	args.elem = eLink2->elem;
1767	args.height.fixedWidth = layout->useWidth;
1768	(*args.elem->typePtr->heightProc)(&args);
1769
1770	if (eLink1->fixedHeight >= 0)
1771	    layout->useHeight = eLink1->fixedHeight;
1772	else if ((eLink1->minHeight >= 0) &&
1773	    (args.height.height <  eLink1->minHeight))
1774	    layout->useHeight = eLink1->minHeight;
1775	else if ((eLink1->maxHeight >= 0) &&
1776	    (args.height.height >  eLink1->maxHeight))
1777	    layout->useHeight = eLink1->maxHeight;
1778	else
1779	    layout->useHeight = args.height.height;
1780
1781#ifdef CACHE_ELEM_SIZE
1782	eLink2->layoutWidth = layout->useWidth;
1783	eLink2->layoutHeight = layout->useHeight;
1784#endif
1785    }
1786
1787    if (neededV) {
1788        Style_DoLayoutNeededV(drawArgs, layouts);
1789    } else {
1790        Style_DoLayoutV(drawArgs, layouts);
1791    }
1792}
1793
1794/*
1795 *----------------------------------------------------------------------
1796 *
1797 * Style_NeededSize --
1798 *
1799 *	Calculate the width and height of a style based only on
1800 *	the requested size of each element.
1801 *
1802 * Results:
1803 *	The width and height. The minimum width and height is equal to
1804 *	the requested width and height minus any squeezing.
1805 *
1806 * Side effects:
1807 *	None.
1808 *
1809 *----------------------------------------------------------------------
1810 */
1811
1812static void
1813Style_NeededSize(
1814    TreeCtrl *tree,		/* Widget info. */
1815    IStyle *style,		/* Style to calculate size of. */
1816    int state,			/* STATE_xxx flags. */
1817    int *widthPtr,		/* Returned width. */
1818    int *heightPtr,		/* Returned height. */
1819    int *minWidthPtr,		/* Returned minimum width. */
1820    int *minHeightPtr		/* Returned minimum height. */
1821    )
1822{
1823    MStyle *masterStyle = style->master;
1824    MElementLink *eLinks1, *eLink1;
1825    IElementLink *eLinks2, *eLink2;
1826    struct Layout staticLayouts[STATIC_SIZE], *layouts = staticLayouts;
1827    int *ePadX, *iPadX, *uPadX, *ePadY, *iPadY, *uPadY;
1828    int i, j;
1829    int x = 0, y = 0;
1830    int squeezeX = 0, squeezeY = 0;
1831
1832    STATIC_ALLOC(layouts, struct Layout, masterStyle->numElements);
1833
1834    eLinks1 = masterStyle->elements;
1835    eLinks2 = style->elements;
1836
1837    for (i = 0; i < masterStyle->numElements; i++) {
1838	struct Layout *layout = &layouts[i];
1839
1840	eLink1 = &eLinks1[i];
1841	eLink2 = &eLinks2[i];
1842
1843	layout->visible = PerStateBoolean_ForState(tree,
1844		&eLink1->visible, state, NULL) != 0;
1845	if (IS_HIDDEN(layout))
1846	    continue;
1847
1848	layout->master = eLink1;
1849	layout->eLink = eLink2;
1850
1851	if (eLink1->onion == NULL) {
1852#ifdef CACHE_ELEM_SIZE
1853	    if ((eLink2->neededWidth == -1) || (eLink2->neededHeight == -1)) {
1854		Element_NeededSize(tree, eLink1, eLink2->elem, state,
1855			&eLink2->neededWidth, &eLink2->neededHeight);
1856		eLink2->layoutWidth = -1;
1857	    }
1858	    layout->useWidth = eLink2->neededWidth;
1859	    layout->useHeight = eLink2->neededHeight;
1860#else
1861	    Element_NeededSize(tree, eLink1, eLink2->elem, state,
1862		    &layout->neededWidth, &layout->neededHeight);
1863	    layout->useWidth = layout->neededWidth;
1864#endif
1865	}
1866
1867	/* No -union padding yet */
1868	layout->uPadX[PAD_TOP_LEFT]     = 0;
1869	layout->uPadX[PAD_BOTTOM_RIGHT] = 0;
1870	layout->uPadY[PAD_TOP_LEFT]     = 0;
1871	layout->uPadY[PAD_BOTTOM_RIGHT] = 0;
1872    }
1873
1874    /* Figure out the padding around elements surrounded by -union elements */
1875    for (i = 0; i < masterStyle->numElements; i++) {
1876	struct Layout *layout = &layouts[i];
1877	int first = -1, last = -1;
1878
1879	if (IS_HIDDEN(layout))
1880	    continue;
1881
1882	eLink1 = &eLinks1[i];
1883
1884	if (eLink1->onion == NULL)
1885	    continue;
1886
1887	for (j = 0; j < eLink1->onionCount; j++) {
1888	    struct Layout *layout = &layouts[eLink1->onion[j]];
1889
1890	    if (IS_HIDDEN(layout))
1891		continue;
1892
1893	    /* Remember the first and last visible elements surrounded by
1894	     * this -union element. */
1895	    if (first == -1)
1896		first = j;
1897	    last = j;
1898	}
1899
1900	/* If there are no visible elements surrounded by this -union
1901	 * element, then hide it. */
1902	if (first == -1) {
1903	    layout->visible = 0;
1904	    continue;
1905	}
1906
1907	ePadX = eLink1->ePadX;
1908	ePadY = eLink1->ePadY;
1909	iPadX = eLink1->iPadX;
1910	iPadY = eLink1->iPadY;
1911
1912	for (j = 0; j < eLink1->onionCount; j++) {
1913	    struct Layout *layout = &layouts[eLink1->onion[j]];
1914
1915	    if (IS_HIDDEN(layout))
1916		continue;
1917
1918	    uPadX = layout->uPadX;
1919	    uPadY = layout->uPadY;
1920
1921	    if (masterStyle->vertical) {
1922		uPadX[PAD_TOP_LEFT] = MAX(uPadX[PAD_TOP_LEFT], iPadX[PAD_TOP_LEFT] + ePadX[PAD_TOP_LEFT]);
1923		uPadX[PAD_BOTTOM_RIGHT] = MAX(uPadX[PAD_BOTTOM_RIGHT], iPadX[PAD_BOTTOM_RIGHT] + ePadX[PAD_BOTTOM_RIGHT]);
1924		if (j == first) /* topmost */
1925		    uPadY[PAD_TOP_LEFT] = MAX(uPadY[PAD_TOP_LEFT], iPadY[PAD_TOP_LEFT] + ePadY[PAD_TOP_LEFT]);
1926		if (j == last) /* bottommost */
1927		    uPadY[PAD_BOTTOM_RIGHT] = MAX(uPadY[PAD_BOTTOM_RIGHT], iPadY[PAD_BOTTOM_RIGHT] + ePadY[PAD_BOTTOM_RIGHT]);
1928	    } else {
1929		if (j == first) /* leftmost */
1930		    uPadX[PAD_TOP_LEFT] = MAX(uPadX[PAD_TOP_LEFT], iPadX[PAD_TOP_LEFT] + ePadX[PAD_TOP_LEFT]);
1931		if (j == last) /* rightmost */
1932		    uPadX[PAD_BOTTOM_RIGHT] = MAX(uPadX[PAD_BOTTOM_RIGHT], iPadX[PAD_BOTTOM_RIGHT] + ePadX[PAD_BOTTOM_RIGHT]);
1933		uPadY[PAD_TOP_LEFT] = MAX(uPadY[PAD_TOP_LEFT], iPadY[PAD_TOP_LEFT] + ePadY[PAD_TOP_LEFT]);
1934		uPadY[PAD_BOTTOM_RIGHT] = MAX(uPadY[PAD_BOTTOM_RIGHT], iPadY[PAD_BOTTOM_RIGHT] + ePadY[PAD_BOTTOM_RIGHT]);
1935	    }
1936	}
1937    }
1938
1939    /* Layout elements left-to-right, or top-to-bottom */
1940    for (i = 0; i < masterStyle->numElements; i++) {
1941	struct Layout *layout = &layouts[i];
1942
1943	if (IS_HIDDEN(layout))
1944	    continue;
1945
1946	eLink1 = &eLinks1[i];
1947	eLink2 = &eLinks2[i];
1948
1949	ePadX = eLink1->ePadX;
1950	ePadY = eLink1->ePadY;
1951	iPadX = eLink1->iPadX;
1952	iPadY = eLink1->iPadY;
1953	uPadX = layout->uPadX;
1954	uPadY = layout->uPadY;
1955
1956	/* The size of a -union element is determined by the elements
1957	 * it surrounds */
1958	if (eLink1->onion != NULL) {
1959	    layout->x = layout->y = layout->eWidth = layout->eHeight = 0;
1960	    layout->ePadX[PAD_TOP_LEFT]     = 0;
1961	    layout->ePadX[PAD_BOTTOM_RIGHT] = 0;
1962	    layout->ePadY[PAD_TOP_LEFT]     = 0;
1963	    layout->ePadY[PAD_BOTTOM_RIGHT] = 0;
1964	    layout->iPadX[PAD_TOP_LEFT]     = 0;
1965	    layout->iPadX[PAD_BOTTOM_RIGHT] = 0;
1966	    layout->iPadY[PAD_TOP_LEFT]     = 0;
1967	    layout->iPadY[PAD_BOTTOM_RIGHT] = 0;
1968	    continue;
1969	}
1970
1971	if (eLink1->flags & ELF_SQUEEZE_X) {
1972	    if ((eLink1->minWidth >= 0) &&
1973		    (eLink1->minWidth <= layout->useWidth)) {
1974		squeezeX += layout->useWidth - eLink1->minWidth;
1975	    } else {
1976		squeezeX += layout->useWidth;
1977	    }
1978	}
1979	if (eLink1->flags & ELF_SQUEEZE_Y) {
1980	    if ((eLink1->minHeight >= 0) &&
1981		    (eLink1->minHeight <= layout->useHeight)) {
1982		squeezeY += layout->useHeight - eLink1->minHeight;
1983	    } else {
1984		squeezeY += layout->useHeight;
1985	    }
1986	}
1987
1988	/* -detach elements are positioned by themselves */
1989	if (eLink1->flags & ELF_DETACH)
1990	    continue;
1991
1992	layout->eLink = eLink2;
1993	layout->x = x + abs(ePadX[PAD_TOP_LEFT] - MAX(ePadX[PAD_TOP_LEFT], uPadX[PAD_TOP_LEFT]));
1994	layout->y = y + abs(ePadY[PAD_TOP_LEFT] - MAX(ePadY[PAD_TOP_LEFT], uPadY[PAD_TOP_LEFT]));
1995	layout->iWidth = iPadX[PAD_TOP_LEFT] + layout->useWidth + iPadX[PAD_BOTTOM_RIGHT];
1996	layout->iHeight = iPadY[PAD_TOP_LEFT] + layout->useHeight + iPadY[PAD_BOTTOM_RIGHT];
1997	layout->eWidth = ePadX[PAD_TOP_LEFT] + layout->iWidth + ePadX[PAD_BOTTOM_RIGHT];
1998	layout->eHeight = ePadY[PAD_TOP_LEFT] + layout->iHeight + ePadY[PAD_BOTTOM_RIGHT];
1999
2000	for (j = 0; j < 2; j++) {
2001	    layout->ePadX[j] = eLink1->ePadX[j];
2002	    layout->ePadY[j] = eLink1->ePadY[j];
2003	    layout->iPadX[j] = eLink1->iPadX[j];
2004	    layout->iPadY[j] = eLink1->iPadY[j];
2005	}
2006
2007	if (masterStyle->vertical)
2008	    y = layout->y + layout->eHeight;
2009	else
2010	    x = layout->x + layout->eWidth;
2011    }
2012
2013    /* -detach elements */
2014    for (i = 0; i < masterStyle->numElements; i++) {
2015	struct Layout *layout = &layouts[i];
2016
2017	if (IS_HIDDEN(layout))
2018	    continue;
2019
2020	eLink1 = &eLinks1[i];
2021	eLink2 = &eLinks2[i];
2022
2023	if (!(eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL))
2024	    continue;
2025
2026	ePadX = eLink1->ePadX;
2027	ePadY = eLink1->ePadY;
2028	iPadX = eLink1->iPadX;
2029	iPadY = eLink1->iPadY;
2030	uPadX = layout->uPadX;
2031	uPadY = layout->uPadY;
2032
2033	layout->eLink = eLink2;
2034	layout->master = eLink1;
2035	layout->x = abs(ePadX[PAD_TOP_LEFT] - MAX(ePadX[PAD_TOP_LEFT], uPadX[PAD_TOP_LEFT]));
2036	layout->y = abs(ePadY[PAD_TOP_LEFT] - MAX(ePadY[PAD_TOP_LEFT], uPadY[PAD_TOP_LEFT]));
2037	layout->iWidth = iPadX[PAD_TOP_LEFT] + layout->useWidth + iPadX[PAD_BOTTOM_RIGHT];
2038	layout->iHeight = iPadY[PAD_TOP_LEFT] + layout->useHeight + iPadY[PAD_BOTTOM_RIGHT];
2039	layout->eWidth = ePadX[PAD_TOP_LEFT] + layout->iWidth + ePadX[PAD_BOTTOM_RIGHT];
2040	layout->eHeight = ePadY[PAD_TOP_LEFT] + layout->iHeight + ePadY[PAD_BOTTOM_RIGHT];
2041
2042	for (j = 0; j < 2; j++) {
2043	    layout->ePadX[j] = eLink1->ePadX[j];
2044	    layout->ePadY[j] = eLink1->ePadY[j];
2045	    layout->iPadX[j] = eLink1->iPadX[j];
2046	    layout->iPadY[j] = eLink1->iPadY[j];
2047	}
2048    }
2049
2050    Layout_Size(masterStyle->vertical, masterStyle->numElements, layouts,
2051	widthPtr, heightPtr);
2052
2053    *minWidthPtr = *widthPtr - squeezeX;
2054    *minHeightPtr = *heightPtr - squeezeY;
2055
2056    STATIC_FREE(layouts, struct Layout, masterStyle->numElements);
2057}
2058
2059/*
2060 *----------------------------------------------------------------------
2061 *
2062 * Style_CheckNeededSize --
2063 *
2064 *	If the style's requested size is out-of-date then recalculate
2065 *	Style.neededWidth, Style.neededHeight, Style.minWidth, and
2066 *	Style.minHeight.
2067 *
2068 * Results:
2069 *	None.
2070 *
2071 * Side effects:
2072 *	None.
2073 *
2074 *----------------------------------------------------------------------
2075 */
2076
2077static void
2078Style_CheckNeededSize(
2079    TreeCtrl *tree,		/* Widget info. */
2080    IStyle *style,		/* Style info. */
2081    int state			/* STATE_xxx flags. */
2082    )
2083{
2084    if (style->neededWidth == -1) {
2085	int minWidth, minHeight;
2086
2087	Style_NeededSize(tree, style, state,
2088	    &style->neededWidth, &style->neededHeight, &minWidth, &minHeight);
2089#ifdef CACHE_STYLE_SIZE
2090	style->minWidth = minWidth;
2091	style->minHeight = minHeight;
2092	style->layoutWidth = -1;
2093#endif /* CACHE_STYLE_SIZE */
2094#ifdef TREECTRL_DEBUG
2095	style->neededState = state;
2096#endif
2097    }
2098#ifdef TREECTRL_DEBUG
2099    if (style->neededState != state)
2100	panic("Style_CheckNeededSize: neededState %d != state %d\n",
2101	    style->neededState, state);
2102#endif
2103}
2104
2105#ifndef CACHE_STYLE_SIZE
2106
2107static void
2108Style_MinSize(
2109    TreeCtrl *tree,		/* Widget info. */
2110    IStyle *style,		/* Style info. */
2111    int state,			/* STATE_xxx flags. */
2112    int *minWidthPtr,
2113    int *minHeightPtr
2114    )
2115{
2116    int i, hasSqueeze = FALSE;
2117
2118    for (i = 0; i < style->master->numElements; i++) {
2119	MElementLink *eLink1 = &style->master->elements[i];
2120	if ((eLink1->onion == NULL) &&
2121		(eLink1->flags & (ELF_SQUEEZE_X | ELF_SQUEEZE_Y))) {
2122	    hasSqueeze = TRUE;
2123	    break;
2124	}
2125    }
2126    if (hasSqueeze) {
2127	int width, height;
2128	Style_NeededSize(tree, style, state, &width, &height,
2129		minWidthPtr, minHeightPtr);
2130    } else {
2131	*minWidthPtr = style->neededWidth;
2132	*minHeightPtr = style->neededHeight;
2133    }
2134}
2135
2136#endif /* !CACHE_STYLE_SIZE */
2137
2138/*
2139 *----------------------------------------------------------------------
2140 *
2141 * TreeStyle_NeededWidth --
2142 *
2143 *	Return the requested width of a style.
2144 *
2145 * Results:
2146 *	The requested width. If the requested size is out-of-date
2147 *	then it is recalculated.
2148 *
2149 * Side effects:
2150 *	None.
2151 *
2152 *----------------------------------------------------------------------
2153 */
2154
2155int
2156TreeStyle_NeededWidth(
2157    TreeCtrl *tree,		/* Widget info. */
2158    TreeStyle style_,		/* Style token. */
2159    int state			/* STATE_xxx flags. */
2160    )
2161{
2162    IStyle *style = (IStyle *) style_;
2163
2164    Style_CheckNeededSize(tree, style, state);
2165    return style->neededWidth;
2166}
2167
2168/*
2169 *----------------------------------------------------------------------
2170 *
2171 * TreeStyle_NeededHeight --
2172 *
2173 *	Return the requested height of a style.
2174 *
2175 * Results:
2176 *	The requested height. If the requested size is out-of-date
2177 *	then it is recalculated.
2178 *
2179 * Side effects:
2180 *	None.
2181 *
2182 *----------------------------------------------------------------------
2183 */
2184
2185int
2186TreeStyle_NeededHeight(
2187    TreeCtrl *tree,		/* Widget info. */
2188    TreeStyle style_,		/* Style token. */
2189    int state			/* STATE_xxx flags. */
2190    )
2191{
2192    IStyle *style = (IStyle *) style_;
2193
2194    Style_CheckNeededSize(tree, style, state);
2195    return style->neededHeight;
2196}
2197
2198/*
2199 *----------------------------------------------------------------------
2200 *
2201 * TreeStyle_UseHeight --
2202 *
2203 *	Return the height of a style for a given state and width.
2204 *
2205 * Results:
2206 *	The height of the style.
2207 *
2208 * Side effects:
2209 *	None.
2210 *
2211 *----------------------------------------------------------------------
2212 */
2213
2214/* Calculate height of Style considering drawArgs.width */
2215int
2216TreeStyle_UseHeight(
2217    StyleDrawArgs *drawArgs	/* Various args. */
2218    )
2219{
2220    TreeCtrl *tree = drawArgs->tree;
2221    IStyle *style = (IStyle *) drawArgs->style;
2222    MStyle *masterStyle = style->master;
2223    int state = drawArgs->state;
2224    struct Layout staticLayouts[STATIC_SIZE], *layouts = staticLayouts;
2225    int width, height, minWidth;
2226#ifndef CACHE_STYLE_SIZE
2227    int minHeight;
2228#endif
2229
2230    Style_CheckNeededSize(tree, style, state);
2231#ifdef CACHE_STYLE_SIZE
2232    minWidth = style->minWidth;
2233#else
2234    if (drawArgs->width < style->neededWidth + drawArgs->indent)
2235	Style_MinSize(tree, style, state, &minWidth, &minHeight);
2236    else
2237	minWidth = style->neededWidth;
2238#endif
2239
2240    /*
2241     * If we have:
2242     * a) infinite space available, or
2243     * b) more width than the style needs, or
2244     * c) less width than the style needs, but it has no -squeeze x elements
2245     * then return the needed height of the style. This is safe since no
2246     * text elements will be growing vertically when lines wrap.
2247     */
2248    if ((drawArgs->width == -1) ||
2249	(drawArgs->width >= style->neededWidth + drawArgs->indent) ||
2250	(style->neededWidth == minWidth)) {
2251	return style->neededHeight;
2252    }
2253
2254    /* We never lay out the style at less than the minimum width */
2255    if (drawArgs->width < minWidth + drawArgs->indent)
2256	drawArgs->width = minWidth + drawArgs->indent;
2257
2258#ifdef CACHE_STYLE_SIZE
2259    /* We have less space than the style needs, and have already calculated
2260     * the height of the style at this width. (The height may change because
2261     * of text elements wrapping lines). */
2262    if (drawArgs->width == style->layoutWidth)
2263	return style->layoutHeight;
2264#endif
2265
2266    STATIC_ALLOC(layouts, struct Layout, masterStyle->numElements);
2267
2268    Style_DoLayout(drawArgs, layouts, TRUE, __FILE__, __LINE__);
2269
2270    Layout_Size(style->master->vertical, masterStyle->numElements, layouts,
2271	&width, &height);
2272
2273    STATIC_FREE(layouts, struct Layout, masterStyle->numElements);
2274
2275#ifdef CACHE_STYLE_SIZE
2276    style->layoutWidth = drawArgs->width;
2277    style->layoutHeight = height;
2278#endif
2279
2280    return height;
2281}
2282
2283/*
2284 *----------------------------------------------------------------------
2285 *
2286 * TreeStyle_Draw --
2287 *
2288 *	Draw all the elements in a style.
2289 *
2290 * Results:
2291 *	None.
2292 *
2293 * Side effects:
2294 *	Stuff is drawn.
2295 *
2296 *----------------------------------------------------------------------
2297 */
2298
2299void TreeStyle_Draw(
2300    StyleDrawArgs *drawArgs	/* Various args. */
2301    )
2302{
2303    IStyle *style = (IStyle *) drawArgs->style;
2304    MStyle *masterStyle = style->master;
2305    TreeCtrl *tree = drawArgs->tree;
2306    int *bounds = drawArgs->bounds;
2307    TreeElementArgs args;
2308    int i, x, y, minWidth, minHeight;
2309    struct Layout staticLayouts[STATIC_SIZE], *layouts = staticLayouts;
2310#undef DEBUG_DRAW
2311#ifdef DEBUG_DRAW
2312    int debugDraw = FALSE;
2313#endif
2314
2315    Style_CheckNeededSize(tree, style, drawArgs->state);
2316#ifdef CACHE_STYLE_SIZE
2317    minWidth = style->minWidth;
2318    minHeight = style->minHeight;
2319#else
2320    if ((drawArgs->width < style->neededWidth + drawArgs->indent) ||
2321	    (drawArgs->height < style->neededHeight)) {
2322	Style_MinSize(tree, style, drawArgs->state, &minWidth, &minHeight);
2323    } else {
2324	minWidth = style->neededWidth;
2325	minHeight = style->neededHeight;
2326    }
2327#endif
2328
2329    /* Get the bounds allowed for drawing (in window coordinates), inside
2330     * the item-column(s) and inside the header/borders. */
2331    x = drawArgs->x + tree->drawableXOrigin - tree->xOrigin;
2332    y = drawArgs->y + tree->drawableYOrigin - tree->yOrigin;
2333    args.display.bounds[0] = MAX(bounds[0], x);
2334    args.display.bounds[1] = MAX(bounds[1], y);
2335    args.display.bounds[2] = MIN(bounds[2], x + drawArgs->width);
2336    args.display.bounds[3] = MIN(bounds[3], y + drawArgs->height);
2337
2338    /* We never lay out the style at less than the minimum size */
2339    if (drawArgs->width < minWidth + drawArgs->indent)
2340	drawArgs->width = minWidth + drawArgs->indent;
2341    if (drawArgs->height < minHeight)
2342	drawArgs->height = minHeight;
2343
2344    STATIC_ALLOC(layouts, struct Layout, masterStyle->numElements);
2345
2346    Style_DoLayout(drawArgs, layouts, FALSE, __FILE__, __LINE__);
2347
2348    args.tree = tree;
2349    args.state = drawArgs->state;
2350    args.display.td = drawArgs->td;
2351    args.display.drawable = drawArgs->td.drawable;
2352
2353    for (i = 0; i < masterStyle->numElements; i++) {
2354	struct Layout *layout = &layouts[i];
2355
2356	if (IS_HIDDEN(layout))
2357	    continue;
2358
2359	/* Don't "draw" window elements. TreeStyle_UpdateWindowPositions()
2360	 * does that for us. */
2361	if (ELEMENT_TYPE_MATCHES(layout->eLink->elem->typePtr, &treeElemTypeWindow))
2362	    continue;
2363
2364	if (PerStateBoolean_ForState(tree, &layout->master->draw,
2365		drawArgs->state, NULL) == 0)
2366	    continue;
2367
2368#ifdef DEBUG_DRAW
2369	if (debugDraw && layout->master->onion != NULL)
2370	    continue;
2371#endif
2372
2373	if ((layout->useWidth > 0) && (layout->useHeight > 0)) {
2374	    args.elem = layout->eLink->elem;
2375	    args.display.x = drawArgs->x + layout->x + layout->ePadX[PAD_TOP_LEFT];
2376	    args.display.y = drawArgs->y + layout->y + layout->ePadY[PAD_TOP_LEFT];
2377	    args.display.x += layout->iPadX[PAD_TOP_LEFT];
2378	    args.display.y += layout->iPadY[PAD_TOP_LEFT];
2379	    args.display.width = layout->useWidth;
2380	    args.display.height = layout->useHeight;
2381	    args.display.sticky = layout->master->flags & ELF_STICKY;
2382#ifdef DEBUG_DRAW
2383	    if (debugDraw) {
2384		XColor *color[3];
2385		GC gc[3];
2386
2387		if (layout->master->onion != NULL) {
2388		    color[0] = Tk_GetColor(tree->interp, tree->tkwin, "blue2");
2389		    gc[0] = Tk_GCForColor(color[0], Tk_WindowId(tree->tkwin));
2390		    color[1] = Tk_GetColor(tree->interp, tree->tkwin, "blue3");
2391		    gc[1] = Tk_GCForColor(color[1], Tk_WindowId(tree->tkwin));
2392		} else {
2393		    color[0] = Tk_GetColor(tree->interp, tree->tkwin, "gray50");
2394		    gc[0] = Tk_GCForColor(color[0], Tk_WindowId(tree->tkwin));
2395		    color[1] = Tk_GetColor(tree->interp, tree->tkwin, "gray60");
2396		    gc[1] = Tk_GCForColor(color[1], Tk_WindowId(tree->tkwin));
2397		    color[2] = Tk_GetColor(tree->interp, tree->tkwin, "gray70");
2398		    gc[2] = Tk_GCForColor(color[2], Tk_WindowId(args.tree->tkwin));
2399		}
2400
2401		/* external */
2402		XFillRectangle(tree->display, args.display.drawable,
2403		    gc[2],
2404		    args.display.x - layout->ePadX[PAD_TOP_LEFT],
2405		    args.display.y - layout->ePadY[PAD_TOP_LEFT],
2406		    layout->eWidth, layout->eHeight);
2407		/* internal */
2408		XFillRectangle(tree->display, args.display.drawable,
2409		    gc[1],
2410		    args.display.x, args.display.y,
2411		    args.display.width, args.display.height);
2412		/* needed */
2413		if (!layout->master->onion && !(layout->master->flags & ELF_DETACH))
2414		XFillRectangle(tree->display, args.display.drawable,
2415		    gc[0],
2416		    args.display.x + layout->iPadX[PAD_TOP_LEFT],
2417		    args.display.y + layout->iPadY[PAD_TOP_LEFT],
2418		    layout->eLink->neededWidth, layout->eLink->neededHeight);
2419	    } else
2420#endif /* DEBUG_DRAW */
2421		(*args.elem->typePtr->displayProc)(&args);
2422	}
2423    }
2424
2425#ifdef DEBUG_DRAW
2426    if (debugDraw)
2427	for (i = 0; i < masterStyle->numElements; i++) {
2428	    struct Layout *layout = &layouts[i];
2429
2430	    if (IS_HIDDEN(layout))
2431		continue;
2432
2433	    if (layout->master->onion == NULL)
2434		continue;
2435	    if (layout->useWidth > 0 && layout->useHeight > 0) {
2436		args.elem = layout->eLink->elem;
2437		args.display.x = drawArgs->x + layout->x + layout->ePadX[PAD_TOP_LEFT];
2438		args.display.y = drawArgs->y + layout->y + layout->ePadY[PAD_TOP_LEFT];
2439		args.display.width = layout->iWidth;
2440		args.display.height = layout->iHeight;
2441		{
2442		    XColor *color[3];
2443		    GC gc[3];
2444
2445		    color[0] = Tk_GetColor(tree->interp, tree->tkwin, "blue2");
2446		    gc[0] = Tk_GCForColor(color[0], Tk_WindowId(tree->tkwin));
2447		    color[1] = Tk_GetColor(tree->interp, tree->tkwin, "blue3");
2448		    gc[1] = Tk_GCForColor(color[1], Tk_WindowId(tree->tkwin));
2449
2450		    /* external */
2451		    XDrawRectangle(tree->display, args.display.drawable,
2452			gc[0],
2453			args.display.x - layout->ePadX[PAD_TOP_LEFT],
2454			args.display.y - layout->ePadY[PAD_TOP_LEFT],
2455			layout->eWidth - 1, layout->eHeight - 1);
2456		    /* internal */
2457		    XDrawRectangle(tree->display, args.display.drawable,
2458			gc[1],
2459			args.display.x, args.display.y,
2460			args.display.width - 1, args.display.height - 1);
2461		}
2462	    }
2463	}
2464#endif /* DEBUG_DRAW */
2465
2466    STATIC_FREE(layouts, struct Layout, masterStyle->numElements);
2467}
2468
2469/*
2470 *----------------------------------------------------------------------
2471 *
2472 * TreeStyle_UpdateWindowPositions --
2473 *
2474 *	Call the displayProc on each window element so it can update
2475 *	its geometry. This is needed if an item was scrolled and its
2476 *	displayProc wasn't otherwise called.
2477 *
2478 * Results:
2479 *	None.
2480 *
2481 * Side effects:
2482 *	Possible window geometry changes.
2483 *
2484 *----------------------------------------------------------------------
2485 */
2486
2487void
2488TreeStyle_UpdateWindowPositions(
2489    StyleDrawArgs *drawArgs	/* Various args. */
2490    )
2491{
2492    IStyle *style = (IStyle *) drawArgs->style;
2493    MStyle *masterStyle = style->master;
2494    TreeCtrl *tree = drawArgs->tree;
2495    int *bounds = drawArgs->bounds;
2496    TreeElementArgs args;
2497    int i, x, y, minWidth, minHeight;
2498    struct Layout staticLayouts[STATIC_SIZE], *layouts = staticLayouts;
2499    int numElements = masterStyle->numElements;
2500
2501    /* FIXME: Perhaps remember whether this style has any window
2502     * elements */
2503    for (i = 0; i < numElements; i++) {
2504	if (ELEMENT_TYPE_MATCHES(masterStyle->elements[i].elem->typePtr, &treeElemTypeWindow))
2505	    break;
2506    }
2507    if (i == numElements)
2508	return;
2509
2510    Style_CheckNeededSize(tree, style, drawArgs->state);
2511#ifdef CACHE_STYLE_SIZE
2512    minWidth = style->minWidth;
2513    minHeight = style->minHeight;
2514#else
2515    if ((drawArgs->width < style->neededWidth + drawArgs->indent) ||
2516	    (drawArgs->height < style->neededHeight)) {
2517	Style_MinSize(tree, style, drawArgs->state, &minWidth, &minHeight);
2518    } else {
2519	minWidth = style->neededWidth;
2520	minHeight = style->neededHeight;
2521    }
2522#endif
2523
2524    /* Get the bounds allowed for drawing (in window coordinates), inside
2525     * the item-column(s) and inside the header/borders. */
2526    x = drawArgs->x + tree->drawableXOrigin - tree->xOrigin;
2527    y = drawArgs->y + tree->drawableYOrigin - tree->yOrigin;
2528    args.display.bounds[0] = MAX(bounds[0], x);
2529    args.display.bounds[1] = MAX(bounds[1], y);
2530    args.display.bounds[2] = MIN(bounds[2], x + drawArgs->width);
2531    args.display.bounds[3] = MIN(bounds[3], y + drawArgs->height);
2532
2533    /* We never lay out the style at less than the minimum size */
2534    if (drawArgs->width < minWidth + drawArgs->indent)
2535	drawArgs->width = minWidth + drawArgs->indent;
2536    if (drawArgs->height < minHeight)
2537	drawArgs->height = minHeight;
2538
2539    STATIC_ALLOC(layouts, struct Layout, numElements);
2540
2541    Style_DoLayout(drawArgs, layouts, FALSE, __FILE__, __LINE__);
2542
2543    args.tree = tree;
2544    args.state = drawArgs->state;
2545    args.display.td = drawArgs->td;
2546    args.display.drawable = drawArgs->td.drawable;
2547
2548    for (i = 0; i < numElements; i++) {
2549	struct Layout *layout = &layouts[i];
2550
2551	if (IS_HIDDEN(layout))
2552	    continue;
2553
2554	if (!ELEMENT_TYPE_MATCHES(layout->eLink->elem->typePtr, &treeElemTypeWindow))
2555	    continue;
2556
2557	if (PerStateBoolean_ForState(tree, &layout->master->draw,
2558		drawArgs->state, NULL) == 0)
2559	    continue;
2560
2561	if ((layout->useWidth > 0) && (layout->useHeight > 0)) {
2562	    int requests;
2563
2564	    TreeDisplay_GetReadyForTrouble(tree, &requests);
2565
2566	    args.elem = layout->eLink->elem;
2567	    args.display.x = drawArgs->x + layout->x + layout->ePadX[PAD_TOP_LEFT];
2568	    args.display.y = drawArgs->y + layout->y + layout->ePadY[PAD_TOP_LEFT];
2569	    args.display.x += layout->iPadX[PAD_TOP_LEFT];
2570	    args.display.y += layout->iPadY[PAD_TOP_LEFT];
2571	    args.display.width = layout->useWidth;
2572	    args.display.height = layout->useHeight;
2573	    args.display.sticky = layout->master->flags & ELF_STICKY;
2574	    (*args.elem->typePtr->displayProc)(&args);
2575
2576	    /* Updating the position of a window may generate a <Configure>
2577	     * or <Map> event on that window. Binding scripts on those
2578	     * events could do anything, including deleting items and
2579	     * thus the style we are drawing. In other cases (such as when
2580	     * using Tile widgets I notice), the Tk_GeomMgr.requestProc
2581	     * may get called which calls Tree_ElementChangedItself which
2582	     * calls FreeDItemInfo which frees a DItem we are in the middle
2583	     * of displaying. So if anything was done that caused a display
2584	     * request, then abort abort abort. */
2585	    if (TreeDisplay_WasThereTrouble(tree, requests))
2586		break;
2587	}
2588    }
2589
2590    STATIC_FREE(layouts, struct Layout, numElements);
2591}
2592
2593/*
2594 *----------------------------------------------------------------------
2595 *
2596 * TreeStyle_OnScreen --
2597 *
2598 *	Call the onScreenProc (if non-NULL) on each element so it can
2599 *	update its visibility when an item's visibility changes.
2600 *
2601 * Results:
2602 *	None.
2603 *
2604 * Side effects:
2605 *	Possible window visibility changes.
2606 *
2607 *----------------------------------------------------------------------
2608 */
2609
2610void
2611TreeStyle_OnScreen(
2612    TreeCtrl *tree,		/* Widget info. */
2613    TreeStyle style_,		/* Style token. */
2614    int onScreen		/* Boolean indicating whether the item
2615				 * using the style is on screen anymore. */
2616    )
2617{
2618    IStyle *style = (IStyle *) style_;
2619    TreeElementArgs args;
2620    int i;
2621
2622    args.tree = tree;
2623    args.screen.visible = onScreen;
2624
2625    for (i = 0; i < style->master->numElements; i++) {
2626	IElementLink *eLink = &style->elements[i];
2627
2628	if (eLink->elem->typePtr->onScreenProc == NULL)
2629	    continue;
2630
2631	args.elem = eLink->elem;
2632	(*args.elem->typePtr->onScreenProc)(&args);
2633    }
2634}
2635
2636/*
2637 *----------------------------------------------------------------------
2638 *
2639 * Element_FreeResources --
2640 *
2641 *	Free memory etc associated with an Element.
2642 *
2643 * Results:
2644 *	None.
2645 *
2646 * Side effects:
2647 *	Memory is deallocated.
2648 *
2649 *----------------------------------------------------------------------
2650 */
2651
2652static void
2653Element_FreeResources(
2654    TreeCtrl *tree,		/* Widget info. */
2655    TreeElement elem		/* Record to free. */
2656    )
2657{
2658    TreeElementType *typePtr = elem->typePtr;
2659    TreeElementArgs args;
2660    Tcl_HashEntry *hPtr;
2661
2662    if (elem->master == NULL) {
2663	hPtr = Tcl_FindHashEntry(&tree->elementHash, elem->name);
2664	Tcl_DeleteHashEntry(hPtr);
2665    }
2666    args.tree = tree;
2667    args.elem = elem;
2668    (*typePtr->deleteProc)(&args);
2669    Tk_FreeConfigOptions((char *) elem,
2670	typePtr->optionTable,
2671	tree->tkwin);
2672    DynamicOption_Free(tree, elem->options, typePtr->optionSpecs);
2673#ifdef ALLOC_HAX
2674    TreeAlloc_Free(tree->allocData, typePtr->name, (char *) elem, typePtr->size);
2675#else
2676    WFREE(elem, TreeElement_);
2677#endif
2678}
2679
2680/*
2681 *----------------------------------------------------------------------
2682 *
2683 * MElementLink_Init --
2684 *
2685 *	Initialize (don't allocate) a MElementLink.
2686 *
2687 * Results:
2688 *	eLink is filled with default values.
2689 *
2690 * Side effects:
2691 *	None.
2692 *
2693 *----------------------------------------------------------------------
2694 */
2695
2696static MElementLink *
2697MElementLink_Init(
2698    MElementLink *eLink,	/* Existing record to initialize. */
2699    TreeElement elem		/* Existing element to point to. */
2700    )
2701{
2702    memset(eLink, '\0', sizeof(MElementLink));
2703    eLink->elem = elem;
2704    eLink->flags |= ELF_INDENT;
2705    eLink->minWidth = eLink->fixedWidth = eLink->maxWidth = -1;
2706    eLink->minHeight = eLink->fixedHeight = eLink->maxHeight = -1;
2707    eLink->flags |= ELF_STICKY;
2708    return eLink;
2709}
2710
2711/*
2712 *----------------------------------------------------------------------
2713 *
2714 * MElementLink_FreeResources --
2715 *
2716 *	Free memory etc associated with an MElementLink.
2717 *
2718 * Results:
2719 *	None.
2720 *
2721 * Side effects:
2722 *	Memory is deallocated.
2723 *
2724 *----------------------------------------------------------------------
2725 */
2726
2727static void
2728MElementLink_FreeResources(
2729    TreeCtrl *tree,		/* Widget info. */
2730    MElementLink *eLink		/* Record to free. */
2731    )
2732{
2733    if (eLink->onion != NULL)
2734	WCFREE(eLink->onion, int, eLink->onionCount);
2735    PerStateInfo_Free(tree, &pstBoolean, &eLink->draw);
2736    if (eLink->draw.obj != NULL) {
2737	Tcl_DecrRefCount(eLink->draw.obj);
2738    }
2739    PerStateInfo_Free(tree, &pstBoolean, &eLink->visible);
2740    if (eLink->visible.obj != NULL) {
2741	Tcl_DecrRefCount(eLink->visible.obj);
2742    }
2743}
2744
2745/*
2746 *----------------------------------------------------------------------
2747 *
2748 * IElementLink_FreeResources --
2749 *
2750 *	Free memory etc associated with an ElementLink.
2751 *
2752 * Results:
2753 *	None.
2754 *
2755 * Side effects:
2756 *	Memory is deallocated.
2757 *
2758 *----------------------------------------------------------------------
2759 */
2760
2761static void
2762IElementLink_FreeResources(
2763    TreeCtrl *tree,		/* Widget info. */
2764    IElementLink *eLink		/* Record to free. */
2765    )
2766{
2767    if (eLink->elem->master != NULL)
2768	Element_FreeResources(tree, eLink->elem);
2769}
2770
2771/*
2772 *----------------------------------------------------------------------
2773 *
2774 * MStyle_FreeResources --
2775 *
2776 *	Free memory etc associated with a Style.
2777 *
2778 * Results:
2779 *	None.
2780 *
2781 * Side effects:
2782 *	Memory is deallocated.
2783 *
2784 *----------------------------------------------------------------------
2785 */
2786
2787static void
2788MStyle_FreeResources(
2789    TreeCtrl *tree,		/* Widget info. */
2790    MStyle *style		/* Style to free. */
2791    )
2792{
2793    Tcl_HashEntry *hPtr;
2794    int i;
2795
2796    hPtr = Tcl_FindHashEntry(&tree->styleHash, style->name);
2797    Tcl_DeleteHashEntry(hPtr);
2798
2799    if (style->numElements > 0) {
2800	for (i = 0; i < style->numElements; i++)
2801	    MElementLink_FreeResources(tree, &style->elements[i]);
2802#ifdef ALLOC_HAX
2803	TreeAlloc_CFree(tree->allocData, MElementLinkUid, (char *) style->elements,
2804		sizeof(MElementLink), style->numElements, ELEMENT_LINK_ROUND);
2805#else
2806	WCFREE(style->elements, MElementLink, style->numElements);
2807#endif
2808    }
2809#ifdef ALLOC_HAX
2810    TreeAlloc_Free(tree->allocData, MStyleUid, (char *) style, sizeof(MStyle));
2811#else
2812    WFREE(style, MStyle);
2813#endif
2814}
2815
2816/*
2817 *----------------------------------------------------------------------
2818 *
2819 * IStyle_FreeResources --
2820 *
2821 *	Free memory etc associated with a Style.
2822 *
2823 * Results:
2824 *	None.
2825 *
2826 * Side effects:
2827 *	Memory is deallocated.
2828 *
2829 *----------------------------------------------------------------------
2830 */
2831
2832static void
2833IStyle_FreeResources(
2834    TreeCtrl *tree,		/* Widget info. */
2835    IStyle *style		/* Style to free. */
2836    )
2837{
2838    MStyle *masterStyle = style->master;
2839    int i;
2840
2841    if (masterStyle->numElements > 0) {
2842	for (i = 0; i < masterStyle->numElements; i++)
2843	    IElementLink_FreeResources(tree, &style->elements[i]);
2844#ifdef ALLOC_HAX
2845	TreeAlloc_CFree(tree->allocData, IElementLinkUid,
2846		(char *) style->elements, sizeof(IElementLink),
2847		masterStyle->numElements, ELEMENT_LINK_ROUND);
2848#else
2849	WCFREE(style->elements, IElementLink, masterStyle->numElements);
2850#endif
2851    }
2852#ifdef ALLOC_HAX
2853    TreeAlloc_Free(tree->allocData, IStyleUid, (char *) style, sizeof(IStyle));
2854#else
2855    WFREE(style, IStyle);
2856#endif
2857}
2858/*
2859 *----------------------------------------------------------------------
2860 *
2861 * TreeStyle_FreeResources --
2862 *
2863 *	Free memory etc associated with a Style.
2864 *
2865 * Results:
2866 *	None.
2867 *
2868 * Side effects:
2869 *	Memory is deallocated.
2870 *
2871 *----------------------------------------------------------------------
2872 */
2873
2874void
2875TreeStyle_FreeResources(
2876    TreeCtrl *tree,		/* Widget info. */
2877    TreeStyle style_		/* Token of style to free. */
2878    )
2879{
2880    MStyle *masterStyle = (MStyle *) style_;
2881    IStyle *style = (IStyle *) style_;
2882
2883    if (style->master == NULL)
2884	MStyle_FreeResources(tree, masterStyle);
2885    else
2886	IStyle_FreeResources(tree, style);
2887}
2888
2889/*
2890 *----------------------------------------------------------------------
2891 *
2892 * MStyle_FindElem --
2893 *
2894 *	Find an ElementLink in a style.
2895 *
2896 * Results:
2897 *	If found, a pointer to the ElementLink and index in the
2898 *	style's array of ElementLinks is returned; otherwise NULL
2899 *	is returned.
2900 *
2901 * Side effects:
2902 *	World peace.
2903 *
2904 *----------------------------------------------------------------------
2905 */
2906
2907static MElementLink *
2908MStyle_FindElem(
2909    TreeCtrl *tree,		/* Widget info. */
2910    MStyle *style,		/* Style to search. */
2911    TreeElement master,		/* Master element to find. */
2912    int *index			/* Returned index, may be NULL. */
2913    )
2914{
2915    int i;
2916
2917    for (i = 0; i < style->numElements; i++) {
2918	MElementLink *eLink = &style->elements[i];
2919	if (eLink->elem->name == master->name) {
2920	    if (index != NULL) (*index) = i;
2921	    return eLink;
2922	}
2923    }
2924    return NULL;
2925}
2926
2927/*
2928 *----------------------------------------------------------------------
2929 *
2930 * IStyle_FindElem --
2931 *
2932 *	Find an ElementLink in a style.
2933 *
2934 * Results:
2935 *	If found, a pointer to the ElementLink and index in the
2936 *	style's array of ElementLinks is returned; otherwise NULL
2937 *	is returned.
2938 *
2939 * Side effects:
2940 *	World peace.
2941 *
2942 *----------------------------------------------------------------------
2943 */
2944
2945static IElementLink *
2946IStyle_FindElem(
2947    TreeCtrl *tree,		/* Widget info. */
2948    IStyle *style,		/* Style to search. */
2949    TreeElement master,		/* Master element to find. */
2950    int *index			/* Returned index, may be NULL. */
2951    )
2952{
2953    MStyle *masterStyle = style->master;
2954    int i;
2955
2956    for (i = 0; i < masterStyle->numElements; i++) {
2957	IElementLink *eLink = &style->elements[i];
2958	if (eLink->elem->name == master->name) {
2959	    if (index != NULL) (*index) = i;
2960	    return eLink;
2961	}
2962    }
2963    return NULL;
2964}
2965
2966/*
2967 *----------------------------------------------------------------------
2968 *
2969 * TreeStyle_FindElement --
2970 *
2971 *	Find an ElementLink in a style.
2972 *
2973 * Results:
2974 *	If found, the index in the style's array of ElementLinks is
2975 *	returned with TCL_OK. Otherwise TCL_ERROR is returned and an
2976 *	error message is placed in the interpreter result.
2977 *
2978 * Side effects:
2979 *	None.
2980 *
2981 *----------------------------------------------------------------------
2982 */
2983
2984int
2985TreeStyle_FindElement(
2986    TreeCtrl *tree,		/* Widget info. */
2987    TreeStyle style_,		/* Token of style to search. */
2988    TreeElement elem,		/* Master element to find. */
2989    int *index			/* Returned index, may be NULL. */
2990    )
2991{
2992    MStyle *masterStyle = (MStyle *) style_;
2993    IStyle *style = (IStyle *) style_;
2994
2995    if (((style->master == NULL) &&
2996	    (MStyle_FindElem(tree, masterStyle, elem, index) == NULL)) ||
2997	    ((style->master != NULL) &&
2998	    (IStyle_FindElem(tree, style, elem, index) == NULL))) {
2999	FormatResult(tree->interp, "style %s does not use element %s",
3000	    style->master ? style->master->name : masterStyle->name,
3001	    elem->name);
3002	return TCL_ERROR;
3003    }
3004    return TCL_OK;
3005}
3006
3007/*
3008 *----------------------------------------------------------------------
3009 *
3010 * Element_CreateAndConfig --
3011 *
3012 *	Allocate and initialize a new Element (master or instance).
3013 *
3014 * Results:
3015 *	An Element is allocated, its createProc is called, default
3016 *	configuration options are set, then the configProc and changeProc
3017 *	are called to handle any given configurations options. If an
3018 *	error occurs NULL is returned.
3019 *
3020 * Side effects:
3021 *	Memory is allocated.
3022 *
3023 *----------------------------------------------------------------------
3024 */
3025
3026static TreeElement
3027Element_CreateAndConfig(
3028    TreeCtrl *tree,		/* Widget info. */
3029    TreeItem item,		/* Item containing the element. Should
3030				 * be NULL for a master element. */
3031    TreeItemColumn column,	/* Item-column containing the element.
3032				 * Should be NULL for a master element. */
3033    TreeElement masterElem,	/* Master element if creating an instance. */
3034    TreeElementType *type,	/* Element type. Should be NULL when
3035				 * creating an instance. */
3036    CONST char *name,		/* Name of master element, NULL for an
3037				 * instance. */
3038    int objc,			/* Array of intialial configuration. */
3039    Tcl_Obj *CONST objv[]	/* options. */
3040    )
3041{
3042    TreeElement elem;
3043    TreeElementArgs args;
3044
3045    if (masterElem != NULL) {
3046	type = masterElem->typePtr;
3047	name = masterElem->name;
3048    }
3049
3050#ifdef ALLOC_HAX
3051    elem = (TreeElement) TreeAlloc_Alloc(tree->allocData, type->name,
3052	    type->size);
3053#else
3054    elem = (TreeElement) ckalloc(type->size);
3055#endif
3056    memset(elem, '\0', type->size);
3057    elem->name = Tk_GetUid(name);
3058    elem->typePtr = type;
3059    elem->master = masterElem;
3060
3061    args.tree = tree;
3062    args.elem = elem;
3063    args.create.item = item;
3064    args.create.column = column;
3065    if ((*type->createProc)(&args) != TCL_OK) {
3066#ifdef ALLOC_HAX
3067	TreeAlloc_Free(tree->allocData, type->name, (char *) elem, type->size);
3068#else
3069	WFREE(elem, TreeElement_);
3070#endif
3071	return NULL;
3072    }
3073
3074    if (Tk_InitOptions(tree->interp, (char *) elem,
3075	type->optionTable, tree->tkwin) != TCL_OK) {
3076#ifdef ALLOC_HAX
3077	TreeAlloc_Free(tree->allocData, type->name, (char *) elem, type->size);
3078#else
3079	WFREE(elem, TreeElement_);
3080#endif
3081	return NULL;
3082    }
3083    args.config.objc = objc;
3084    args.config.objv = objv;
3085    args.config.flagSelf = 0;
3086    args.config.item = item;
3087    args.config.column = column;
3088    if ((*type->configProc)(&args) != TCL_OK) {
3089	(*type->deleteProc)(&args);
3090	Tk_FreeConfigOptions((char *) elem,
3091	    type->optionTable,
3092	    tree->tkwin);
3093	DynamicOption_Free(tree, elem->options, type->optionSpecs);
3094#ifdef ALLOC_HAX
3095	TreeAlloc_Free(tree->allocData, type->name, (char *) elem, type->size);
3096#else
3097	WFREE(elem, TreeElement_);
3098#endif
3099	return NULL;
3100    }
3101
3102    args.change.flagSelf = args.config.flagSelf;
3103    args.change.flagTree = 0;
3104    args.change.flagMaster = 0;
3105    (*type->changeProc)(&args);
3106
3107    return elem;
3108}
3109
3110/*
3111 *----------------------------------------------------------------------
3112 *
3113 * Style_CreateElem --
3114 *
3115 *	Allocate and initialize a new instance Element in a IStyle
3116 *	(if it doesn't already exist) and return its associated
3117 *	IElementLink.
3118 *
3119 * Results:
3120 *	If the style already has a matching instance element, then a
3121 *	pointer to an existing IElementLink is returned.
3122 *	If the style does not already have a matching instance element,
3123 *	then a new one is created and a pointer to an existing
3124 *	IElementLink is returned.
3125 *	If an error occurs creating the new element the result is
3126 *	NULL.
3127 *
3128 * Side effects:
3129 *	Memory is allocated.
3130 *
3131 *----------------------------------------------------------------------
3132 */
3133
3134static IElementLink *
3135Style_CreateElem(
3136    TreeCtrl *tree,		/* Widget info. */
3137    TreeItem item,		/* Item containing the element. */
3138    TreeItemColumn column,	/* Item-column containing the element. */
3139    IStyle *style,		/* Style to search/add the element to. */
3140    TreeElement masterElem,	/* Element to find or create and instance of. */
3141    int *isNew)			/* If non-NULL, set to TRUE if a new instance
3142				 * element was created. */
3143{
3144    MStyle *masterStyle = style->master;
3145    IElementLink *eLink = NULL;
3146    TreeElement elem;
3147    int i;
3148
3149    if (masterElem->master != NULL)
3150	panic("Style_CreateElem called with instance Element");
3151
3152    if (isNew != NULL) (*isNew) = FALSE;
3153
3154    for (i = 0; i < masterStyle->numElements; i++) {
3155	eLink = &style->elements[i];
3156	if (eLink->elem == masterElem) {
3157	    /* Allocate instance Element here */
3158	    break;
3159	}
3160
3161	/* Instance Style already has instance Element */
3162	if (eLink->elem->name == masterElem->name)
3163	    return eLink;
3164    }
3165
3166    /* Error: Element isn't in the master Style */
3167    if (i == masterStyle->numElements)
3168	return NULL;
3169
3170    elem = Element_CreateAndConfig(tree, item, column, masterElem, NULL, NULL, 0, NULL);
3171    if (elem == NULL)
3172	return NULL;
3173
3174    eLink->elem = elem;
3175    if (isNew != NULL) (*isNew) = TRUE;
3176    return eLink;
3177}
3178
3179/*
3180 *----------------------------------------------------------------------
3181 *
3182 * TreeStyle_NewInstance --
3183 *
3184 *	Create and initialize a new instance of a master style.
3185 *
3186 * Results:
3187 *	A new instance Style. The new array of ElementLinks is
3188 *	initialized to contain pointers to master elements; instance
3189 *	elements are created the first time they are configured.
3190 *
3191 * Side effects:
3192 *	Memory is allocated.
3193 *
3194 *----------------------------------------------------------------------
3195 */
3196
3197TreeStyle
3198TreeStyle_NewInstance(
3199    TreeCtrl *tree,		/* Widget info. */
3200    TreeStyle style_		/* Master style to create instance of. */
3201    )
3202{
3203    MStyle *style = (MStyle *) style_;
3204    IStyle *copy;
3205    IElementLink *eLink;
3206    int i;
3207
3208#ifdef ALLOC_HAX
3209    copy = (IStyle *) TreeAlloc_Alloc(tree->allocData, IStyleUid, sizeof(IStyle));
3210#else
3211    copy = (IStyle *) ckalloc(sizeof(IStyle));
3212#endif
3213    memset(copy, '\0', sizeof(IStyle));
3214    copy->master = style;
3215    copy->neededWidth = -1;
3216    copy->neededHeight = -1;
3217    if (style->numElements > 0) {
3218#ifdef ALLOC_HAX
3219	copy->elements = (IElementLink *) TreeAlloc_CAlloc(tree->allocData,
3220		IElementLinkUid, sizeof(IElementLink), style->numElements,
3221		ELEMENT_LINK_ROUND);
3222#else
3223	copy->elements = (IElementLink *) ckalloc(sizeof(IElementLink) *
3224		style->numElements);
3225#endif
3226	memset(copy->elements, '\0', sizeof(IElementLink) * style->numElements);
3227	for (i = 0; i < style->numElements; i++) {
3228	    eLink = &copy->elements[i];
3229	    eLink->elem = style->elements[i].elem;
3230#ifdef CACHE_ELEM_SIZE
3231	    eLink->neededWidth = -1;
3232	    eLink->neededHeight = -1;
3233#endif
3234	}
3235    }
3236
3237    return (TreeStyle) copy;
3238}
3239
3240/*
3241 *----------------------------------------------------------------------
3242 *
3243 * Element_FromObj --
3244 *
3245 *	Convert a Tcl_Obj to a master element.
3246 *
3247 * Results:
3248 *	A standard Tcl result.
3249 *
3250 * Side effects:
3251 *	None.
3252 *
3253 *----------------------------------------------------------------------
3254 */
3255
3256static int
3257Element_FromObj(
3258    TreeCtrl *tree,		/* Widget info. */
3259    Tcl_Obj *obj,		/* Object to convert from. */
3260    TreeElement *elemPtr	/* Returned record. */
3261    )
3262{
3263    char *name;
3264    Tcl_HashEntry *hPtr;
3265
3266    name = Tcl_GetString(obj);
3267    hPtr = Tcl_FindHashEntry(&tree->elementHash, name);
3268    if (hPtr == NULL) {
3269	Tcl_AppendResult(tree->interp, "element \"", name, "\" doesn't exist",
3270	    NULL);
3271	return TCL_ERROR;
3272    }
3273    (*elemPtr) = (TreeElement) Tcl_GetHashValue(hPtr);
3274    return TCL_OK;
3275}
3276
3277/*
3278 *----------------------------------------------------------------------
3279 *
3280 * TreeElement_FromObj --
3281 *
3282 *	Convert a Tcl_Obj to a master element.
3283 *
3284 * Results:
3285 *	A standard Tcl result.
3286 *
3287 * Side effects:
3288 *	None.
3289 *
3290 *----------------------------------------------------------------------
3291 */
3292
3293int
3294TreeElement_FromObj(
3295    TreeCtrl *tree,		/* Widget info. */
3296    Tcl_Obj *obj,		/* Object to convert from. */
3297    TreeElement *elemPtr	/* Returned master element token. */
3298    )
3299{
3300    return Element_FromObj(tree, obj, elemPtr);
3301}
3302
3303
3304/*
3305 *----------------------------------------------------------------------
3306 *
3307 * TreeElement_IsType --
3308 *
3309 *	Determine if an element is of a certain type.
3310 *
3311 * Results:
3312 *	TRUE if the type matches, otherwise FALSE.
3313 *
3314 * Side effects:
3315 *	None.
3316 *
3317 *----------------------------------------------------------------------
3318 */
3319
3320int
3321TreeElement_IsType(
3322    TreeCtrl *tree,		/* Widget info. */
3323    TreeElement elem,		/* Element to check. */
3324    CONST char *type		/* NULL-terminated element type name. */
3325    )
3326{
3327    return strcmp(elem->typePtr->name, type) == 0;
3328}
3329
3330/*
3331 *----------------------------------------------------------------------
3332 *
3333 * TreeStyle_FromObj --
3334 *
3335 *	Convert a Tcl_Obj to a master style.
3336 *
3337 * Results:
3338 *	A standard Tcl result.
3339 *
3340 * Side effects:
3341 *	None.
3342 *
3343 *----------------------------------------------------------------------
3344 */
3345
3346int
3347TreeStyle_FromObj(
3348    TreeCtrl *tree,		/* Widget info. */
3349    Tcl_Obj *obj,		/* Object to convert from. */
3350    TreeStyle *stylePtr)	/* Returned master style token. */
3351{
3352    char *name;
3353    Tcl_HashEntry *hPtr;
3354
3355    name = Tcl_GetString(obj);
3356    hPtr = Tcl_FindHashEntry(&tree->styleHash, name);
3357    if (hPtr == NULL) {
3358	Tcl_AppendResult(tree->interp, "style \"", name, "\" doesn't exist",
3359	    NULL);
3360	return TCL_ERROR;
3361    }
3362    (*stylePtr) = (TreeStyle) Tcl_GetHashValue(hPtr);
3363    return TCL_OK;
3364}
3365
3366/*
3367 *----------------------------------------------------------------------
3368 *
3369 * Element_ToObj --
3370 *
3371 *	Create a new Tcl_Obj representing an element.
3372 *
3373 * Results:
3374 *	A Tcl_Obj.
3375 *
3376 * Side effects:
3377 *	Memory is allocated.
3378 *
3379 *----------------------------------------------------------------------
3380 */
3381
3382static Tcl_Obj *
3383Element_ToObj(
3384    TreeElement elem		/* Element to create Tcl_Obj from. */
3385    )
3386{
3387    return Tcl_NewStringObj(elem->name, -1);
3388}
3389
3390/*
3391 *----------------------------------------------------------------------
3392 *
3393 * TreeStyle_ToObj --
3394 *
3395 *	Create a new Tcl_Obj representing a style.
3396 *
3397 * Results:
3398 *	A Tcl_Obj.
3399 *
3400 * Side effects:
3401 *	Memory is allocated.
3402 *
3403 *----------------------------------------------------------------------
3404 */
3405
3406Tcl_Obj *
3407TreeStyle_ToObj(
3408    TreeStyle style_		/* Style token to create Tcl_Obj from. */
3409    )
3410{
3411    MStyle *masterStyle = (MStyle *) style_;
3412    IStyle *style = (IStyle *) style_;
3413
3414    if (style->master != NULL)
3415	masterStyle = style->master;
3416    return Tcl_NewStringObj(masterStyle->name, -1);
3417}
3418
3419/*
3420 *----------------------------------------------------------------------
3421 *
3422 * Style_Changed --
3423 *
3424 *	Called when a master style is configured or the layout of one
3425 *	of its elements changes.
3426 *
3427 * Results:
3428 *	For each item-column using an instance of the given master
3429 *	style, size and display info is marked out-of-date.
3430 *
3431 * Side effects:
3432 *	Display changes.
3433 *
3434 *----------------------------------------------------------------------
3435 */
3436
3437static void
3438Style_Changed(
3439    TreeCtrl *tree,		/* Widget info. */
3440    MStyle *masterStyle		/* Style that changed. */
3441    )
3442{
3443    TreeItem item;
3444    TreeItemColumn column;
3445    TreeColumn treeColumn;
3446    Tcl_HashEntry *hPtr;
3447    Tcl_HashSearch search;
3448    int columnIndex, layout;
3449    int updateDInfo = FALSE;
3450    IStyle *style;
3451
3452    hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
3453    while (hPtr != NULL) {
3454	item = (TreeItem) Tcl_GetHashValue(hPtr);
3455	treeColumn = tree->columns;
3456	column = TreeItem_GetFirstColumn(tree, item);
3457	columnIndex = 0;
3458	layout = FALSE;
3459	while (column != NULL) {
3460	    style = (IStyle *) TreeItemColumn_GetStyle(tree, column);
3461	    if ((style != NULL) && (style->master == masterStyle)) {
3462#ifdef CACHE_ELEM_SIZE
3463		int i;
3464		for (i = 0; i < masterStyle->numElements; i++) {
3465		    IElementLink *eLink = &style->elements[i];
3466		    /* This is needed if the -width/-height layout options change */
3467		    eLink->neededWidth = eLink->neededHeight = -1;
3468		}
3469#endif
3470		style->neededWidth = style->neededHeight = -1;
3471		Tree_InvalidateColumnWidth(tree, treeColumn);
3472		TreeItemColumn_InvalidateSize(tree, column);
3473		layout = TRUE;
3474	    }
3475	    columnIndex++;
3476	    column = TreeItemColumn_GetNext(tree, column);
3477	    treeColumn = TreeColumn_Next(treeColumn);
3478	}
3479	if (layout) {
3480	    TreeItem_InvalidateHeight(tree, item);
3481	    Tree_FreeItemDInfo(tree, item, NULL);
3482	    updateDInfo = TRUE;
3483	}
3484	hPtr = Tcl_NextHashEntry(&search);
3485    }
3486    if (updateDInfo)
3487	Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
3488}
3489
3490/*
3491 *----------------------------------------------------------------------
3492 *
3493 * MStyle_ChangeElementsAux --
3494 *
3495 *	Update the list of elements used by a style. Elements
3496 *	may be inserted or deleted.
3497 *
3498 * Results:
3499 *	The list of elements in the style is updated.
3500 *
3501 * Side effects:
3502 *	Memory may be allocated/deallocated.
3503 *
3504 *----------------------------------------------------------------------
3505 */
3506
3507static void
3508MStyle_ChangeElementsAux(
3509    TreeCtrl *tree,		/* Widget info. */
3510    MStyle *style,		/* Master style to be updated. */
3511    int count,			/* The number of elements in the style after
3512				 * this routine finishes. */
3513    TreeElement *elemList,	/* List of master elements the style uses. */
3514    int *map			/* Array of indexes into the list of elements
3515				 * currently used by the style. */
3516    )
3517{
3518    MElementLink *eLink, *eLinks = NULL;
3519    int i, staticKeep[STATIC_SIZE], *keep = staticKeep;
3520
3521    STATIC_ALLOC(keep, int, style->numElements);
3522
3523    if (count > 0) {
3524#ifdef ALLOC_HAX
3525	eLinks = (MElementLink *) TreeAlloc_CAlloc(tree->allocData,
3526		MElementLinkUid, sizeof(MElementLink), count,
3527		ELEMENT_LINK_ROUND);
3528#else
3529	eLinks = (MElementLink *) ckalloc(sizeof(MElementLink) * count);
3530#endif
3531    }
3532
3533    /* Assume we are discarding all the old ElementLinks */
3534    for (i = 0; i < style->numElements; i++)
3535	keep[i] = 0;
3536
3537    for (i = 0; i < count; i++) {
3538	if (map[i] != -1) {
3539	    eLinks[i] = style->elements[map[i]];
3540	    keep[map[i]] = 1;
3541	} else {
3542	    eLink = MElementLink_Init(&eLinks[i], elemList[i]);
3543	}
3544    }
3545
3546    if (style->numElements > 0) {
3547	/* Free unused ElementLinks */
3548	for (i = 0; i < style->numElements; i++) {
3549	    if (!keep[i]) {
3550		MElementLink_FreeResources(tree, &style->elements[i]);
3551	    }
3552	}
3553#ifdef ALLOC_HAX
3554	TreeAlloc_CFree(tree->allocData, MElementLinkUid,
3555		(char *) style->elements, sizeof(MElementLink),
3556		style->numElements, ELEMENT_LINK_ROUND);
3557#else
3558	WCFREE(style->elements, MElementLink, style->numElements);
3559#endif
3560    }
3561
3562    STATIC_FREE(keep, int, style->numElements);
3563
3564    style->elements = eLinks;
3565    style->numElements = count;
3566}
3567
3568/*
3569 *----------------------------------------------------------------------
3570 *
3571 * IStyle_ChangeElementsAux --
3572 *
3573 *	Update the list of elements used by a style. Elements
3574 *	may be inserted or deleted.
3575 *
3576 * Results:
3577 *	The list of elements in the style is updated.
3578 *
3579 * Side effects:
3580 *	Memory may be allocated/deallocated.
3581 *
3582 *----------------------------------------------------------------------
3583 */
3584
3585static void
3586IStyle_ChangeElementsAux(
3587    TreeCtrl *tree,		/* Widget info. */
3588    IStyle *style,		/* Instance style to be updated. */
3589    int oldCount,		/* The previous number of elements. */
3590    int count,			/* The number of elements in the style after
3591				 * this routine finishes. */
3592    TreeElement *elemList,	/* List of master elements the style uses. */
3593    int *map			/* Array of indexes into the list of elements
3594				 * currently used by the style. */
3595    )
3596{
3597    IElementLink *eLink, *eLinks = NULL;
3598    int i, staticKeep[STATIC_SIZE], *keep = staticKeep;
3599
3600    STATIC_ALLOC(keep, int, oldCount);
3601
3602    if (count > 0) {
3603#ifdef ALLOC_HAX
3604	eLinks = (IElementLink *) TreeAlloc_CAlloc(tree->allocData,
3605		IElementLinkUid, sizeof(IElementLink), count,
3606		ELEMENT_LINK_ROUND);
3607#else
3608	eLinks = (IElementLink *) ckalloc(sizeof(IElementLink) * count);
3609#endif
3610    }
3611
3612    /* Assume we are discarding all the old ElementLinks */
3613    for (i = 0; i < oldCount; i++)
3614	keep[i] = 0;
3615
3616    for (i = 0; i < count; i++) {
3617	if (map[i] != -1) {
3618	    eLinks[i] = style->elements[map[i]];
3619	    keep[map[i]] = 1;
3620	} else {
3621	    eLink = &eLinks[i];
3622	    eLink->elem = elemList[i];
3623#ifdef CACHE_ELEM_SIZE
3624	    eLink->neededWidth = eLink->neededHeight = -1;
3625#endif
3626	}
3627    }
3628
3629    if (oldCount > 0) {
3630	/* Free unused ElementLinks */
3631	for (i = 0; i < oldCount; i++) {
3632	    if (!keep[i]) {
3633		IElementLink_FreeResources(tree, &style->elements[i]);
3634	    }
3635	}
3636#ifdef ALLOC_HAX
3637	TreeAlloc_CFree(tree->allocData, IElementLinkUid,
3638		(char *) style->elements, sizeof(IElementLink),
3639		oldCount, ELEMENT_LINK_ROUND);
3640#else
3641	WCFREE(style->elements, IElementLink, oldCount);
3642#endif
3643    }
3644
3645    STATIC_FREE(keep, int, oldCount);
3646
3647    style->elements = eLinks;
3648}
3649
3650/*
3651 *----------------------------------------------------------------------
3652 *
3653 * Style_ChangeElements --
3654 *
3655 *	Update the list of elements used by a style. Elements
3656 *	may be inserted or deleted.
3657 *
3658 * Results:
3659 *	The list of elements in the master style is updated. For
3660 *	each item-column using an instance of the master style,
3661 *	the list of elements is updated.
3662 *
3663 * Side effects:
3664 *	Display changes.
3665 *
3666 *----------------------------------------------------------------------
3667 */
3668
3669static void
3670Style_ChangeElements(
3671    TreeCtrl *tree,		/* Widget info. */
3672    MStyle *masterStyle,	/* Master style to be updated. */
3673    int count,			/* The number of elements in the style after
3674				 * this routine finishes. */
3675    TreeElement *elemList,	/* List of master elements the style uses. */
3676    int *map			/* Array of indexes into the list of elements
3677				 * currently used by the style. */
3678    )
3679{
3680    TreeItem item;
3681    TreeItemColumn column;
3682    TreeColumn treeColumn;
3683    Tcl_HashEntry *hPtr;
3684    Tcl_HashSearch search;
3685    int columnIndex, layout;
3686    int updateDInfo = FALSE;
3687    IStyle *style;
3688    int i, j, k, oldCount;
3689
3690    /* Update -union lists */
3691    for (i = 0; i < masterStyle->numElements; i++) {
3692	MElementLink *eLink = &masterStyle->elements[i];
3693	int staticKeep[STATIC_SIZE], *keep = staticKeep;
3694	int onionCnt = 0, *onion = NULL;
3695
3696	if (eLink->onion == NULL)
3697	    continue;
3698
3699	STATIC_ALLOC(keep, int, eLink->onionCount);
3700
3701	/* Check every Element in this -union */
3702	for (j = 0; j < eLink->onionCount; j++) {
3703	    MElementLink *eLink2 = &masterStyle->elements[eLink->onion[j]];
3704
3705	    /* Check the new list of Elements */
3706	    keep[j] = -1;
3707	    for (k = 0; k < count; k++) {
3708		/* This new Element is in the -union */
3709		if (elemList[k] == eLink2->elem) {
3710		    keep[j] = k;
3711		    onionCnt++;
3712		    break;
3713		}
3714	    }
3715	}
3716
3717	if (onionCnt > 0) {
3718	    if (onionCnt != eLink->onionCount)
3719		onion = (int *) ckalloc(sizeof(int) * onionCnt);
3720	    else
3721		onion = eLink->onion;
3722	    k = 0;
3723	    for (j = 0; j < eLink->onionCount; j++) {
3724		if (keep[j] != -1)
3725		    onion[k++] = keep[j];
3726	    }
3727	}
3728
3729	STATIC_FREE(keep, int, eLink->onionCount);
3730
3731	if (onionCnt != eLink->onionCount) {
3732	    WCFREE(eLink->onion, int, eLink->onionCount);
3733	    eLink->onion = onion;
3734	    eLink->onionCount = onionCnt;
3735	}
3736    }
3737
3738    oldCount = masterStyle->numElements;
3739    MStyle_ChangeElementsAux(tree, masterStyle, count, elemList, map);
3740
3741    hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
3742    while (hPtr != NULL) {
3743	item = (TreeItem) Tcl_GetHashValue(hPtr);
3744	treeColumn = tree->columns;
3745	column = TreeItem_GetFirstColumn(tree, item);
3746	columnIndex = 0;
3747	layout = FALSE;
3748	while (column != NULL) {
3749	    style = (IStyle *) TreeItemColumn_GetStyle(tree, column);
3750	    if ((style != NULL) && (style->master == masterStyle)) {
3751		IStyle_ChangeElementsAux(tree, style, oldCount, count, elemList, map);
3752		style->neededWidth = style->neededHeight = -1;
3753		Tree_InvalidateColumnWidth(tree, treeColumn);
3754		TreeItemColumn_InvalidateSize(tree, column);
3755		layout = TRUE;
3756	    }
3757	    columnIndex++;
3758	    column = TreeItemColumn_GetNext(tree, column);
3759	    treeColumn = TreeColumn_Next(treeColumn);
3760	}
3761	if (layout) {
3762	    TreeItem_InvalidateHeight(tree, item);
3763	    Tree_FreeItemDInfo(tree, item, NULL);
3764	    updateDInfo = TRUE;
3765	}
3766	hPtr = Tcl_NextHashEntry(&search);
3767    }
3768    if (updateDInfo)
3769	Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
3770}
3771
3772/*
3773 *----------------------------------------------------------------------
3774 *
3775 * Style_ElemChanged --
3776 *
3777 *	Called when a master element or TreeCtrl is configured.
3778 *
3779 * Results:
3780 *	A check is made on each item-column to see if it is using
3781 *	the element. The size of any element/column/item affected
3782 *	is marked out-of-date.
3783 *
3784 * Side effects:
3785 *	Display changes.
3786 *
3787 *----------------------------------------------------------------------
3788 */
3789
3790static void
3791Style_ElemChanged(
3792    TreeCtrl *tree,		/* Widget info. */
3793    MStyle *masterStyle,	/* Master style that uses the element. */
3794    TreeElement masterElem,	/* Master element affected by the change. */
3795    int masterElemIndex,	/* Index of masterElem in masterStyle. */
3796    int flagM,			/* Flags returned by TreeElementType.configProc()
3797				 * if the master element was configured,
3798				 * zero if the TreeCtrl was configured. */
3799    int flagT,			/* TREE_CONF_xxx flags if the TreeCtrl was
3800				 * configured, zero if the master element
3801				 * was configured. */
3802    int csM			/* CS_xxx flags returned by
3803				 * TreeElementType.changeProc(). */
3804    )
3805{
3806    TreeItem item;
3807    TreeItemColumn column;
3808    TreeColumn treeColumn;
3809    Tcl_HashEntry *hPtr;
3810    Tcl_HashSearch search;
3811    IElementLink *eLink;
3812    int columnIndex;
3813    TreeElementArgs args;
3814    IStyle *style;
3815    int eMask, cMask, iMask;
3816    int updateDInfo = FALSE;
3817
3818    args.tree = tree;
3819    args.change.flagTree = flagT;
3820    args.change.flagMaster = flagM;
3821    args.change.flagSelf = 0;
3822
3823    hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
3824    while (hPtr != NULL) {
3825	item = (TreeItem) Tcl_GetHashValue(hPtr);
3826	treeColumn = tree->columns;
3827	column = TreeItem_GetFirstColumn(tree, item);
3828	columnIndex = 0;
3829	iMask = 0;
3830	while (column != NULL) {
3831	    cMask = 0;
3832	    style = (IStyle *) TreeItemColumn_GetStyle(tree, column);
3833	    if ((style != NULL) && (style->master == masterStyle)) {
3834		eLink = &style->elements[masterElemIndex];
3835		if (eLink->elem == masterElem) {
3836#ifdef CACHE_ELEM_SIZE
3837		    if (csM & CS_LAYOUT)
3838			eLink->neededWidth = eLink->neededHeight = -1;
3839#endif
3840		    cMask |= csM;
3841		}
3842		/* Instance element */
3843		else {
3844		    args.elem = eLink->elem;
3845		    eMask = (*masterElem->typePtr->changeProc)(&args);
3846#ifdef CACHE_ELEM_SIZE
3847		    if (eMask & CS_LAYOUT)
3848			eLink->neededWidth = eLink->neededHeight = -1;
3849#endif
3850		    cMask |= eMask;
3851		}
3852		iMask |= cMask;
3853		if (cMask & CS_LAYOUT) {
3854		    style->neededWidth = style->neededHeight = -1;
3855		    Tree_InvalidateColumnWidth(tree, treeColumn);
3856		    TreeItemColumn_InvalidateSize(tree, column);
3857		}
3858		else if (cMask & CS_DISPLAY) {
3859		    Tree_InvalidateItemDInfo(tree, treeColumn, item, NULL);
3860		}
3861	    }
3862	    columnIndex++;
3863	    column = TreeItemColumn_GetNext(tree, column);
3864	    treeColumn = TreeColumn_Next(treeColumn);
3865	}
3866	if (iMask & CS_LAYOUT) {
3867	    TreeItem_InvalidateHeight(tree, item);
3868	    Tree_FreeItemDInfo(tree, item, NULL);
3869	    updateDInfo = TRUE;
3870	}
3871	else if (iMask & CS_DISPLAY) {
3872	}
3873	hPtr = Tcl_NextHashEntry(&search);
3874    }
3875    if (updateDInfo)
3876	Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
3877}
3878
3879/*
3880 *----------------------------------------------------------------------
3881 *
3882 * TreeStyle_GetMaster --
3883 *
3884 *	Return the master style for an instance style.
3885 *
3886 * Results:
3887 *	Token for the master style.
3888 *
3889 * Side effects:
3890 *	None.
3891 *
3892 *----------------------------------------------------------------------
3893 */
3894
3895TreeStyle
3896TreeStyle_GetMaster(
3897    TreeCtrl *tree,		/* Widget info. */
3898    TreeStyle style_		/* Instance style token. */
3899    )
3900{
3901    return (TreeStyle) ((IStyle *) style_)->master;
3902}
3903
3904static Tcl_Obj *confImageObj = NULL;
3905static Tcl_Obj *confTextObj = NULL;
3906
3907/*
3908 *----------------------------------------------------------------------
3909 *
3910 * Style_GetImageOrText --
3911 *
3912 *	Return the value of a configuration option for an element.
3913 *
3914 * Results:
3915 *	The result of Tk_GetOptionValue for an option of the first
3916 *	element of the proper type (if any), otherwise NULL.
3917 *
3918 * Side effects:
3919 *	A Tcl_Obj may be allocated.
3920 *
3921 *----------------------------------------------------------------------
3922 */
3923
3924static Tcl_Obj *
3925Style_GetImageOrText(
3926    TreeCtrl *tree,		/* Widget info. */
3927    IStyle *style,		/* Style. */
3928    TreeElementType *typePtr,	/* Type of element to look for. */
3929    CONST char *optionName,	/* Name of config option to query. */
3930    Tcl_Obj **optionNameObj	/* Pointer to a Tcl_Obj to hold the
3931				 * option name. Initialized
3932				 * on the first call. */
3933    )
3934{
3935    IElementLink *eLink;
3936    int i;
3937
3938    if (*optionNameObj == NULL) {
3939	*optionNameObj = Tcl_NewStringObj(optionName, -1);
3940	Tcl_IncrRefCount(*optionNameObj);
3941    }
3942
3943    for (i = 0; i < style->master->numElements; i++) {
3944	eLink = &style->elements[i];
3945	if (ELEMENT_TYPE_MATCHES(eLink->elem->typePtr, typePtr)) {
3946	    Tcl_Obj *resultObjPtr;
3947	    resultObjPtr = Tk_GetOptionValue(tree->interp,
3948		(char *) eLink->elem, eLink->elem->typePtr->optionTable,
3949		*optionNameObj, tree->tkwin);
3950	    return resultObjPtr;
3951	}
3952    }
3953
3954    return NULL;
3955}
3956
3957/*
3958 *----------------------------------------------------------------------
3959 *
3960 * TreeStyle_GetImage --
3961 *
3962 *	Return the value of the -image option for the first
3963 *	image element in a style (if any).
3964 *
3965 * Results:
3966 *	The result of Tk_GetOptionValue if the element was found,
3967 *	otherwise NULL.
3968 *
3969 * Side effects:
3970 *	A Tcl_Obj may be allocated.
3971 *
3972 *----------------------------------------------------------------------
3973 */
3974
3975Tcl_Obj *
3976TreeStyle_GetImage(
3977    TreeCtrl *tree,		/* Widget info. */
3978    TreeStyle style_		/* Token for style to examine. */
3979    )
3980{
3981    return Style_GetImageOrText(tree, (IStyle *) style_, &treeElemTypeImage,
3982	"-image", &confImageObj);
3983}
3984
3985/*
3986 *----------------------------------------------------------------------
3987 *
3988 * TreeStyle_GetText --
3989 *
3990 *	Return the value of the -text option for the first
3991 *	text element in a style (if any).
3992 *
3993 * Results:
3994 *	The result of Tk_GetOptionValue if the element was found,
3995 *	otherwise NULL.
3996 *
3997 * Side effects:
3998 *	A Tcl_Obj may be allocated.
3999 *
4000 *----------------------------------------------------------------------
4001 */
4002
4003Tcl_Obj *
4004TreeStyle_GetText(
4005    TreeCtrl *tree,		/* Widget info. */
4006    TreeStyle style_		/* Token for style to examine. */
4007    )
4008{
4009    return Style_GetImageOrText(tree, (IStyle *) style_, &treeElemTypeText,
4010	"-text", &confTextObj);
4011}
4012
4013/*
4014 *----------------------------------------------------------------------
4015 *
4016 * Style_SetImageOrText --
4017 *
4018 *	Set the value of a configuration option for the first
4019 *	element of the proper type in a style (if any).
4020 *
4021 * Results:
4022 *	A standard Tcl result.
4023 *
4024 * Side effects:
4025 *	Size of the element and style will be marked out-of-date.
4026 *	A Tcl_Obj may be allocated.
4027 *
4028 *----------------------------------------------------------------------
4029 */
4030
4031static int
4032Style_SetImageOrText(
4033    TreeCtrl *tree,		/* Widget info. */
4034    TreeItem item,		/* Item containing the style. Needed if
4035				 * a new instance Element is created. */
4036    TreeItemColumn column,	/* Item-column containing the style */
4037    IStyle *style,		/* The style */
4038    TreeElementType *typePtr,	/* Element type to look for. */
4039    CONST char *optionName,	/* NULL-terminated config option name. */
4040    Tcl_Obj **optionNameObj,	/* Pointer to Tcl_Obj to hold the option
4041				 * name; initialized on the first call. */
4042    Tcl_Obj *valueObj		/* New value for the config option. */
4043    )
4044{
4045    MStyle *masterStyle = style->master;
4046    IElementLink *eLink;
4047    int i;
4048
4049    if (*optionNameObj == NULL) {
4050	*optionNameObj = Tcl_NewStringObj(optionName, -1);
4051	Tcl_IncrRefCount(*optionNameObj);
4052    }
4053
4054    for (i = 0; i < masterStyle->numElements; i++) {
4055	TreeElement masterElem = masterStyle->elements[i].elem;
4056	if (ELEMENT_TYPE_MATCHES(masterElem->typePtr, typePtr)) {
4057	    Tcl_Obj *objv[2];
4058	    TreeElementArgs args;
4059
4060	    eLink = Style_CreateElem(tree, item, column, style, masterElem, NULL);
4061
4062	    objv[0] = *optionNameObj;
4063	    objv[1] = valueObj;
4064	    args.tree = tree;
4065	    args.elem = eLink->elem;
4066	    args.config.objc = 2;
4067	    args.config.objv = objv;
4068	    args.config.flagSelf = 0;
4069	    args.config.item = item;
4070	    args.config.column = column;
4071	    if ((*eLink->elem->typePtr->configProc)(&args) != TCL_OK)
4072		return TCL_ERROR;
4073
4074	    args.change.flagSelf = args.config.flagSelf;
4075	    args.change.flagTree = 0;
4076	    args.change.flagMaster = 0;
4077	    (void) (*eLink->elem->typePtr->changeProc)(&args);
4078
4079#ifdef CACHE_ELEM_SIZE
4080	    eLink->neededWidth = eLink->neededHeight = -1;
4081#endif
4082	    style->neededWidth = style->neededHeight = -1;
4083	    break;
4084	}
4085    }
4086    return TCL_OK;
4087}
4088
4089/*
4090 *----------------------------------------------------------------------
4091 *
4092 * TreeStyle_SetImage --
4093 *
4094 *	Set the value of the -image option for the first image
4095 *	element in a style (if any).
4096 *
4097 * Results:
4098 *	A standard Tcl result.
4099 *
4100 * Side effects:
4101 *	Size of the element and style will be marked out-of-date.
4102 *	A Tcl_Obj may be allocated.
4103 *
4104 *----------------------------------------------------------------------
4105 */
4106
4107int
4108TreeStyle_SetImage(
4109    TreeCtrl *tree,		/* Widget info. */
4110    TreeItem item,		/* Item containing the style. */
4111    TreeItemColumn column,	/* Item-column containing the style. */
4112    TreeStyle style_,		/* The instance style. */
4113    Tcl_Obj *valueObj		/* New value for -image option. */
4114    )
4115{
4116    return Style_SetImageOrText(tree, item, column, (IStyle *) style_,
4117	&treeElemTypeImage, "-image", &confImageObj, valueObj);
4118}
4119
4120/*
4121 *----------------------------------------------------------------------
4122 *
4123 * TreeStyle_SetText --
4124 *
4125 *	Set the value of the -text option for the first text
4126 *	element in a style (if any).
4127 *
4128 * Results:
4129 *	A standard Tcl result.
4130 *
4131 * Side effects:
4132 *	Size of the element and style will be marked out-of-date.
4133 *	A Tcl_Obj may be allocated.
4134 *
4135 *----------------------------------------------------------------------
4136 */
4137
4138int
4139TreeStyle_SetText(
4140    TreeCtrl *tree,		/* Widget info. */
4141    TreeItem item,		/* Item containing the style. */
4142    TreeItemColumn column,	/* Item-column containing the style. */
4143    TreeStyle style_,		/* The instance style. */
4144    Tcl_Obj *valueObj		/* New value for -text option. */
4145    )
4146{
4147    return Style_SetImageOrText(tree, item, column, (IStyle *) style_,
4148	&treeElemTypeText, "-text", &confTextObj, valueObj);
4149}
4150
4151/*
4152 *----------------------------------------------------------------------
4153 *
4154 * Style_Deleted --
4155 *
4156 *	Called when a master style is about to be deleted. Any
4157 *	item-columns using an instance of the style have their style
4158 *	freed.
4159 *
4160 * Results:
4161 *	The TreeCtrl -defaultstyle option is updated if the deleted
4162 *	style was specified in the value of the option.
4163 *
4164 * Side effects:
4165 *	Display changes. Memory is deallocated.
4166 *
4167 *----------------------------------------------------------------------
4168 */
4169
4170static void
4171Style_Deleted(
4172    TreeCtrl *tree,		/* Widget info. */
4173    MStyle *masterStyle		/* The master style being deleted. */
4174    )
4175{
4176    TreeItem item;
4177    TreeItemColumn column;
4178    TreeColumn treeColumn;
4179    Tcl_HashEntry *hPtr;
4180    Tcl_HashSearch search;
4181    IStyle *style;
4182    int columnIndex;
4183
4184    hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
4185    while (hPtr != NULL) {
4186	item = (TreeItem) Tcl_GetHashValue(hPtr);
4187	treeColumn = tree->columns;
4188	column = TreeItem_GetFirstColumn(tree, item);
4189	columnIndex = 0;
4190	while (column != NULL) {
4191	    style = (IStyle *) TreeItemColumn_GetStyle(tree, column);
4192	    if ((style != NULL) && (style->master == masterStyle)) {
4193		Tree_InvalidateColumnWidth(tree, treeColumn);
4194		TreeItemColumn_ForgetStyle(tree, column);
4195		TreeItem_InvalidateHeight(tree, item);
4196		Tree_FreeItemDInfo(tree, item, NULL);
4197	    }
4198	    columnIndex++;
4199	    column = TreeItemColumn_GetNext(tree, column);
4200	    treeColumn = TreeColumn_Next(treeColumn);
4201	}
4202	hPtr = Tcl_NextHashEntry(&search);
4203    }
4204
4205    /* Update each column's -itemstyle option */
4206    treeColumn = tree->columns;
4207    while (treeColumn != NULL) {
4208	TreeColumn_StyleDeleted(treeColumn, (TreeStyle) masterStyle);
4209	treeColumn = TreeColumn_Next(treeColumn);
4210    }
4211
4212#ifdef DEPRECATED
4213    /* Update -defaultstyle option */
4214    if (tree->defaultStyle.stylesObj != NULL) {
4215	Tcl_Obj *stylesObj = tree->defaultStyle.stylesObj;
4216	if (Tcl_IsShared(stylesObj)) {
4217	    stylesObj = Tcl_DuplicateObj(stylesObj);
4218	    Tcl_DecrRefCount(tree->defaultStyle.stylesObj);
4219	    Tcl_IncrRefCount(stylesObj);
4220	    tree->defaultStyle.stylesObj = stylesObj;
4221	}
4222	for (columnIndex = 0; columnIndex < tree->defaultStyle.numStyles; columnIndex++) {
4223	    Tcl_Obj *emptyObj;
4224	    if (tree->defaultStyle.styles[columnIndex] != (TreeStyle) masterStyle)
4225		continue;
4226	    tree->defaultStyle.styles[columnIndex] = NULL;
4227	    emptyObj = Tcl_NewObj();
4228	    Tcl_ListObjReplace(tree->interp, stylesObj, columnIndex, 1, 1, &emptyObj);
4229	}
4230    }
4231#endif /* DEPRECATED */
4232
4233#ifdef DRAGIMAGE_STYLE
4234    TreeDragImage_StyleDeleted(tree->dragImage, (TreeStyle) masterStyle);
4235#endif
4236}
4237
4238/*
4239 *----------------------------------------------------------------------
4240 *
4241 * Element_Changed --
4242 *
4243 *	Called when a master element or TreeCtrl has been configured.
4244 *
4245 * Results:
4246 *	Every master and instance style using the element is updated.
4247 *
4248 * Side effects:
4249 *	Display changes.
4250 *
4251 *----------------------------------------------------------------------
4252 */
4253
4254static void
4255Element_Changed(
4256    TreeCtrl *tree,		/* Widget info. */
4257    TreeElement masterElem,	/* Master element that may have changed. */
4258    int flagM,			/* Flags returned by TreeElementType.configProc()
4259				 * if the master element was configured,
4260				 * zero if the TreeCtrl was configured. */
4261    int flagT,			/* TREE_CONF_xxx flags if the TreeCtrl was
4262				 * configured, zero if the master element
4263				 * was configured. */
4264    int csM			/* CS_xxx flags returned by
4265				 * TreeElementType.changeProc(). */
4266    )
4267{
4268    Tcl_HashEntry *hPtr;
4269    Tcl_HashSearch search;
4270    MStyle *masterStyle;
4271    MElementLink *eLink;
4272    int i;
4273
4274    hPtr = Tcl_FirstHashEntry(&tree->styleHash, &search);
4275    while (hPtr != NULL) {
4276	masterStyle = (MStyle *) Tcl_GetHashValue(hPtr);
4277	for (i = 0; i < masterStyle->numElements; i++) {
4278	    eLink = &masterStyle->elements[i];
4279	    if (eLink->elem == masterElem) {
4280		Style_ElemChanged(tree, masterStyle, masterElem, i, flagM, flagT, csM);
4281		break;
4282	    }
4283	}
4284	hPtr = Tcl_NextHashEntry(&search);
4285    }
4286}
4287
4288/*
4289 *----------------------------------------------------------------------
4290 *
4291 * Element_Deleted --
4292 *
4293 *	Called when a master element is about to be deleted.
4294 *
4295 * Results:
4296 *	The list of elements in any master styles using the element is
4297 *	updated. Ditto for instance styles.
4298 *
4299 * Side effects:
4300 *	Display changes.
4301 *
4302 *----------------------------------------------------------------------
4303 */
4304
4305static void
4306Element_Deleted(
4307    TreeCtrl *tree,		/* Widget info. */
4308    TreeElement masterElem	/* Master element being deleted. */
4309    )
4310{
4311    Tcl_HashEntry *hPtr;
4312    Tcl_HashSearch search;
4313    MStyle *masterStyle;
4314    MElementLink *eLink;
4315    int i, j;
4316
4317    hPtr = Tcl_FirstHashEntry(&tree->styleHash, &search);
4318    while (hPtr != NULL) {
4319	masterStyle = (MStyle *) Tcl_GetHashValue(hPtr);
4320	for (i = 0; i < masterStyle->numElements; i++) {
4321	    eLink = &masterStyle->elements[i];
4322	    if (eLink->elem == masterElem) {
4323		TreeElement staticElemList[STATIC_SIZE],
4324		    *elemList = staticElemList;
4325		int staticElemMap[STATIC_SIZE], *elemMap = staticElemMap;
4326
4327		STATIC_ALLOC(elemList, TreeElement, masterStyle->numElements);
4328		STATIC_ALLOC(elemMap, int, masterStyle->numElements);
4329
4330		for (j = 0; j < masterStyle->numElements; j++) {
4331		    if (j == i)
4332			continue;
4333		    elemList[(j < i) ? j : (j - 1)] =
4334			masterStyle->elements[j].elem;
4335		    elemMap[(j < i) ? j : (j - 1)] = j;
4336		}
4337		Style_ChangeElements(tree, masterStyle,
4338		    masterStyle->numElements - 1, elemList, elemMap);
4339		STATIC_FREE(elemList, TreeElement, masterStyle->numElements + 1);
4340		STATIC_FREE(elemMap, int, masterStyle->numElements + 1);
4341		break;
4342	    }
4343	}
4344	hPtr = Tcl_NextHashEntry(&search);
4345    }
4346}
4347
4348/*
4349 *----------------------------------------------------------------------
4350 *
4351 * Tree_RedrawElement --
4352 *
4353 *	A STUB export. Schedules a redraw of the given item.
4354 *
4355 * Results:
4356 *	None.
4357 *
4358 * Side effects:
4359 *	Display changes.
4360 *
4361 *----------------------------------------------------------------------
4362 */
4363
4364void
4365Tree_RedrawElement(
4366    TreeCtrl *tree,		/* Widget info. */
4367    TreeItem item,		/* Item containing the element. */
4368    TreeElement elem		/* The element that changed. */
4369    )
4370{
4371    /* Master element */
4372    if (elem->master == NULL) {
4373    }
4374
4375    /* Instance element */
4376    else {
4377	Tree_InvalidateItemDInfo(tree, NULL, item, NULL);
4378    }
4379}
4380
4381typedef struct Iterate
4382{
4383    TreeCtrl *tree;
4384    TreeItem item;
4385    TreeItemColumn column;
4386    int columnIndex;
4387    IStyle *style;
4388    TreeElementType *elemTypePtr;
4389    IElementLink *eLink;
4390    Tcl_HashSearch search;
4391    Tcl_HashEntry *hPtr;
4392} Iterate;
4393
4394static int IterateItem(Iterate *iter)
4395{
4396    int i;
4397
4398    while (iter->column != NULL) {
4399	iter->style = (IStyle *) TreeItemColumn_GetStyle(iter->tree, iter->column);
4400	if (iter->style != NULL) {
4401	    for (i = 0; i < iter->style->master->numElements; i++) {
4402		iter->eLink = &iter->style->elements[i];
4403		if (ELEMENT_TYPE_MATCHES(iter->eLink->elem->typePtr, iter->elemTypePtr))
4404		    return 1;
4405	    }
4406	}
4407	iter->column = TreeItemColumn_GetNext(iter->tree, iter->column);
4408	iter->columnIndex++;
4409    }
4410    return 0;
4411}
4412
4413TreeIterate
4414Tree_ElementIterateBegin(
4415    TreeCtrl *tree,
4416    TreeElementType *elemTypePtr)
4417{
4418    Iterate *iter;
4419
4420    iter = (Iterate *) ckalloc(sizeof(Iterate));
4421    iter->tree = tree;
4422    iter->elemTypePtr = elemTypePtr;
4423    iter->hPtr = Tcl_FirstHashEntry(&tree->itemHash, &iter->search);
4424    while (iter->hPtr != NULL) {
4425	iter->item = (TreeItem) Tcl_GetHashValue(iter->hPtr);
4426	iter->column = TreeItem_GetFirstColumn(tree, iter->item);
4427	iter->columnIndex = 0;
4428	if (IterateItem(iter))
4429	    return (TreeIterate) iter;
4430	iter->hPtr = Tcl_NextHashEntry(&iter->search);
4431    }
4432    ckfree((char *) iter);
4433    return NULL;
4434}
4435
4436TreeIterate
4437Tree_ElementIterateNext(
4438    TreeIterate iter_)
4439{
4440    Iterate *iter = (Iterate *) iter_;
4441
4442    iter->column = TreeItemColumn_GetNext(iter->tree, iter->column);
4443    iter->columnIndex++;
4444    if (IterateItem(iter))
4445	return iter_;
4446    iter->hPtr = Tcl_NextHashEntry(&iter->search);
4447    while (iter->hPtr != NULL) {
4448	iter->item = (TreeItem) Tcl_GetHashValue(iter->hPtr);
4449	iter->column = TreeItem_GetFirstColumn(iter->tree, iter->item);
4450	iter->columnIndex = 0;
4451	if (IterateItem(iter))
4452	    return iter_;
4453	iter->hPtr = Tcl_NextHashEntry(&iter->search);
4454    }
4455    ckfree((char *) iter);
4456    return NULL;
4457}
4458
4459/*
4460 *----------------------------------------------------------------------
4461 *
4462 * Tree_ElementChangedItself --
4463 *
4464 *	Called when an element has reconfigured itself outside of
4465 *	any API calls. For example, when a window associated with a
4466 *	window element is resized, or a text element's -textvariable
4467 *	is set.
4468 *
4469 * Results:
4470 *	None.
4471 *
4472 * Side effects:
4473 *	Display changes.
4474 *
4475 *----------------------------------------------------------------------
4476 */
4477
4478void
4479Tree_ElementChangedItself(
4480    TreeCtrl *tree,		/* Widget info. */
4481    TreeItem item,		/* Item containing the element. */
4482    TreeItemColumn column,	/* Item-column containing the element. */
4483    TreeElement elem,		/* The element that changed. */
4484    int flags,			/* Element-specific configuration flags. */
4485    int csM			/* CS_xxx flags detailing the effects of
4486				 * the change. */
4487    )
4488{
4489    /* Master element. */
4490    if (item == NULL) {
4491	Element_Changed(tree, elem, flags, 0, csM);
4492	return;
4493    }
4494    if (csM & CS_LAYOUT) {
4495	IStyle *style = (IStyle *) TreeItemColumn_GetStyle(tree, column);
4496	int i;
4497	IElementLink *eLink = NULL;
4498	int columnIndex;
4499
4500	if (style == NULL)
4501	    panic("Tree_ElementChangedItself but style is NULL\n");
4502
4503	for (i = 0; i < style->master->numElements; i++) {
4504	    eLink = &style->elements[i];
4505	    if (eLink->elem == elem)
4506		break;
4507	}
4508
4509	if (eLink == NULL)
4510	    panic("Tree_ElementChangedItself but eLink is NULL\n");
4511
4512	columnIndex = TreeItemColumn_Index(tree, item, column);
4513
4514#ifdef CACHE_ELEM_SIZE
4515	eLink->neededWidth = eLink->neededHeight = -1;
4516#endif
4517	style->neededWidth = style->neededHeight = -1;
4518
4519	Tree_InvalidateColumnWidth(tree, Tree_FindColumn(tree, columnIndex));
4520	TreeItemColumn_InvalidateSize(tree, column);
4521	TreeItem_InvalidateHeight(tree, item);
4522	Tree_FreeItemDInfo(tree, item, NULL);
4523	Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
4524    }
4525    else if (csM & CS_DISPLAY) {
4526	int columnIndex;
4527
4528	columnIndex = TreeItemColumn_Index(tree, item, column);
4529	Tree_InvalidateItemDInfo(tree, Tree_FindColumn(tree, columnIndex),
4530		item, NULL);
4531    }
4532}
4533
4534void Tree_ElementIterateChanged(TreeIterate iter_, int mask)
4535{
4536    Iterate *iter = (Iterate *) iter_;
4537
4538    if (mask & CS_LAYOUT) {
4539#ifdef CACHE_ELEM_SIZE
4540	iter->eLink->neededWidth = iter->eLink->neededHeight = -1;
4541#endif
4542	iter->style->neededWidth = iter->style->neededHeight = -1;
4543	Tree_InvalidateColumnWidth(iter->tree,
4544	    Tree_FindColumn(iter->tree, iter->columnIndex));
4545	TreeItemColumn_InvalidateSize(iter->tree, iter->column);
4546	TreeItem_InvalidateHeight(iter->tree, iter->item);
4547	Tree_FreeItemDInfo(iter->tree, iter->item, NULL);
4548	Tree_DInfoChanged(iter->tree, DINFO_REDO_RANGES);
4549    }
4550    if (mask & CS_DISPLAY)
4551	Tree_InvalidateItemDInfo(iter->tree, NULL, iter->item, NULL);
4552}
4553
4554TreeElement Tree_ElementIterateGet(TreeIterate iter_)
4555{
4556    Iterate *iter = (Iterate *) iter_;
4557
4558    return iter->eLink->elem;
4559}
4560
4561/*
4562 *----------------------------------------------------------------------
4563 *
4564 * TreeStyle_TreeChanged --
4565 *
4566 *	Called when a TreeCtrl is configured. This handles changes to
4567 *	the -font option affecting text elements for example.
4568 *
4569 * Results:
4570 *	Calls the changeProc on every master element. Any elements
4571 *	affected by the change are eventually redisplayed.
4572 *
4573 * Side effects:
4574 *	Display changes.
4575 *
4576 *----------------------------------------------------------------------
4577 */
4578
4579void
4580TreeStyle_TreeChanged(
4581    TreeCtrl *tree,		/* Widget info. */
4582    int flagT			/* TREE_CONF_xxx flags. */
4583    )
4584{
4585    Tcl_HashEntry *hPtr;
4586    Tcl_HashSearch search;
4587    TreeElement masterElem;
4588    TreeElementArgs args;
4589    int eMask;
4590
4591    if (flagT == 0)
4592	return;
4593
4594    args.tree = tree;
4595    args.change.flagTree = flagT;
4596    args.change.flagMaster = 0;
4597    args.change.flagSelf = 0;
4598
4599    hPtr = Tcl_FirstHashEntry(&tree->elementHash, &search);
4600    while (hPtr != NULL) {
4601	masterElem = (TreeElement) Tcl_GetHashValue(hPtr);
4602	args.elem = masterElem;
4603	eMask = (*masterElem->typePtr->changeProc)(&args);
4604	Element_Changed(tree, masterElem, 0, flagT, eMask);
4605	hPtr = Tcl_NextHashEntry(&search);
4606    }
4607}
4608
4609/*
4610 *----------------------------------------------------------------------
4611 *
4612 * TreeStyle_ElementCget --
4613 *
4614 *	This procedure is invoked to process the [item element cget]
4615 *	widget command.  See the user documentation for details on what
4616 *	it does.
4617 *
4618 * Results:
4619 *	A standard Tcl result.
4620 *
4621 * Side effects:
4622 *	See the user documentation.
4623 *
4624 *----------------------------------------------------------------------
4625 */
4626
4627int
4628TreeStyle_ElementCget(
4629    TreeCtrl *tree,		/* Widget info. */
4630    TreeItem item,		/* Item containing the element. */
4631    TreeItemColumn column,	/* Item-column containing the element. */
4632    TreeStyle style_,		/* Style containing the element. */
4633    Tcl_Obj *elemObj,		/* Name of the element. */
4634    Tcl_Obj *optionNameObj	/* Name of the config option. */
4635    )
4636{
4637    IStyle *style = (IStyle *) style_;
4638    Tcl_Obj *resultObjPtr = NULL;
4639    TreeElement elem;
4640    IElementLink *eLink;
4641
4642    if (Element_FromObj(tree, elemObj, &elem) != TCL_OK)
4643	return TCL_ERROR;
4644
4645    eLink = IStyle_FindElem(tree, style, elem, NULL);
4646    if ((eLink != NULL) && (eLink->elem == elem)) {
4647	int index = TreeItemColumn_Index(tree, item, column);
4648	TreeColumn treeColumn = Tree_FindColumn(tree, index);
4649
4650	FormatResult(tree->interp,
4651	    "element %s is not configured in item %s%d column %s%d",
4652	    elem->name, tree->itemPrefix, TreeItem_GetID(tree, item),
4653	    tree->columnPrefix, TreeColumn_GetID(treeColumn));
4654	return TCL_ERROR;
4655    }
4656    if (eLink == NULL) {
4657	FormatResult(tree->interp, "style %s does not use element %s",
4658	    style->master->name, elem->name);
4659	return TCL_ERROR;
4660    }
4661
4662    resultObjPtr = Tk_GetOptionValue(tree->interp, (char *) eLink->elem,
4663	eLink->elem->typePtr->optionTable, optionNameObj, tree->tkwin);
4664    if (resultObjPtr == NULL)
4665	return TCL_ERROR;
4666    Tcl_SetObjResult(tree->interp, resultObjPtr);
4667    return TCL_OK;
4668}
4669
4670/*
4671 *----------------------------------------------------------------------
4672 *
4673 * TreeStyle_ElementConfigure --
4674 *
4675 *	This procedure is invoked to process the [item element configure]
4676 *	widget command.  See the user documentation for details on what
4677 *	it does.
4678 *
4679 * Results:
4680 *	A standard Tcl result.
4681 *
4682 * Side effects:
4683 *	See the user documentation.
4684 *
4685 *----------------------------------------------------------------------
4686 */
4687
4688int
4689TreeStyle_ElementConfigure(
4690    TreeCtrl *tree,		/* Widget info. */
4691    TreeItem item,		/* Item containing the element. */
4692    TreeItemColumn column,	/* Item-column containing the element. */
4693    TreeStyle style_,		/* Style containing the element. */
4694    Tcl_Obj *elemObj,		/* Name of the element. */
4695    int objc,			/* Number of arguments. */
4696    Tcl_Obj **objv,		/* Argument values. */
4697    int *eMask			/* Returned CS_xxx flags. */
4698    )
4699{
4700    IStyle *style = (IStyle *) style_;
4701    TreeElement elem;
4702    IElementLink *eLink;
4703    TreeElementArgs args;
4704
4705    (*eMask) = 0;
4706
4707    if (Element_FromObj(tree, elemObj, &elem) != TCL_OK)
4708	return TCL_ERROR;
4709
4710    if (objc <= 1) {
4711	Tcl_Obj *resultObjPtr;
4712
4713	eLink = IStyle_FindElem(tree, style, elem, NULL);
4714	if ((eLink != NULL) && (eLink->elem == elem)) {
4715	    int index = TreeItemColumn_Index(tree, item, column);
4716	    TreeColumn treeColumn = Tree_FindColumn(tree, index);
4717
4718	    FormatResult(tree->interp,
4719		"element %s is not configured in item %s%d column %s%d",
4720		elem->name, tree->itemPrefix, TreeItem_GetID(tree, item),
4721		tree->columnPrefix, TreeColumn_GetID(treeColumn));
4722	    return TCL_ERROR;
4723	}
4724	if (eLink == NULL) {
4725	    FormatResult(tree->interp, "style %s does not use element %s",
4726		style->master->name, elem->name);
4727	    return TCL_ERROR;
4728	}
4729
4730	resultObjPtr = Tk_GetOptionInfo(tree->interp, (char *) eLink->elem,
4731	    eLink->elem->typePtr->optionTable,
4732	    (objc == 0) ? (Tcl_Obj *) NULL : objv[0],
4733	    tree->tkwin);
4734	if (resultObjPtr == NULL)
4735	    return TCL_ERROR;
4736	Tcl_SetObjResult(tree->interp, resultObjPtr);
4737    } else {
4738	int isNew;
4739
4740	eLink = Style_CreateElem(tree, item, column, style, elem, &isNew);
4741	if (eLink == NULL) {
4742	    FormatResult(tree->interp, "style %s does not use element %s",
4743		style->master->name, elem->name);
4744	    return TCL_ERROR;
4745	}
4746
4747	/* Do this before configProc(). If eLink was just allocated and an
4748	 * error occurs in configProc() it won't be done */
4749	(*eMask) = 0;
4750	if (isNew) {
4751#ifdef CACHE_ELEM_SIZE
4752	    eLink->neededWidth = eLink->neededHeight = -1;
4753#endif
4754	    style->neededWidth = style->neededHeight = -1;
4755	    (*eMask) = CS_DISPLAY | CS_LAYOUT;
4756	}
4757
4758	args.tree = tree;
4759	args.elem = eLink->elem;
4760	args.config.objc = objc;
4761	args.config.objv = objv;
4762	args.config.flagSelf = 0;
4763	args.config.item = item;
4764	args.config.column = column;
4765	if ((*args.elem->typePtr->configProc)(&args) != TCL_OK)
4766	    return TCL_ERROR;
4767
4768	args.change.flagSelf = args.config.flagSelf;
4769	args.change.flagTree = 0;
4770	args.change.flagMaster = 0;
4771	(*eMask) |= (*elem->typePtr->changeProc)(&args);
4772
4773	if (!isNew && ((*eMask) & CS_LAYOUT)) {
4774#ifdef CACHE_ELEM_SIZE
4775	    eLink->neededWidth = eLink->neededHeight = -1;
4776#endif
4777	    style->neededWidth = style->neededHeight = -1;
4778	}
4779    }
4780    return TCL_OK;
4781}
4782
4783/*
4784 *----------------------------------------------------------------------
4785 *
4786 * TreeStyle_ElementActual --
4787 *
4788 *	This procedure is invoked to process the [item element perstate]
4789 *	widget command.  See the user documentation for details on what
4790 *	it does.
4791 *
4792 * Results:
4793 *	A standard Tcl result.
4794 *
4795 * Side effects:
4796 *	See the user documentation.
4797 *
4798 *----------------------------------------------------------------------
4799 */
4800
4801int
4802TreeStyle_ElementActual(
4803    TreeCtrl *tree,		/* Widget info. */
4804    TreeStyle style_,		/* The style. */
4805    int state,			/* STATE_xxx flags. */
4806    Tcl_Obj *elemObj,		/* Name of the element. */
4807    Tcl_Obj *optionNameObj	/* Name of the config option. */
4808    )
4809{
4810    IStyle *style = (IStyle *) style_;
4811    TreeElement masterElem;
4812    IElementLink *eLink;
4813    TreeElementArgs args;
4814
4815    if (Element_FromObj(tree, elemObj, &masterElem) != TCL_OK)
4816	return TCL_ERROR;
4817
4818    eLink = IStyle_FindElem(tree, style, masterElem, NULL);
4819    if (eLink == NULL) {
4820	FormatResult(tree->interp, "style %s does not use element %s",
4821	    style->master->name, masterElem->name);
4822	return TCL_ERROR;
4823    }
4824
4825    args.tree = tree;
4826    args.elem = eLink->elem;
4827    args.state = state;
4828    args.actual.obj = optionNameObj;
4829    return (*masterElem->typePtr->actualProc)(&args);
4830}
4831
4832/*
4833 *----------------------------------------------------------------------
4834 *
4835 * TreeElementCmd --
4836 *
4837 *	This procedure is invoked to process the [element]
4838 *	widget command.  See the user documentation for details on what
4839 *	it does.
4840 *
4841 * Results:
4842 *	A standard Tcl result.
4843 *
4844 * Side effects:
4845 *	See the user documentation.
4846 *
4847 *----------------------------------------------------------------------
4848 */
4849
4850int
4851TreeElementCmd(
4852    ClientData clientData,	/* Widget info. */
4853    Tcl_Interp *interp,		/* Current interpreter. */
4854    int objc,			/* Number of arguments. */
4855    Tcl_Obj *CONST objv[]	/* Argument values. */
4856    )
4857{
4858    TreeCtrl *tree = clientData;
4859    static CONST char *commandNames[] = {
4860	"cget", "configure", "create", "delete", "names", "perstate", "type",
4861	(char *) NULL
4862    };
4863    enum {
4864	COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_CREATE, COMMAND_DELETE,
4865	COMMAND_NAMES, COMMAND_PERSTATE, COMMAND_TYPE
4866    };
4867    int index;
4868
4869    if (objc < 3) {
4870	Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg ...?");
4871	return TCL_ERROR;
4872    }
4873
4874    if (Tcl_GetIndexFromObj(interp, objv[2], commandNames, "command", 0,
4875	&index) != TCL_OK) {
4876	return TCL_ERROR;
4877    }
4878
4879    switch (index) {
4880	case COMMAND_CGET: {
4881	    Tcl_Obj *resultObjPtr = NULL;
4882	    TreeElement elem;
4883
4884	    if (objc != 5) {
4885		Tcl_WrongNumArgs(interp, 3, objv, "name option");
4886		return TCL_ERROR;
4887	    }
4888	    if (Element_FromObj(tree, objv[3], &elem) != TCL_OK)
4889		return TCL_ERROR;
4890	    resultObjPtr = Tk_GetOptionValue(interp, (char *) elem,
4891		elem->typePtr->optionTable, objv[4], tree->tkwin);
4892	    if (resultObjPtr == NULL)
4893		return TCL_ERROR;
4894	    Tcl_SetObjResult(interp, resultObjPtr);
4895	    break;
4896	}
4897
4898	case COMMAND_CONFIGURE: {
4899	    Tcl_Obj *resultObjPtr = NULL;
4900	    TreeElement elem;
4901	    int eMask;
4902
4903	    if (objc < 4) {
4904		Tcl_WrongNumArgs(interp, 3, objv, "name ?option? ?value option value ...?");
4905		return TCL_ERROR;
4906	    }
4907	    if (Element_FromObj(tree, objv[3], &elem) != TCL_OK)
4908		return TCL_ERROR;
4909	    if (objc <= 5) {
4910		resultObjPtr = Tk_GetOptionInfo(interp, (char *) elem,
4911		    elem->typePtr->optionTable,
4912		    (objc == 4) ? (Tcl_Obj *) NULL : objv[4],
4913		    tree->tkwin);
4914		if (resultObjPtr == NULL)
4915		    return TCL_ERROR;
4916		Tcl_SetObjResult(interp, resultObjPtr);
4917	    } else {
4918		TreeElementArgs args;
4919
4920		args.tree = tree;
4921		args.elem = elem;
4922		args.config.objc = objc - 4;
4923		args.config.objv = objv + 4;
4924		args.config.flagSelf = 0;
4925		args.config.item = NULL;
4926		args.config.column = NULL;
4927		if ((*elem->typePtr->configProc)(&args) != TCL_OK)
4928		    return TCL_ERROR;
4929
4930		args.change.flagSelf = args.config.flagSelf;
4931		args.change.flagTree = 0;
4932		args.change.flagMaster = 0;
4933		eMask = (*elem->typePtr->changeProc)(&args);
4934
4935		Element_Changed(tree, elem, args.change.flagSelf, 0, eMask);
4936	    }
4937	    break;
4938	}
4939
4940	case COMMAND_CREATE: {
4941	    char *name;
4942	    int length;
4943	    int isNew;
4944	    TreeElement elem;
4945	    TreeElementType *typePtr;
4946	    Tcl_HashEntry *hPtr;
4947
4948	    if (objc < 5) {
4949		Tcl_WrongNumArgs(interp, 3, objv, "name type ?option value ...?");
4950		return TCL_ERROR;
4951	    }
4952	    name = Tcl_GetStringFromObj(objv[3], &length);
4953	    if (!length)
4954		return TCL_ERROR;
4955	    hPtr = Tcl_FindHashEntry(&tree->elementHash, name);
4956	    if (hPtr != NULL) {
4957		FormatResult(interp, "element \"%s\" already exists", name);
4958		return TCL_ERROR;
4959	    }
4960	    if (TreeElement_TypeFromObj(tree, objv[4], &typePtr) != TCL_OK)
4961		return TCL_ERROR;
4962	    elem = Element_CreateAndConfig(tree, NULL, NULL, NULL, typePtr, name, objc - 5, objv + 5);
4963	    if (elem == NULL)
4964		return TCL_ERROR;
4965	    hPtr = Tcl_CreateHashEntry(&tree->elementHash, name, &isNew);
4966	    Tcl_SetHashValue(hPtr, elem);
4967	    Tcl_SetObjResult(interp, Element_ToObj(elem));
4968	    break;
4969	}
4970
4971	case COMMAND_DELETE: {
4972	    TreeElement elem;
4973	    int i;
4974
4975	    for (i = 3; i < objc; i++) {
4976		if (Element_FromObj(tree, objv[i], &elem) != TCL_OK)
4977		    return TCL_ERROR;
4978		Element_Deleted(tree, elem);
4979		Element_FreeResources(tree, elem);
4980	    }
4981	    break;
4982	}
4983
4984	case COMMAND_NAMES: {
4985	    Tcl_Obj *listObj;
4986	    Tcl_HashSearch search;
4987	    Tcl_HashEntry *hPtr;
4988	    TreeElement elem;
4989
4990	    if (objc != 3) {
4991		Tcl_WrongNumArgs(interp, 3, objv, NULL);
4992		return TCL_ERROR;
4993	    }
4994	    listObj = Tcl_NewListObj(0, NULL);
4995	    hPtr = Tcl_FirstHashEntry(&tree->elementHash, &search);
4996	    while (hPtr != NULL) {
4997		elem = (TreeElement) Tcl_GetHashValue(hPtr);
4998		Tcl_ListObjAppendElement(interp, listObj, Element_ToObj(elem));
4999		hPtr = Tcl_NextHashEntry(&search);
5000	    }
5001	    Tcl_SetObjResult(interp, listObj);
5002	    break;
5003	}
5004
5005	/* T element perstate E option stateList */
5006	case COMMAND_PERSTATE: {
5007	    TreeElement elem;
5008	    int states[3];
5009	    TreeElementArgs args;
5010
5011	    if (objc != 6) {
5012		Tcl_WrongNumArgs(tree->interp, 3, objv,
5013		    "element option stateList");
5014		return TCL_ERROR;
5015	    }
5016
5017	    if (Element_FromObj(tree, objv[3], &elem) != TCL_OK)
5018		return TCL_ERROR;
5019
5020	    if (Tree_StateFromListObj(tree, objv[5], states,
5021		    SFO_NOT_OFF | SFO_NOT_TOGGLE) != TCL_OK)
5022		return TCL_ERROR;
5023
5024	    args.tree = tree;
5025	    args.elem = elem;
5026	    args.state = states[STATE_OP_ON];
5027	    args.actual.obj = objv[4];
5028	    return (*elem->typePtr->actualProc)(&args);
5029	}
5030
5031	case COMMAND_TYPE: {
5032	    TreeElement elem;
5033
5034	    if (objc != 4) {
5035		Tcl_WrongNumArgs(interp, 3, objv, "name");
5036		return TCL_ERROR;
5037	    }
5038	    if (Element_FromObj(tree, objv[3], &elem) != TCL_OK)
5039		return TCL_ERROR;
5040	    Tcl_SetResult(interp, elem->typePtr->name, TCL_STATIC); /* Tk_Uid */
5041	    break;
5042	}
5043    }
5044    return TCL_OK;
5045}
5046
5047/*
5048 *----------------------------------------------------------------------
5049 *
5050 * Style_CreateAndConfig --
5051 *
5052 *	Allocate and initialize a master style.
5053 *
5054 * Results:
5055 *	Pointer to the new Style, or NULL if an error occurs.
5056 *
5057 * Side effects:
5058 *	Memory is allocated.
5059 *
5060 *----------------------------------------------------------------------
5061 */
5062
5063static MStyle *
5064Style_CreateAndConfig(
5065    TreeCtrl *tree,		/* Widget info. */
5066    char *name,			/* Name of new style. */
5067    int objc,			/* Number of config-option arg-value pairs. */
5068    Tcl_Obj *CONST objv[]	/* Config-option arg-value pairs. */
5069    )
5070{
5071    MStyle *style;
5072
5073#ifdef ALLOC_HAX
5074    style = (MStyle *) TreeAlloc_Alloc(tree->allocData, MStyleUid,
5075	    sizeof(MStyle));
5076#else
5077    style = (MStyle *) ckalloc(sizeof(MStyle));
5078#endif
5079    memset(style, '\0', sizeof(MStyle));
5080    style->name = Tk_GetUid(name);
5081
5082    if (Tk_InitOptions(tree->interp, (char *) style,
5083	tree->styleOptionTable, tree->tkwin) != TCL_OK) {
5084#ifdef ALLOC_HAX
5085	TreeAlloc_Free(tree->allocData, MStyleUid, (char *) style, sizeof(MStyle));
5086#else
5087	WFREE(style, MStyle);
5088#endif
5089	return NULL;
5090    }
5091
5092    if (Tk_SetOptions(tree->interp, (char *) style,
5093	tree->styleOptionTable, objc, objv, tree->tkwin,
5094	NULL, NULL) != TCL_OK) {
5095	Tk_FreeConfigOptions((char *) style, tree->styleOptionTable, tree->tkwin);
5096#ifdef ALLOC_HAX
5097	TreeAlloc_Free(tree->allocData, MStyleUid, (char *) style, sizeof(MStyle));
5098#else
5099	WFREE(style, MStyle);
5100#endif
5101	return NULL;
5102    }
5103
5104    return style;
5105}
5106
5107/*
5108 *----------------------------------------------------------------------
5109 *
5110 * TreeStyle_ListElements --
5111 *
5112 *	Creates a Tcl list with the names of elements in a style.
5113 *
5114 * Results:
5115 *	If the style is a master style, the interpreter result holds
5116 *	a list of each element in the style. If the style is an
5117 *	instance style, the interpreter result holds a list of those
5118 *	elements configured for the style (i.e., instance elements).
5119 *
5120 * Side effects:
5121 *	Memory is allocated, interpreter result changed.
5122 *
5123 *----------------------------------------------------------------------
5124 */
5125
5126void
5127TreeStyle_ListElements(
5128    TreeCtrl *tree,		/* Widget info. */
5129    TreeStyle style_		/* The style. */
5130    )
5131{
5132    MStyle *masterStyle = (MStyle *) style_;
5133    IStyle *style = (IStyle *) style_;
5134    Tcl_Obj *listObj;
5135    TreeElement elem;
5136    int i, numElements = TreeStyle_NumElements(tree, style_);
5137
5138    if (numElements <= 0)
5139	return;
5140
5141    listObj = Tcl_NewListObj(0, NULL);
5142    for (i = 0; i < numElements; i++) {
5143	if (style->master != NULL) {
5144	    elem = style->elements[i].elem;
5145	    if (elem->master == NULL)
5146		continue;
5147	} else {
5148	    elem = masterStyle->elements[i].elem;
5149	}
5150	Tcl_ListObjAppendElement(tree->interp, listObj, Element_ToObj(elem));
5151    }
5152    Tcl_SetObjResult(tree->interp, listObj);
5153}
5154
5155enum {
5156    OPTION_DETACH, OPTION_DRAW, OPTION_EXPAND, OPTION_HEIGHT, OPTION_iEXPAND,
5157    OPTION_INDENT, OPTION_iPADX, OPTION_iPADY, OPTION_MAXHEIGHT,
5158    OPTION_MAXWIDTH, OPTION_MINHEIGHT, OPTION_MINWIDTH, OPTION_PADX,
5159    OPTION_PADY, OPTION_SQUEEZE, OPTION_STICKY, OPTION_UNION,
5160    OPTION_WIDTH, OPTION_VISIBLE
5161};
5162
5163/*
5164 *----------------------------------------------------------------------
5165 *
5166 * LayoutOptionToObj --
5167 *
5168 *	Return a Tcl_Obj holding the value of a style layout option
5169 *	for an element.
5170 *
5171 * Results:
5172 *	Pointer to a new Tcl_Obj or NULL if the option has no value.
5173 *
5174 * Side effects:
5175 *	A Tcl_Obj may be allocated.
5176 *
5177 *----------------------------------------------------------------------
5178 */
5179
5180static Tcl_Obj *
5181LayoutOptionToObj(
5182    TreeCtrl *tree,		/* Widget info. */
5183    MStyle *style,		/* Master style using the element. */
5184    MElementLink *eLink,	/* Layout info for the element. */
5185    int option			/* OPTION_xxx constant. */
5186    )
5187{
5188    Tcl_Interp *interp = tree->interp;
5189
5190    switch (option) {
5191	case OPTION_PADX:
5192	    return TreeCtrl_NewPadAmountObj(eLink->ePadX);
5193	case OPTION_PADY:
5194	    return TreeCtrl_NewPadAmountObj(eLink->ePadY);
5195	case OPTION_iPADX:
5196	    return TreeCtrl_NewPadAmountObj(eLink->iPadX);
5197	case OPTION_iPADY:
5198	    return TreeCtrl_NewPadAmountObj(eLink->iPadY);
5199	case OPTION_DETACH:
5200	    return Tcl_NewStringObj((eLink->flags & ELF_DETACH) ? "yes" : "no", -1);
5201	case OPTION_EXPAND: {
5202	    char flags[4];
5203	    int n = 0;
5204
5205	    if (eLink->flags & ELF_eEXPAND_W) flags[n++] = 'w';
5206	    if (eLink->flags & ELF_eEXPAND_N) flags[n++] = 'n';
5207	    if (eLink->flags & ELF_eEXPAND_E) flags[n++] = 'e';
5208	    if (eLink->flags & ELF_eEXPAND_S) flags[n++] = 's';
5209	    if (n)
5210		return Tcl_NewStringObj(flags, n);
5211	    break;
5212	}
5213	case OPTION_iEXPAND: {
5214	    char flags[6];
5215	    int n = 0;
5216
5217	    if (eLink->flags & ELF_iEXPAND_X) flags[n++] = 'x';
5218	    if (eLink->flags & ELF_iEXPAND_Y) flags[n++] = 'y';
5219	    if (eLink->flags & ELF_iEXPAND_W) flags[n++] = 'w';
5220	    if (eLink->flags & ELF_iEXPAND_N) flags[n++] = 'n';
5221	    if (eLink->flags & ELF_iEXPAND_E) flags[n++] = 'e';
5222	    if (eLink->flags & ELF_iEXPAND_S) flags[n++] = 's';
5223	    if (n)
5224		return Tcl_NewStringObj(flags, n);
5225	    break;
5226	}
5227	case OPTION_INDENT:
5228	    return Tcl_NewStringObj((eLink->flags & ELF_INDENT) ? "yes" : "no", -1);
5229	case OPTION_SQUEEZE: {
5230	    char flags[2];
5231	    int n = 0;
5232
5233	    if (eLink->flags & ELF_SQUEEZE_X) flags[n++] = 'x';
5234	    if (eLink->flags & ELF_SQUEEZE_Y) flags[n++] = 'y';
5235	    if (n)
5236		return Tcl_NewStringObj(flags, n);
5237	    break;
5238	}
5239	case OPTION_UNION: {
5240	    int i;
5241	    Tcl_Obj *objPtr;
5242
5243	    if (eLink->onionCount == 0)
5244		break;
5245	    objPtr = Tcl_NewListObj(0, NULL);
5246	    for (i = 0; i < eLink->onionCount; i++)
5247		Tcl_ListObjAppendElement(interp, objPtr,
5248		    Element_ToObj(style->elements[eLink->onion[i]].elem));
5249	    return objPtr;
5250	}
5251	case OPTION_MAXHEIGHT: {
5252	    if (eLink->maxHeight >= 0)
5253		return Tcl_NewIntObj(eLink->maxHeight);
5254	    break;
5255	}
5256	case OPTION_MINHEIGHT: {
5257	    if (eLink->minHeight >= 0)
5258		return Tcl_NewIntObj(eLink->minHeight);
5259	    break;
5260	}
5261	case OPTION_HEIGHT: {
5262	    if (eLink->fixedHeight >= 0)
5263		return Tcl_NewIntObj(eLink->fixedHeight);
5264	    break;
5265	}
5266	case OPTION_MAXWIDTH: {
5267	    if (eLink->maxWidth >= 0)
5268		return Tcl_NewIntObj(eLink->maxWidth);
5269	    break;
5270	}
5271	case OPTION_MINWIDTH: {
5272	    if (eLink->minWidth >= 0)
5273		return Tcl_NewIntObj(eLink->minWidth);
5274	    break;
5275	}
5276	case OPTION_WIDTH: {
5277	    if (eLink->fixedWidth >= 0)
5278		return Tcl_NewIntObj(eLink->fixedWidth);
5279	    break;
5280	}
5281	case OPTION_STICKY: {
5282	    char flags[4];
5283	    int n = 0;
5284
5285	    if (eLink->flags & ELF_STICKY_W) flags[n++] = 'w';
5286	    if (eLink->flags & ELF_STICKY_N) flags[n++] = 'n';
5287	    if (eLink->flags & ELF_STICKY_E) flags[n++] = 'e';
5288	    if (eLink->flags & ELF_STICKY_S) flags[n++] = 's';
5289	    if (n)
5290		return Tcl_NewStringObj(flags, n);
5291	    break;
5292	}
5293	case OPTION_DRAW: {
5294	    return eLink->draw.obj;
5295	}
5296	case OPTION_VISIBLE: {
5297	    return eLink->visible.obj;
5298	}
5299    }
5300    return NULL;
5301}
5302
5303/*
5304 *----------------------------------------------------------------------
5305 *
5306 * StyleLayoutCmd --
5307 *
5308 *	This procedure is invoked to process the [style layout]
5309 *	widget command.  See the user documentation for details on what
5310 *	it does.
5311 *
5312 * Results:
5313 *	A standard Tcl result.
5314 *
5315 * Side effects:
5316 *	See the user documentation.
5317 *
5318 *----------------------------------------------------------------------
5319 */
5320
5321static int
5322StyleLayoutCmd(
5323    ClientData clientData,	/* Widget info. */
5324    Tcl_Interp *interp,		/* The current interpreter. */
5325    int objc,			/* Number of arguments. */
5326    Tcl_Obj *CONST objv[]	/* Argument values. */
5327    )
5328{
5329    TreeCtrl *tree = clientData;
5330    TreeStyle _style;
5331    MStyle *style;
5332    TreeElement elem;
5333    MElementLink saved, *eLink;
5334    int i, index;
5335    static CONST char *optionNames[] = {
5336	"-detach", "-draw", "-expand", "-height", "-iexpand",
5337	"-indent", "-ipadx", "-ipady", "-maxheight", "-maxwidth", "-minheight",
5338	"-minwidth", "-padx", "-pady", "-squeeze", "-sticky", "-union",
5339	"-width", "-visible",
5340	(char *) NULL
5341    };
5342    if (objc < 5) {
5343	Tcl_WrongNumArgs(interp, 3, objv, "name element ?option? ?value? ?option value ...?");
5344	return TCL_ERROR;
5345    }
5346
5347    if (TreeStyle_FromObj(tree, objv[3], &_style) != TCL_OK)
5348	return TCL_ERROR;
5349    style = (MStyle *) _style;
5350
5351    if (Element_FromObj(tree, objv[4], &elem) != TCL_OK)
5352	return TCL_ERROR;
5353
5354    eLink = MStyle_FindElem(tree, style, elem, NULL);
5355    if (eLink == NULL) {
5356	FormatResult(interp, "style %s does not use element %s",
5357	    style->name, elem->name);
5358	return TCL_ERROR;
5359    }
5360
5361    /* T style layout S E */
5362    if (objc == 5) {
5363	Tcl_Obj *listObj = Tcl_NewListObj(0, NULL);
5364	Tcl_Obj *objPtr;
5365
5366	for (i = 0; optionNames[i] != NULL; i++) {
5367	    Tcl_ListObjAppendElement(interp, listObj,
5368		Tcl_NewStringObj(optionNames[i], -1));
5369	    objPtr = LayoutOptionToObj(tree, style, eLink, i);
5370	    Tcl_ListObjAppendElement(interp, listObj,
5371		objPtr ? objPtr : Tcl_NewObj());
5372	}
5373	Tcl_SetObjResult(interp, listObj);
5374	return TCL_OK;
5375    }
5376
5377    /* T style layout S E option */
5378    if (objc == 6) {
5379	Tcl_Obj *objPtr;
5380
5381	if (Tcl_GetIndexFromObj(interp, objv[5], optionNames, "option",
5382	    0, &index) != TCL_OK)
5383	    return TCL_ERROR;
5384	objPtr = LayoutOptionToObj(tree, style, eLink, index);
5385	if (objPtr != NULL)
5386	    Tcl_SetObjResult(interp, objPtr);
5387	return TCL_OK;
5388    }
5389
5390    saved = *eLink;
5391
5392    for (i = 5; i < objc; i += 2) {
5393	if (i + 2 > objc) {
5394	    FormatResult(interp, "value for \"%s\" missing",
5395		Tcl_GetString(objv[i]));
5396	    goto badConfig;
5397	}
5398	if (Tcl_GetIndexFromObj(interp, objv[i], optionNames, "option",
5399	    0, &index) != TCL_OK) {
5400	    goto badConfig;
5401	}
5402	switch (index) {
5403	    case OPTION_PADX: {
5404		if (TreeCtrl_GetPadAmountFromObj(interp,
5405		    tree->tkwin, objv[i + 1],
5406		    &eLink->ePadX[PAD_TOP_LEFT],
5407		    &eLink->ePadX[PAD_BOTTOM_RIGHT]) != TCL_OK)
5408		    goto badConfig;
5409		break;
5410	    }
5411	    case OPTION_PADY: {
5412		if (TreeCtrl_GetPadAmountFromObj(interp,
5413		    tree->tkwin, objv[i + 1],
5414		    &eLink->ePadY[PAD_TOP_LEFT],
5415		    &eLink->ePadY[PAD_BOTTOM_RIGHT]) != TCL_OK)
5416		    goto badConfig;
5417		break;
5418	    }
5419	    case OPTION_iPADX: {
5420		if (TreeCtrl_GetPadAmountFromObj(interp,
5421		    tree->tkwin, objv[i + 1],
5422		    &eLink->iPadX[PAD_TOP_LEFT],
5423		    &eLink->iPadX[PAD_BOTTOM_RIGHT]) != TCL_OK)
5424		    goto badConfig;
5425		break;
5426	    }
5427	    case OPTION_iPADY: {
5428		if (TreeCtrl_GetPadAmountFromObj(interp,
5429		    tree->tkwin, objv[i + 1],
5430		    &eLink->iPadY[PAD_TOP_LEFT],
5431		    &eLink->iPadY[PAD_BOTTOM_RIGHT]) != TCL_OK)
5432		    goto badConfig;
5433		break;
5434	    }
5435	    case OPTION_DETACH: {
5436		int detach;
5437		if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &detach) != TCL_OK)
5438		    goto badConfig;
5439		if (detach)
5440		    eLink->flags |= ELF_DETACH;
5441		else
5442		    eLink->flags &= ~ELF_DETACH;
5443		break;
5444	    }
5445	    case OPTION_EXPAND: {
5446		char *expand;
5447		int len, k;
5448		expand = Tcl_GetStringFromObj(objv[i + 1], &len);
5449		eLink->flags &= ~ELF_eEXPAND;
5450		for (k = 0; k < len; k++) {
5451		    switch (expand[k]) {
5452			case 'w': case 'W': eLink->flags |= ELF_eEXPAND_W; break;
5453			case 'n': case 'N': eLink->flags |= ELF_eEXPAND_N; break;
5454			case 'e': case 'E': eLink->flags |= ELF_eEXPAND_E; break;
5455			case 's': case 'S': eLink->flags |= ELF_eEXPAND_S; break;
5456			default: {
5457			    Tcl_ResetResult(tree->interp);
5458			    Tcl_AppendResult(tree->interp, "bad expand value \"",
5459				expand, "\": must be a string ",
5460				"containing zero or more of n, e, s, and w",
5461				(char *) NULL);
5462			    goto badConfig;
5463			}
5464		    }
5465		}
5466		break;
5467	    }
5468	    case OPTION_iEXPAND: {
5469		char *expand;
5470		int len, k;
5471		expand = Tcl_GetStringFromObj(objv[i + 1], &len);
5472		eLink->flags &= ~(ELF_iEXPAND | ELF_iEXPAND_X | ELF_iEXPAND_Y);
5473		for (k = 0; k < len; k++) {
5474		    switch (expand[k]) {
5475			case 'x': case 'X': eLink->flags |= ELF_iEXPAND_X; break;
5476			case 'y': case 'Y': eLink->flags |= ELF_iEXPAND_Y; break;
5477			case 'w': case 'W': eLink->flags |= ELF_iEXPAND_W; break;
5478			case 'n': case 'N': eLink->flags |= ELF_iEXPAND_N; break;
5479			case 'e': case 'E': eLink->flags |= ELF_iEXPAND_E; break;
5480			case 's': case 'S': eLink->flags |= ELF_iEXPAND_S; break;
5481			default: {
5482			    Tcl_ResetResult(tree->interp);
5483			    Tcl_AppendResult(tree->interp, "bad iexpand value \"",
5484				expand, "\": must be a string ",
5485				"containing zero or more of x, y, n, e, s, and w",
5486				(char *) NULL);
5487			    goto badConfig;
5488			}
5489		    }
5490		}
5491		break;
5492	    }
5493	    case OPTION_INDENT: {
5494		int indent;
5495		if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &indent) != TCL_OK)
5496		    goto badConfig;
5497		if (indent)
5498		    eLink->flags |= ELF_INDENT;
5499		else
5500		    eLink->flags &= ~ELF_INDENT;
5501		break;
5502	    }
5503	    case OPTION_SQUEEZE: {
5504		char *string;
5505		int len, k;
5506		string = Tcl_GetStringFromObj(objv[i + 1], &len);
5507		eLink->flags &= ~(ELF_SQUEEZE_X | ELF_SQUEEZE_Y);
5508		for (k = 0; k < len; k++) {
5509		    switch (string[k]) {
5510			case 'x': case 'X': eLink->flags |= ELF_SQUEEZE_X; break;
5511			case 'y': case 'Y': eLink->flags |= ELF_SQUEEZE_Y; break;
5512			default: {
5513			    Tcl_ResetResult(tree->interp);
5514			    Tcl_AppendResult(tree->interp, "bad squeeze value \"",
5515				string, "\": must be a string ",
5516				"containing zero or more of x and y",
5517				(char *) NULL);
5518			    goto badConfig;
5519			}
5520		    }
5521		}
5522		break;
5523	    }
5524	    case OPTION_UNION: {
5525		int objc1;
5526		Tcl_Obj **objv1;
5527		int j, k, n, *onion, count = 0;
5528
5529		if (Tcl_ListObjGetElements(interp, objv[i + 1],
5530		    &objc1, &objv1) != TCL_OK)
5531		    goto badConfig;
5532		if (objc1 == 0) {
5533		    if (eLink->onion != NULL) {
5534			if (eLink->onion != saved.onion)
5535			    WCFREE(eLink->onion, int, eLink->onionCount);
5536			eLink->onionCount = 0;
5537			eLink->onion = NULL;
5538		    }
5539		    break;
5540		}
5541		onion = (int *) ckalloc(sizeof(int) * objc1);
5542		for (j = 0; j < objc1; j++) {
5543		    TreeElement elem2;
5544		    MElementLink *eLink2;
5545
5546		    if (Element_FromObj(tree, objv1[j], &elem2) != TCL_OK) {
5547			ckfree((char *) onion);
5548			goto badConfig;
5549		    }
5550
5551		    eLink2 = MStyle_FindElem(tree, style, elem2, &n);
5552		    if (eLink2 == NULL) {
5553			ckfree((char *) onion);
5554			FormatResult(interp,
5555			    "style %s does not use element %s",
5556			    style->name, elem2->name);
5557			goto badConfig;
5558		    }
5559		    if (eLink == eLink2) {
5560			ckfree((char *) onion);
5561			FormatResult(interp,
5562			    "element %s can't form union with itself",
5563			    elem2->name);
5564			goto badConfig;
5565		    }
5566		    /* Silently ignore duplicates */
5567		    for (k = 0; k < count; k++) {
5568			if (onion[k] == n)
5569			    break;
5570		    }
5571		    if (k < count)
5572			continue;
5573		    onion[count++] = n;
5574		}
5575		if ((eLink->onion != NULL) && (eLink->onion != saved.onion))
5576		    WCFREE(eLink->onion, int, eLink->onionCount);
5577		if (count == objc1)
5578		    eLink->onion = onion;
5579		else {
5580		    eLink->onion = (int *) ckalloc(sizeof(int) * count);
5581		    for (k = 0; k < count; k++)
5582			eLink->onion[k] = onion[k];
5583		    ckfree((char *) onion);
5584		}
5585		eLink->onionCount = count;
5586		break;
5587	    }
5588	    case OPTION_MAXHEIGHT: {
5589		int height;
5590		if (ObjectIsEmpty(objv[i + 1])) {
5591		    eLink->maxHeight = -1;
5592		    break;
5593		}
5594		if ((Tk_GetPixelsFromObj(interp, tree->tkwin, objv[i + 1],
5595		    &height) != TCL_OK) || (height < 0)) {
5596		    FormatResult(interp, "bad screen distance \"%s\"",
5597			Tcl_GetString(objv[i + 1]));
5598		    goto badConfig;
5599		}
5600		eLink->maxHeight = height;
5601		break;
5602	    }
5603	    case OPTION_MINHEIGHT: {
5604		int height;
5605		if (ObjectIsEmpty(objv[i + 1])) {
5606		    eLink->minHeight = -1;
5607		    break;
5608		}
5609		if ((Tk_GetPixelsFromObj(interp, tree->tkwin, objv[i + 1],
5610		    &height) != TCL_OK) || (height < 0)) {
5611		    FormatResult(interp, "bad screen distance \"%s\"",
5612			Tcl_GetString(objv[i + 1]));
5613		    goto badConfig;
5614		}
5615		eLink->minHeight = height;
5616		break;
5617	    }
5618	    case OPTION_HEIGHT: {
5619		int height;
5620		if (ObjectIsEmpty(objv[i + 1])) {
5621		    eLink->fixedHeight = -1;
5622		    break;
5623		}
5624		if ((Tk_GetPixelsFromObj(interp, tree->tkwin, objv[i + 1],
5625			&height) != TCL_OK) || (height < 0)) {
5626		    FormatResult(interp, "bad screen distance \"%s\"",
5627			Tcl_GetString(objv[i + 1]));
5628		    goto badConfig;
5629		}
5630		eLink->fixedHeight = height;
5631		break;
5632	    }
5633	    case OPTION_MAXWIDTH: {
5634		int width;
5635		if (ObjectIsEmpty(objv[i + 1])) {
5636		    eLink->maxWidth = -1;
5637		    break;
5638		}
5639		if ((Tk_GetPixelsFromObj(interp, tree->tkwin, objv[i + 1],
5640		    &width) != TCL_OK) || (width < 0)) {
5641		    FormatResult(interp, "bad screen distance \"%s\"",
5642			Tcl_GetString(objv[i + 1]));
5643		    goto badConfig;
5644		}
5645		eLink->maxWidth = width;
5646		break;
5647	    }
5648	    case OPTION_MINWIDTH: {
5649		int width;
5650		if (ObjectIsEmpty(objv[i + 1])) {
5651		    eLink->minWidth = -1;
5652		    break;
5653		}
5654		if ((Tk_GetPixelsFromObj(interp, tree->tkwin, objv[i + 1],
5655			&width) != TCL_OK) || (width < 0)) {
5656		    FormatResult(interp, "bad screen distance \"%s\"",
5657			Tcl_GetString(objv[i + 1]));
5658		    goto badConfig;
5659		}
5660		eLink->minWidth = width;
5661		break;
5662	    }
5663	    case OPTION_WIDTH: {
5664		int width;
5665		if (ObjectIsEmpty(objv[i + 1])) {
5666		    eLink->fixedWidth = -1;
5667		    break;
5668		}
5669		if ((Tk_GetPixelsFromObj(interp, tree->tkwin, objv[i + 1],
5670			&width) != TCL_OK) || (width < 0)) {
5671		    FormatResult(interp, "bad screen distance \"%s\"",
5672			Tcl_GetString(objv[i + 1]));
5673		    goto badConfig;
5674		}
5675		eLink->fixedWidth = width;
5676		break;
5677	    }
5678	    case OPTION_STICKY: {
5679		char *sticky;
5680		int len, k;
5681		sticky = Tcl_GetStringFromObj(objv[i + 1], &len);
5682		eLink->flags &= ~ELF_STICKY;
5683		for (k = 0; k < len; k++) {
5684		    switch (sticky[k]) {
5685			case 'w': case 'W': eLink->flags |= ELF_STICKY_W; break;
5686			case 'n': case 'N': eLink->flags |= ELF_STICKY_N; break;
5687			case 'e': case 'E': eLink->flags |= ELF_STICKY_E; break;
5688			case 's': case 'S': eLink->flags |= ELF_STICKY_S; break;
5689			default: {
5690			    Tcl_ResetResult(tree->interp);
5691			    Tcl_AppendResult(tree->interp, "bad sticky value \"",
5692				sticky, "\": must be a string ",
5693				"containing zero or more of n, e, s, and w",
5694				(char *) NULL);
5695			    goto badConfig;
5696			}
5697		    }
5698		}
5699		break;
5700	    }
5701	    case OPTION_DRAW:
5702	    case OPTION_VISIBLE: {
5703		PerStateInfo *psi, *psiSaved;
5704
5705		if (index == OPTION_DRAW) {
5706		    psi = &eLink->draw;
5707		    psiSaved = &saved.draw;
5708		} else {
5709		    psi = &eLink->visible;
5710		    psiSaved = &saved.visible;
5711		}
5712
5713		/* Already configured this once. */
5714		if (psi->obj != NULL && psi->obj != psiSaved->obj) {
5715		    PerStateInfo_Free(tree, &pstBoolean, psi);
5716		    Tcl_DecrRefCount(psi->obj);
5717
5718		/* First configure. Don't free the saved data. */
5719		} else {
5720		    psi->data = NULL;
5721		    psi->count = 0;
5722		}
5723		psi->obj = objv[i + 1];
5724		Tcl_IncrRefCount(psi->obj);
5725		if (PerStateInfo_FromObj(tree, TreeStateFromObj, &pstBoolean,
5726			psi) != TCL_OK) {
5727		    goto badConfig;
5728		}
5729		break;
5730	    }
5731	}
5732    }
5733    if (saved.onion && (eLink->onion != saved.onion))
5734	WCFREE(saved.onion, int, saved.onionCount);
5735    if (saved.draw.obj != NULL &&
5736	    saved.draw.obj != eLink->draw.obj) {
5737	PerStateInfo_Free(tree, &pstBoolean, &saved.draw);
5738	Tcl_DecrRefCount(saved.draw.obj);
5739    }
5740    if (saved.visible.obj != NULL &&
5741	    saved.visible.obj != eLink->visible.obj) {
5742	PerStateInfo_Free(tree, &pstBoolean, &saved.visible);
5743	Tcl_DecrRefCount(saved.visible.obj);
5744    }
5745    Style_Changed(tree, style);
5746    return TCL_OK;
5747
5748badConfig:
5749    if (eLink->onion && (eLink->onion != saved.onion))
5750	WCFREE(eLink->onion, int, eLink->onionCount);
5751    if (eLink->draw.obj != NULL &&
5752	    eLink->draw.obj != saved.draw.obj) {
5753	PerStateInfo_Free(tree, &pstBoolean, &eLink->draw);
5754	Tcl_DecrRefCount(eLink->draw.obj);
5755    }
5756    if (eLink->visible.obj != NULL &&
5757	    eLink->visible.obj != saved.visible.obj) {
5758	PerStateInfo_Free(tree, &pstBoolean, &eLink->visible);
5759	Tcl_DecrRefCount(eLink->visible.obj);
5760    }
5761    *eLink = saved;
5762    return TCL_ERROR;
5763}
5764
5765/*
5766 *----------------------------------------------------------------------
5767 *
5768 * TreeStyleCmd --
5769 *
5770 *	This procedure is invoked to process the [style] widget
5771 *	command.  See the user documentation for details on what it
5772 *	does.
5773 *
5774 * Results:
5775 *	A standard Tcl result.
5776 *
5777 * Side effects:
5778 *	See the user documentation.
5779 *
5780 *----------------------------------------------------------------------
5781 */
5782
5783int
5784TreeStyleCmd(
5785    ClientData clientData,	/* Widget info. */
5786    Tcl_Interp *interp,		/* Current interpreter. */
5787    int objc,			/* Number of arguments. */
5788    Tcl_Obj *CONST objv[]	/* Argument values. */
5789    )
5790{
5791    TreeCtrl *tree = clientData;
5792    static CONST char *commandNames[] = {
5793	"cget", "configure", "create", "delete", "elements", "layout",
5794	"names", (char *) NULL };
5795    enum {
5796	COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_CREATE, COMMAND_DELETE,
5797	COMMAND_ELEMENTS, COMMAND_LAYOUT, COMMAND_NAMES };
5798    int index;
5799    TreeStyle _style;
5800    MStyle *style;
5801
5802    if (objc < 3) {
5803	Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg ...?");
5804	return TCL_ERROR;
5805    }
5806
5807    if (Tcl_GetIndexFromObj(interp, objv[2], commandNames, "command", 0,
5808	&index) != TCL_OK) {
5809	return TCL_ERROR;
5810    }
5811
5812    switch (index) {
5813	case COMMAND_CGET: {
5814	    Tcl_Obj *resultObjPtr;
5815
5816	    if (objc != 5) {
5817		Tcl_WrongNumArgs(interp, 3, objv, "name option");
5818		return TCL_ERROR;
5819	    }
5820	    if (TreeStyle_FromObj(tree, objv[3], &_style) != TCL_OK)
5821		return TCL_ERROR;
5822	    style = (MStyle *) _style;
5823	    resultObjPtr = Tk_GetOptionValue(interp, (char *) style,
5824		tree->styleOptionTable, objv[4], tree->tkwin);
5825	    if (resultObjPtr == NULL)
5826		return TCL_ERROR;
5827	    Tcl_SetObjResult(interp, resultObjPtr);
5828	    break;
5829	}
5830
5831	case COMMAND_CONFIGURE: {
5832	    Tcl_Obj *resultObjPtr = NULL;
5833
5834	    if (objc < 4) {
5835		Tcl_WrongNumArgs(interp, 3, objv, "name ?option? ?value option value ...?");
5836		return TCL_ERROR;
5837	    }
5838	    if (TreeStyle_FromObj(tree, objv[3], &_style) != TCL_OK)
5839		return TCL_ERROR;
5840	    style = (MStyle *) _style;
5841	    if (objc <= 5) {
5842		resultObjPtr = Tk_GetOptionInfo(interp, (char *) style,
5843		    tree->styleOptionTable,
5844		    (objc == 4) ? (Tcl_Obj *) NULL : objv[4],
5845		    tree->tkwin);
5846		if (resultObjPtr == NULL)
5847		    return TCL_ERROR;
5848		Tcl_SetObjResult(interp, resultObjPtr);
5849	    } else {
5850		if (Tk_SetOptions(tree->interp, (char *) style,
5851		    tree->styleOptionTable, objc - 4, objv + 4, tree->tkwin,
5852		    NULL, NULL) != TCL_OK)
5853		    return TCL_ERROR;
5854		Style_Changed(tree, style);
5855	    }
5856	    break;
5857	}
5858
5859	case COMMAND_CREATE: {
5860	    char *name;
5861	    int len;
5862	    Tcl_HashEntry *hPtr;
5863	    int isNew;
5864
5865	    if (objc < 4) {
5866		Tcl_WrongNumArgs(interp, 3, objv, "name ?option value ...?");
5867		return TCL_ERROR;
5868	    }
5869	    name = Tcl_GetStringFromObj(objv[3], &len);
5870	    if (!len) {
5871		FormatResult(interp, "invalid style name \"\"");
5872		return TCL_ERROR;
5873	    }
5874	    hPtr = Tcl_FindHashEntry(&tree->styleHash, name);
5875	    if (hPtr != NULL) {
5876		FormatResult(interp, "style \"%s\" already exists", name);
5877		return TCL_ERROR;
5878	    }
5879	    style = Style_CreateAndConfig(tree, name, objc - 4, objv + 4);
5880	    if (style == NULL)
5881		return TCL_ERROR;
5882	    hPtr = Tcl_CreateHashEntry(&tree->styleHash, name, &isNew);
5883	    Tcl_SetHashValue(hPtr, style);
5884	    Tcl_SetObjResult(interp, TreeStyle_ToObj((TreeStyle) style));
5885	    break;
5886	}
5887
5888	case COMMAND_DELETE: {
5889	    int i;
5890
5891	    if (objc < 3) {
5892		Tcl_WrongNumArgs(interp, 3, objv, "?name ...?");
5893		return TCL_ERROR;
5894	    }
5895	    for (i = 3; i < objc; i++) {
5896		if (TreeStyle_FromObj(tree, objv[i], &_style) != TCL_OK)
5897		    return TCL_ERROR;
5898		Style_Deleted(tree, (MStyle *) _style);
5899		TreeStyle_FreeResources(tree, _style);
5900	    }
5901	    break;
5902	}
5903
5904	/* T style elements S ?{E ...}? */
5905	case COMMAND_ELEMENTS: {
5906	    TreeElement elem, *elemList = NULL;
5907	    int i, j, count = 0;
5908	    int staticMap[STATIC_SIZE], *map = staticMap;
5909	    int listObjc;
5910	    Tcl_Obj **listObjv;
5911
5912	    if (objc < 4 || objc > 5) {
5913		Tcl_WrongNumArgs(interp, 3, objv, "name ?elementList?");
5914		return TCL_ERROR;
5915	    }
5916	    if (TreeStyle_FromObj(tree, objv[3], &_style) != TCL_OK)
5917		return TCL_ERROR;
5918	    style = (MStyle *) _style;
5919	    if (objc == 5) {
5920		if (Tcl_ListObjGetElements(interp, objv[4], &listObjc, &listObjv) != TCL_OK)
5921		    return TCL_ERROR;
5922		if (listObjc > 0)
5923		    elemList = (TreeElement *) ckalloc(sizeof(TreeElement_) * listObjc);
5924		for (i = 0; i < listObjc; i++) {
5925		    if (Element_FromObj(tree, listObjv[i], &elem) != TCL_OK) {
5926			ckfree((char *) elemList);
5927			return TCL_ERROR;
5928		    }
5929
5930		    /* Ignore duplicate elements */
5931		    for (j = 0; j < count; j++) {
5932			if (elemList[j] == elem)
5933			    break;
5934		    }
5935		    if (j < count)
5936			continue;
5937
5938		    elemList[count++] = elem;
5939		}
5940
5941		STATIC_ALLOC(map, int, count);
5942
5943		for (i = 0; i < count; i++)
5944		    map[i] = -1;
5945
5946		/* Reassigning Elements to a Style */
5947		if (style->numElements > 0) {
5948		    /* Check each Element */
5949		    for (i = 0; i < count; i++) {
5950			/* See if this Element is already used by the Style */
5951			for (j = 0; j < style->numElements; j++) {
5952			    if (elemList[i] == style->elements[j].elem) {
5953				/* Preserve it */
5954				map[i] = j;
5955				break;
5956			    }
5957			}
5958		    }
5959		}
5960		Style_ChangeElements(tree, style, count, elemList, map);
5961		if (elemList != NULL)
5962		    ckfree((char *) elemList);
5963		STATIC_FREE(map, int, count);
5964		break;
5965	    }
5966	    TreeStyle_ListElements(tree, (TreeStyle) style);
5967	    break;
5968	}
5969
5970	/* T style layout S E ?option? ?value? ?option value ...? */
5971	case COMMAND_LAYOUT: {
5972	    return StyleLayoutCmd(clientData, interp, objc, objv);
5973	}
5974
5975	case COMMAND_NAMES: {
5976	    Tcl_Obj *listObj;
5977	    Tcl_HashSearch search;
5978	    Tcl_HashEntry *hPtr;
5979
5980	    listObj = Tcl_NewListObj(0, NULL);
5981	    hPtr = Tcl_FirstHashEntry(&tree->styleHash, &search);
5982	    while (hPtr != NULL) {
5983		_style = (TreeStyle) Tcl_GetHashValue(hPtr);
5984		Tcl_ListObjAppendElement(interp, listObj,
5985		    TreeStyle_ToObj(_style));
5986		hPtr = Tcl_NextHashEntry(&search);
5987	    }
5988	    Tcl_SetObjResult(interp, listObj);
5989	    break;
5990	}
5991    }
5992    return TCL_OK;
5993}
5994
5995/*
5996 *----------------------------------------------------------------------
5997 *
5998 * Tree_ButtonMaxWidth --
5999 *
6000 *	Return the maximum possible size of a button in any state. This
6001 *	includes the size of the -buttonimage and -buttonbitmap options,
6002 *	as well as the theme button and default +/- button.
6003 *
6004 * Results:
6005 *	Pixel size >= 0.
6006 *
6007 * Side effects:
6008 *	None.
6009 *
6010 *----------------------------------------------------------------------
6011 */
6012
6013int
6014Tree_ButtonMaxWidth(
6015    TreeCtrl *tree		/* Widget info. */
6016    )
6017{
6018    int w, h, width = 0;
6019
6020    PerStateImage_MaxSize(tree, &tree->buttonImage, &w, &h);
6021    width = MAX(width, w);
6022
6023    PerStateBitmap_MaxSize(tree, &tree->buttonBitmap, &w, &h);
6024    width = MAX(width, w);
6025
6026    if (tree->useTheme) {
6027	if (TreeTheme_GetButtonSize(tree, Tk_WindowId(tree->tkwin),
6028	    TRUE, &w, &h) == TCL_OK)
6029	    width = MAX(width, w);
6030	if (TreeTheme_GetButtonSize(tree, Tk_WindowId(tree->tkwin),
6031	    FALSE, &w, &h) == TCL_OK)
6032	    width = MAX(width, w);
6033    }
6034
6035    return MAX(width, tree->buttonSize);
6036}
6037
6038/*
6039 *----------------------------------------------------------------------
6040 *
6041 * Tree_ButtonHeight --
6042 *
6043 *	Return the size of a button for a certain state.
6044 *
6045 * Results:
6046 *	Pixel size >= 0.
6047 *
6048 * Side effects:
6049 *	None.
6050 *
6051 *----------------------------------------------------------------------
6052 */
6053
6054int
6055Tree_ButtonHeight(
6056    TreeCtrl *tree,		/* Widget info. */
6057    int state			/* STATE_xxx flags. */
6058    )
6059{
6060    Tk_Image image;
6061    Pixmap bitmap;
6062    int w, h;
6063
6064    image = PerStateImage_ForState(tree, &tree->buttonImage, state, NULL);
6065    if (image != NULL) {
6066        Tk_SizeOfImage(image, &w, &h);
6067        return h;
6068    }
6069
6070    bitmap = PerStateBitmap_ForState(tree, &tree->buttonBitmap, state, NULL);
6071    if (bitmap != None) {
6072	Tk_SizeOfBitmap(tree->display, bitmap, &w, &h);
6073	return h;
6074    }
6075
6076    if (tree->useTheme &&
6077	TreeTheme_GetButtonSize(tree, Tk_WindowId(tree->tkwin),
6078	    (state & STATE_OPEN) != 0, &w, &h) == TCL_OK)
6079	return h;
6080
6081    return tree->buttonSize;
6082}
6083
6084/*
6085 *----------------------------------------------------------------------
6086 *
6087 * TreeStyle_Identify --
6088 *
6089 *	Perform hit-testing on a style.
6090 *
6091 * Results:
6092 *	The name of the element containing the given point, or NULL.
6093 *
6094 * Side effects:
6095 *	None.
6096 *
6097 *----------------------------------------------------------------------
6098 */
6099
6100char *
6101TreeStyle_Identify(
6102    StyleDrawArgs *drawArgs,	/* Various args. */
6103    int x,			/* Window x-coord to hit-test against. */
6104    int y			/* Window y-coord to hit-test against. */
6105    )
6106{
6107    TreeCtrl *tree = drawArgs->tree;
6108    IStyle *style = (IStyle *) drawArgs->style;
6109    MStyle *masterStyle = style->master;
6110    int state = drawArgs->state;
6111    IElementLink *eLink = NULL;
6112    int i, minWidth, minHeight;
6113    struct Layout staticLayouts[STATIC_SIZE], *layouts = staticLayouts;
6114
6115    Style_CheckNeededSize(tree, style, state);
6116#ifdef CACHE_STYLE_SIZE
6117    minWidth = style->minWidth;
6118    minHeight = style->minHeight;
6119#else
6120    Style_MinSize(tree, style, state, &minWidth, &minHeight);
6121#endif
6122
6123    if (drawArgs->width < minWidth + drawArgs->indent)
6124	drawArgs->width = minWidth + drawArgs->indent;
6125    if (drawArgs->height < minHeight)
6126	drawArgs->height = minHeight;
6127
6128    x -= drawArgs->x;
6129
6130    STATIC_ALLOC(layouts, struct Layout, masterStyle->numElements);
6131
6132    Style_DoLayout(drawArgs, layouts, FALSE, __FILE__, __LINE__);
6133
6134    for (i = style->master->numElements - 1; i >= 0; i--) {
6135	struct Layout *layout = &layouts[i];
6136
6137	if (IS_HIDDEN(layout))
6138	    continue;
6139
6140	eLink = layout->eLink;
6141	if ((x >= layout->x + layout->ePadX[PAD_TOP_LEFT]) && (x < layout->x + layout->ePadX[PAD_TOP_LEFT] + layout->iWidth) &&
6142	    (y >= layout->y + layout->ePadY[PAD_TOP_LEFT]) && (y < layout->y + layout->ePadY[PAD_TOP_LEFT] + layout->iHeight)) {
6143	    goto done;
6144	}
6145    }
6146    eLink = NULL;
6147done:
6148    STATIC_FREE(layouts, struct Layout, masterStyle->numElements);
6149    if (eLink != NULL)
6150	return (char *) eLink->elem->name;
6151    return NULL;
6152}
6153
6154/*
6155 *----------------------------------------------------------------------
6156 *
6157 * TreeStyle_Identify2 --
6158 *
6159 *	Return a list of elements overlapping the given area.
6160 *
6161 * Results:
6162 *	The names of any elements overlapping the given area are
6163 *	appended to the supplied list.
6164 *
6165 * Side effects:
6166 *	Memory is allocated.
6167 *
6168 *----------------------------------------------------------------------
6169 */
6170
6171void
6172TreeStyle_Identify2(
6173    StyleDrawArgs *drawArgs,	/* Various args. */
6174    int x1, int y1,		/* Top-left of area to hit-test. */
6175    int x2, int y2,		/* Bottom-right of area to hit-test. */
6176    Tcl_Obj *listObj		/* Initialized list object to hold
6177				 * the result. */
6178    )
6179{
6180    TreeCtrl *tree = drawArgs->tree;
6181    IStyle *style = (IStyle *) drawArgs->style;
6182    MStyle *masterStyle = style->master;
6183    int state = drawArgs->state;
6184    IElementLink *eLink;
6185    int i, minWidth, minHeight;
6186    struct Layout staticLayouts[STATIC_SIZE], *layouts = staticLayouts;
6187
6188    Style_CheckNeededSize(tree, style, state);
6189#ifdef CACHE_STYLE_SIZE
6190    minWidth = style->minWidth;
6191    minHeight = style->minHeight;
6192#else
6193    Style_MinSize(tree, style, state, &minWidth, &minHeight);
6194#endif
6195
6196    if (drawArgs->width < minWidth + drawArgs->indent)
6197	drawArgs->width = minWidth + drawArgs->indent;
6198    if (drawArgs->height < minHeight)
6199	drawArgs->height = minHeight;
6200
6201    STATIC_ALLOC(layouts, struct Layout, masterStyle->numElements);
6202
6203    Style_DoLayout(drawArgs, layouts, FALSE, __FILE__, __LINE__);
6204
6205    for (i = style->master->numElements - 1; i >= 0; i--) {
6206	struct Layout *layout = &layouts[i];
6207
6208	if (IS_HIDDEN(layout))
6209	    continue;
6210
6211	eLink = layout->eLink;
6212	if ((drawArgs->x + layout->x + layout->ePadX[PAD_TOP_LEFT] < x2) &&
6213	    (drawArgs->x + layout->x + layout->ePadX[PAD_TOP_LEFT] + layout->iWidth > x1) &&
6214	    (drawArgs->y + layout->y + layout->ePadY[PAD_TOP_LEFT] < y2) &&
6215	    (drawArgs->y + layout->y + layout->ePadY[PAD_TOP_LEFT] + layout->iHeight > y1)) {
6216	    Tcl_ListObjAppendElement(drawArgs->tree->interp, listObj,
6217		Tcl_NewStringObj(eLink->elem->name, -1));
6218	}
6219    }
6220
6221    STATIC_FREE(layouts, struct Layout, masterStyle->numElements);
6222}
6223
6224/*
6225 *----------------------------------------------------------------------
6226 *
6227 * TreeStyle_Remap --
6228 *
6229 *	The guts of the [item style map] command.  See the user
6230 *	documentation for details on what it does.
6231 *
6232 * Results:
6233 *	A standard Tcl result.
6234 *
6235 * Side effects:
6236 *	See the user documentation.
6237 *
6238 *----------------------------------------------------------------------
6239 */
6240
6241int
6242TreeStyle_Remap(
6243    TreeCtrl *tree,		/* Widget info. */
6244    TreeStyle styleFrom_,	/* Current instance style. */
6245    TreeStyle styleTo_,		/* Master style to "convert" the current
6246				 * style to. */
6247    int objc,			/* Must be even number. */
6248    Tcl_Obj *CONST objv[]	/* Array of old-new element names. */
6249    )
6250{
6251    IStyle *styleFrom = (IStyle *) styleFrom_;
6252    MStyle *styleTo = (MStyle *) styleTo_;
6253    int i, indexFrom, indexTo;
6254    int staticMap[STATIC_SIZE], *map = staticMap;
6255    IElementLink *eLink;
6256    TreeElement elemFrom, elemTo;
6257    TreeElement staticElemMap[STATIC_SIZE], *elemMap = staticElemMap;
6258    int styleFromNumElements = styleFrom->master->numElements;
6259    int result = TCL_OK;
6260
6261    /* Must be instance */
6262    if ((styleFrom == NULL) || (styleFrom->master == NULL))
6263	return TCL_ERROR;
6264
6265    /* Must be master */
6266    if ((styleTo == NULL) || (styleTo->master != NULL))
6267	return TCL_ERROR;
6268
6269    /* Nothing to do */
6270    if (styleFrom->master == styleTo)
6271	return TCL_OK;
6272
6273    if (objc & 1)
6274	return TCL_ERROR;
6275
6276    STATIC_ALLOC(map, int, styleFromNumElements);
6277    STATIC_ALLOC(elemMap, TreeElement, styleFromNumElements);
6278
6279    for (i = 0; i < styleFromNumElements; i++)
6280	map[i] = -1;
6281
6282    for (i = 0; i < objc; i += 2) {
6283	/* Get the old-style element */
6284	if (Element_FromObj(tree, objv[i], &elemFrom) != TCL_OK) {
6285	    result = TCL_ERROR;
6286	    goto done;
6287	}
6288
6289	/* Verify the old style uses the element */
6290	if (MStyle_FindElem(tree, styleFrom->master, elemFrom, &indexFrom) == NULL) {
6291	    FormatResult(tree->interp, "style %s does not use element %s",
6292		styleFrom->master->name, elemFrom->name);
6293	    result = TCL_ERROR;
6294	    goto done;
6295	}
6296
6297	/* Get the new-style element */
6298	if (Element_FromObj(tree, objv[i + 1], &elemTo) != TCL_OK) {
6299	    result = TCL_ERROR;
6300	    goto done;
6301	}
6302
6303	/* Verify the new style uses the element */
6304	if (MStyle_FindElem(tree, styleTo, elemTo, &indexTo) == NULL) {
6305	    FormatResult(tree->interp, "style %s does not use element %s",
6306		styleTo->name, elemTo->name);
6307	    result = TCL_ERROR;
6308	    goto done;
6309	}
6310
6311	/* Must be the same type */
6312	if (elemFrom->typePtr != elemTo->typePtr) {
6313	    FormatResult(tree->interp, "can't map element type %s to %s",
6314		elemFrom->typePtr->name, elemTo->typePtr->name);
6315	    result = TCL_ERROR;
6316	    goto done;
6317	}
6318
6319	/* See if the instance style has any info for this element */
6320	eLink = &styleFrom->elements[indexFrom];
6321	if (eLink->elem->master != NULL) {
6322	    map[indexFrom] = indexTo;
6323	    elemMap[indexFrom] = eLink->elem;
6324	}
6325    }
6326
6327    for (i = 0; i < styleFromNumElements; i++) {
6328	eLink = &styleFrom->elements[i];
6329	indexTo = map[i];
6330
6331	/* Free info for any Elements not being remapped */
6332	if ((indexTo == -1) && (eLink->elem->master != NULL)) {
6333	    elemFrom = eLink->elem->master;
6334	    Element_FreeResources(tree, eLink->elem);
6335	    eLink->elem = elemFrom;
6336	}
6337
6338	/* Remap this Element */
6339	if (indexTo != -1) {
6340	    elemMap[i]->master = styleTo->elements[indexTo].elem;
6341	    elemMap[i]->name = styleTo->elements[indexTo].elem->name;
6342	}
6343    }
6344
6345    if (styleFromNumElements != styleTo->numElements) {
6346#ifdef ALLOC_HAX
6347	if (styleFromNumElements > 0)
6348	    TreeAlloc_CFree(tree->allocData, IElementLinkUid,
6349		(char *) styleFrom->elements, sizeof(IElementLink),
6350		styleFromNumElements, ELEMENT_LINK_ROUND);
6351	styleFrom->elements = (IElementLink *) TreeAlloc_CAlloc(tree->allocData,
6352	    IElementLinkUid, sizeof(IElementLink), styleTo->numElements,
6353	    ELEMENT_LINK_ROUND);
6354#else
6355	if (styleFromNumElements > 0)
6356	    WCFREE(styleFrom->elements, IElementLink, styleFromNumElements);
6357	styleFrom->elements = (IElementLink *) ckalloc(sizeof(IElementLink) *
6358	    styleTo->numElements);
6359#endif
6360	memset(styleFrom->elements, '\0', sizeof(IElementLink) * styleTo->numElements);
6361    }
6362    for (i = 0; i < styleTo->numElements; i++) {
6363	styleFrom->elements[i].elem = styleTo->elements[i].elem;
6364#ifdef CACHE_ELEM_SIZE
6365	styleFrom->elements[i].neededWidth = -1;
6366	styleFrom->elements[i].neededHeight = -1;
6367#endif
6368    }
6369    for (i = 0; i < styleFromNumElements; i++) {
6370	indexTo = map[i];
6371	if (indexTo != -1)
6372	    styleFrom->elements[indexTo].elem = elemMap[i];
6373    }
6374    styleFrom->master = styleTo;
6375    styleFrom->neededWidth = styleFrom->neededHeight = -1;
6376
6377done:
6378    STATIC_FREE(map, int, styleFromNumElements);
6379    STATIC_FREE(elemMap, TreeElement, styleFromNumElements);
6380    return result;
6381}
6382
6383/*
6384 *----------------------------------------------------------------------
6385 *
6386 * TreeStyle_GetSortData --
6387 *
6388 *	Called by the [item sort] code. Returns a long, double or
6389 *	string value from a text element.
6390 *
6391 * Results:
6392 *	A standard Tcl result.
6393 *
6394 * Side effects:
6395 *	None.
6396 *
6397 *----------------------------------------------------------------------
6398 */
6399
6400int
6401TreeStyle_GetSortData(
6402    TreeCtrl *tree,		/* Widget info. */
6403    TreeStyle style_,		/* The style. */
6404    int elemIndex,		/* Index of a text element, or -1 to use
6405				 * the first text element. */
6406    int type,			/* SORT_xxx constant. */
6407    long *lv,			/* Returned for SORT_LONG. */
6408    double *dv,			/* Returned for SORT_DOUBLE. */
6409    char **sv			/* Returned for SORT_ASCII or SORT_DICT. */
6410    )
6411{
6412    IStyle *style = (IStyle *) style_;
6413    IElementLink *eLink = style->elements;
6414    int i;
6415
6416    if (elemIndex == -1) {
6417	for (i = 0; i < style->master->numElements; i++) {
6418	    if (ELEMENT_TYPE_MATCHES(eLink->elem->typePtr, &treeElemTypeText))
6419		return TreeElement_GetSortData(tree, eLink->elem, type, lv, dv, sv);
6420	    eLink++;
6421	}
6422    } else {
6423	if ((elemIndex < 0) || (elemIndex >= style->master->numElements))
6424	    panic("bad elemIndex %d to TreeStyle_GetSortData", elemIndex);
6425	eLink = &style->elements[elemIndex];
6426	if (ELEMENT_TYPE_MATCHES(eLink->elem->typePtr, &treeElemTypeText))
6427	    return TreeElement_GetSortData(tree, eLink->elem, type, lv, dv, sv);
6428    }
6429
6430    FormatResult(tree->interp, "can't find text element in style %s",
6431	style->master->name);
6432    return TCL_ERROR;
6433}
6434
6435#if 0
6436
6437/*
6438 *----------------------------------------------------------------------
6439 *
6440 * TreeStyle_ValidateElements --
6441 *
6442 *	Verify that each object in an objv[] array refers to a
6443 *	master element.
6444 *
6445 * Results:
6446 *	A standard Tcl result.
6447 *
6448 * Side effects:
6449 *	None.
6450 *
6451 *----------------------------------------------------------------------
6452 */
6453
6454int
6455TreeStyle_ValidateElements(
6456    TreeCtrl *tree,		/* Widget info. */
6457    TreeStyle style_, 		/* Instance style. */
6458    int objc,			/* Number of element names. */
6459    Tcl_Obj *CONST objv[]	/* Array of element names. */
6460    )
6461{
6462    IStyle *style = (IStyle *) style_;
6463    MStyle *master = style->master;
6464    TreeElement elem;
6465    MElementLink *eLink;
6466    int i;
6467
6468    for (i = 0; i < objc; i++) {
6469	if (Element_FromObj(tree, objv[i], &elem) != TCL_OK)
6470	    return TCL_ERROR;
6471
6472	eLink = MStyle_FindElem(tree, master, elem, NULL);
6473	if (eLink == NULL) {
6474	    FormatResult(tree->interp,
6475		"style %s does not use element %s",
6476		master->name, elem->name);
6477	    return TCL_ERROR;
6478	}
6479    }
6480    return TCL_OK;
6481}
6482
6483#endif /* 0 */
6484
6485/*
6486 *----------------------------------------------------------------------
6487 *
6488 * TreeStyle_GetElemRects --
6489 *
6490 *	Return a list of rectangles for specified elements in a style.
6491 *
6492 * Results:
6493 *	The number of rects[] written.
6494 *
6495 * Side effects:
6496 *	None.
6497 *
6498 *----------------------------------------------------------------------
6499 */
6500
6501int
6502TreeStyle_GetElemRects(
6503    StyleDrawArgs *drawArgs,	/* Various args. */
6504    int objc,			/* Number of element names. */
6505    Tcl_Obj *CONST objv[],	/* Array of element names. */
6506    TreeRectangle rects[]	/* Returned rectangles. */
6507    )
6508
6509{
6510    IStyle *style = (IStyle *) drawArgs->style;
6511    MStyle *master = style->master;
6512    int i, j, count = 0, minWidth, minHeight;
6513    struct Layout staticLayouts[STATIC_SIZE], *layouts = staticLayouts;
6514    TreeElement staticElems[STATIC_SIZE], *elems = staticElems;
6515    MElementLink *eLink;
6516
6517    STATIC_ALLOC(elems, TreeElement, objc);
6518
6519    for (j = 0; j < objc; j++) {
6520	if (Element_FromObj(drawArgs->tree, objv[j], &elems[j]) != TCL_OK) {
6521	    count = -1;
6522	    goto done;
6523	}
6524
6525	eLink = MStyle_FindElem(drawArgs->tree, master, elems[j], NULL);
6526	if (eLink == NULL) {
6527	    FormatResult(drawArgs->tree->interp,
6528		"style %s does not use element %s",
6529		master->name, elems[j]->name);
6530	    count = -1;
6531	    goto done;
6532	}
6533    }
6534
6535#ifdef CACHE_STYLE_SIZE
6536    minWidth = style->minWidth;
6537    minHeight = style->minHeight;
6538#else
6539    Style_MinSize(drawArgs->tree, style, drawArgs->state, &minWidth, &minHeight);
6540#endif
6541
6542    if (drawArgs->width < minWidth + drawArgs->indent)
6543	drawArgs->width = minWidth + drawArgs->indent;
6544    if (drawArgs->height < minHeight)
6545	drawArgs->height = minHeight;
6546
6547    STATIC_ALLOC(layouts, struct Layout, master->numElements);
6548
6549    Style_DoLayout(drawArgs, layouts, FALSE, __FILE__, __LINE__);
6550
6551    for (i = master->numElements - 1; i >= 0; i--) {
6552	struct Layout *layout = &layouts[i];
6553
6554	if (IS_HIDDEN(layout))
6555	    continue;
6556
6557	if (objc > 0) {
6558	    for (j = 0; j < objc; j++)
6559		if (elems[j] == layout->eLink->elem ||
6560		    elems[j] == layout->master->elem)
6561		    break;
6562	    if (j == objc)
6563		continue;
6564	}
6565	rects[count].x = drawArgs->x + layout->x + layout->ePadX[PAD_TOP_LEFT];
6566	rects[count].y = drawArgs->y + layout->y + layout->ePadY[PAD_TOP_LEFT];
6567	if (layout->master->onion == NULL) {
6568	    rects[count].x += layout->iPadX[PAD_TOP_LEFT];
6569	    rects[count].y += layout->iPadY[PAD_TOP_LEFT];
6570	    rects[count].width = layout->useWidth;
6571	    rects[count].height = layout->useHeight;
6572	} else {
6573	    rects[count].width = layout->iWidth;
6574	    rects[count].height = layout->iHeight;
6575	}
6576	count++;
6577    }
6578
6579    STATIC_FREE(layouts, struct Layout, master->numElements);
6580
6581done:
6582    STATIC_FREE(elems, TreeElement, objc);
6583    return count;
6584}
6585
6586/*
6587 *----------------------------------------------------------------------
6588 *
6589 * TreeStyle_ChangeState --
6590 *
6591 *	Called when the state of an item or item-column changes.
6592 *
6593 * Results:
6594 *	A bitmask of CS_DISPLAY and CS_LAYOUT values.
6595 *
6596 * Side effects:
6597 *	None.
6598 *
6599 *----------------------------------------------------------------------
6600 */
6601
6602int
6603TreeStyle_ChangeState(
6604    TreeCtrl *tree,		/* Widget info. */
6605    TreeStyle style_,		/* The instance style. */
6606    int state1,			/* The previous state. */
6607    int state2			/* The current state. */
6608    )
6609{
6610    IStyle *style = (IStyle *) style_;
6611    MStyle *masterStyle = style->master;
6612    MElementLink *eLink1;
6613    IElementLink *eLink2;
6614    TreeElementArgs args;
6615    int i, eMask, mask = 0;
6616    int undisplay;
6617
6618    if (state1 == state2)
6619	return 0;
6620
6621    args.tree = tree;
6622
6623    for (i = 0; i < masterStyle->numElements; i++) {
6624	eLink2 = &style->elements[i];
6625	args.elem = eLink2->elem;
6626	args.states.state1 = state1;
6627	args.states.state2 = state2;
6628	args.states.draw1 = args.states.draw2 = TRUE;
6629	args.states.visible1 = args.states.visible2 = TRUE;
6630
6631	eLink1 = &masterStyle->elements[i];
6632	undisplay = FALSE;
6633	eMask = 0;
6634
6635	/* Check for the style layout option -draw changing. */
6636	if (eLink1->draw.count > 0) {
6637	    args.states.draw1 = PerStateBoolean_ForState(tree,
6638		    &eLink1->draw, state1, NULL) != 0;
6639	    args.states.draw2 = PerStateBoolean_ForState(tree,
6640		    &eLink1->draw, state2, NULL) != 0;
6641	    if (args.states.draw1 != args.states.draw2) {
6642		eMask |= CS_DISPLAY;
6643		if (!args.states.draw2)
6644		    undisplay = TRUE;
6645	    }
6646	}
6647
6648	/* Check for the style layout option -visible changing. */
6649	if (eLink1->visible.count > 0) {
6650	    args.states.visible1 = PerStateBoolean_ForState(tree,
6651		    &eLink1->visible, state1, NULL) != 0;
6652	    args.states.visible2 = PerStateBoolean_ForState(tree,
6653		    &eLink1->visible, state2, NULL) != 0;
6654	    /* FIXME: Changing visibility might not change the layout. */
6655	    if (args.states.visible1 != args.states.visible2) {
6656		eMask |= CS_DISPLAY | CS_LAYOUT;
6657		if (!args.states.visible2)
6658		    undisplay = TRUE;
6659	    }
6660	}
6661
6662	/* Tell the element about the state change. */
6663	eMask |= (*args.elem->typePtr->stateProc)(&args);
6664
6665	/* Hack: If a window element becomes hidden, then tell it it is
6666	 * not onscreen, otherwise it will never be "drawn" in the
6667	 * hidden state. */
6668	if (undisplay && ELEMENT_TYPE_MATCHES(args.elem->typePtr,
6669		&treeElemTypeWindow)) {
6670	    args.screen.visible = FALSE;
6671	    (*args.elem->typePtr->onScreenProc)(&args);
6672	}
6673
6674	if (eMask) {
6675#ifdef CACHE_ELEM_SIZE
6676	    if (eMask & CS_LAYOUT)
6677		eLink2->neededWidth = eLink2->neededHeight = -1;
6678#endif
6679	    mask |= eMask;
6680	}
6681    }
6682
6683    if (mask & CS_LAYOUT)
6684	style->neededWidth = style->neededHeight = -1;
6685
6686#ifdef TREECTRL_DEBUG
6687    if (style->neededWidth != -1)
6688	style->neededState = state2;
6689#endif
6690
6691    return mask;
6692}
6693
6694/*
6695 *----------------------------------------------------------------------
6696 *
6697 * Tree_UndefineState --
6698 *
6699 *	The guts of the [state undefine] widget command.
6700 *
6701 * Results:
6702 *	The undefProc of every element is called to respond to the
6703 *	undefined state flag. The size of every element/column/item is
6704 *	marked out-of-date regardless of whether the state change
6705 *	affected the element.
6706 *
6707 * Side effects:
6708 *	Display changes.
6709 *
6710 *----------------------------------------------------------------------
6711 */
6712
6713void
6714Tree_UndefineState(
6715    TreeCtrl *tree,		/* Widget info. */
6716    int state			/* STATE_xxx flag. */
6717    )
6718{
6719    TreeItem item;
6720    TreeItemColumn column;
6721    Tcl_HashEntry *hPtr;
6722    Tcl_HashSearch search;
6723    IElementLink *eLink;
6724    int i, columnIndex;
6725    TreeElementArgs args;
6726
6727    /* Undefine the state for the -draw and -visible style layout
6728     * options for each element of this style. */
6729    hPtr = Tcl_FirstHashEntry(&tree->styleHash, &search);
6730    while (hPtr != NULL) {
6731	MStyle *masterStyle = (MStyle *) Tcl_GetHashValue(hPtr);
6732	for (i = 0; i < masterStyle->numElements; i++) {
6733	    MElementLink *eLink1 = &masterStyle->elements[i];
6734	    PerStateInfo_Undefine(tree, &pstBoolean, &eLink1->draw, state);
6735	    PerStateInfo_Undefine(tree, &pstBoolean, &eLink1->visible, state);
6736	}
6737	hPtr = Tcl_NextHashEntry(&search);
6738    }
6739
6740    args.tree = tree;
6741    args.state = state;
6742
6743    hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
6744    while (hPtr != NULL) {
6745	item = (TreeItem) Tcl_GetHashValue(hPtr);
6746	column = TreeItem_GetFirstColumn(tree, item);
6747	columnIndex = 0;
6748	while (column != NULL) {
6749	    IStyle *style = (IStyle *) TreeItemColumn_GetStyle(tree, column);
6750	    if (style != NULL) {
6751		for (i = 0; i < style->master->numElements; i++) {
6752		    eLink = &style->elements[i];
6753		    /* Instance element */
6754		    if (eLink->elem->master != NULL) {
6755			args.elem = eLink->elem;
6756			(*args.elem->typePtr->undefProc)(&args);
6757		    }
6758#ifdef CACHE_ELEM_SIZE
6759		    eLink->neededWidth = eLink->neededHeight = -1;
6760#endif
6761		}
6762		style->neededWidth = style->neededHeight = -1;
6763		TreeItemColumn_InvalidateSize(tree, column);
6764	    }
6765	    columnIndex++;
6766	    column = TreeItemColumn_GetNext(tree, column);
6767	}
6768	TreeItem_InvalidateHeight(tree, item);
6769	Tree_FreeItemDInfo(tree, item, NULL);
6770	TreeItem_UndefineState(tree, item, state);
6771	hPtr = Tcl_NextHashEntry(&search);
6772    }
6773    Tree_InvalidateColumnWidth(tree, NULL);
6774    Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
6775
6776    hPtr = Tcl_FirstHashEntry(&tree->elementHash, &search);
6777    while (hPtr != NULL) {
6778	args.elem = (TreeElement) Tcl_GetHashValue(hPtr);
6779	(*args.elem->typePtr->undefProc)(&args);
6780	hPtr = Tcl_NextHashEntry(&search);
6781    }
6782}
6783
6784/*
6785 *----------------------------------------------------------------------
6786 *
6787 * TreeStyle_NumElements --
6788 *
6789 *	Return the number of elements in a style.
6790 *
6791 * Results:
6792 *	The number of... oh nevermind.
6793 *
6794 * Side effects:
6795 *	None.
6796 *
6797 *----------------------------------------------------------------------
6798 */
6799
6800int
6801TreeStyle_NumElements(
6802    TreeCtrl *tree,		/* Widget info. */
6803    TreeStyle style_		/* The style. */
6804    )
6805{
6806    MStyle *masterStyle = ((MStyle *) style_);
6807    IStyle *style = ((IStyle *) style_);
6808    return (style->master == NULL) ?
6809	masterStyle->numElements :
6810	style->master->numElements;
6811}
6812
6813/*
6814 *----------------------------------------------------------------------
6815 *
6816 * TreeStyle_Init --
6817 *
6818 *	Style-related package initialization.
6819 *
6820 * Results:
6821 *	A standard Tcl result.
6822 *
6823 * Side effects:
6824 *	None.
6825 *
6826 *----------------------------------------------------------------------
6827 */
6828
6829int
6830TreeStyle_Init(
6831    TreeCtrl *tree		/* Widget info. */
6832    )
6833{
6834    tree->styleOptionTable = Tk_CreateOptionTable(tree->interp,
6835	styleOptionSpecs);
6836    return TCL_OK;
6837}
6838
6839/*
6840 *----------------------------------------------------------------------
6841 *
6842 * TreeStyle_Free --
6843 *
6844 *	Free style-related resources for a deleted TreeCtrl.
6845 *
6846 * Results:
6847 *	None.
6848 *
6849 * Side effects:
6850 *	Memory is freed.
6851 *
6852 *----------------------------------------------------------------------
6853 */
6854
6855void
6856TreeStyle_Free(
6857    TreeCtrl *tree		/* Widget info. */
6858    )
6859{
6860    Tcl_HashEntry *hPtr;
6861    Tcl_HashSearch search;
6862    TreeElement elem;
6863    TreeStyle style;
6864
6865    while (1) {
6866	hPtr = Tcl_FirstHashEntry(&tree->styleHash, &search);
6867	if (hPtr == NULL)
6868	    break;
6869	style = (TreeStyle) Tcl_GetHashValue(hPtr);
6870	TreeStyle_FreeResources(tree, style);
6871    }
6872
6873    while (1) {
6874	hPtr = Tcl_FirstHashEntry(&tree->elementHash, &search);
6875	if (hPtr == NULL)
6876	    break;
6877	elem = (TreeElement) Tcl_GetHashValue(hPtr);
6878	Element_FreeResources(tree, elem);
6879    }
6880
6881    Tcl_DeleteHashTable(&tree->elementHash);
6882    Tcl_DeleteHashTable(&tree->styleHash);
6883}
6884
6885