1/*
2 * tkTreeElem.c --
3 *
4 *	This module implements elements for treectrl widgets.
5 *
6 * Copyright (c) 2002-2009 Tim Baker
7 *
8 * RCS: @(#) $Id: tkTreeElem.c,v 1.71 2010/03/08 17:04:58 treectrl Exp $
9 */
10
11#include "tkTreeCtrl.h"
12#include "tkTreeElem.h"
13
14/*
15 *----------------------------------------------------------------------
16 *
17 * DO_BooleanForState --
18 * DO_ColorForState --
19 * DO_FontForState --
20 *
21 *	Returns the value of a per-state option for an element.
22 *
23 * Results:
24 *	If the element has the dynamic option allocated, then the
25 *	per-state info is checked for a match. If an exact match for
26 *	the given state is not found, and if the element is an instance
27 *	(not a master), then the master element is checked.
28 *
29 * Side effects:
30 *	None.
31 *
32 *----------------------------------------------------------------------
33 */
34
35static int
36DO_BooleanForState(
37    TreeCtrl *tree,		/* Widget info. */
38    TreeElement elem,		/* Element to examine. */
39    int id,			/* Unique id of dynamic option. */
40    int state			/* STATE_xxx flags. */
41    )
42{
43    int result = -1;
44    PerStateInfo *psi;
45    int match = MATCH_NONE;
46
47    psi = DynamicOption_FindData(elem->options, id);
48    if (psi != NULL)
49	result = PerStateBoolean_ForState(tree, psi, state, &match);
50    if ((match != MATCH_EXACT) && (elem->master != NULL)) {
51	PerStateInfo *psi = DynamicOption_FindData(elem->master->options, id);
52	if (psi != NULL) {
53	    int matchM;
54	    int resultM = PerStateBoolean_ForState(tree, psi, state, &matchM);
55	    if (matchM > match)
56		result = resultM;
57	}
58    }
59    return result;
60}
61
62static XColor *
63DO_ColorForState(
64    TreeCtrl *tree,
65    TreeElement elem,
66    int id,
67    int state
68    )
69{
70    XColor *result = NULL;
71    PerStateInfo *psi;
72    int match = MATCH_NONE;
73
74    psi = DynamicOption_FindData(elem->options, id);
75    if (psi != NULL)
76	result = PerStateColor_ForState(tree, psi, state, &match);
77    if ((match != MATCH_EXACT) && (elem->master != NULL)) {
78	PerStateInfo *psi = DynamicOption_FindData(elem->master->options, id);
79	if (psi != NULL) {
80	    int matchM;
81	    XColor *resultM = PerStateColor_ForState(tree, psi, state, &matchM);
82	    if (matchM > match)
83		result = resultM;
84	}
85    }
86    return result;
87}
88
89static Tk_Font
90DO_FontForState(
91    TreeCtrl *tree,
92    TreeElement elem,
93    int id,
94    int state
95    )
96{
97    Tk_Font result = NULL;
98    PerStateInfo *psi;
99    int match = MATCH_NONE;
100
101    psi = DynamicOption_FindData(elem->options, id);
102    if (psi != NULL)
103	result = PerStateFont_ForState(tree, psi, state, &match);
104    if ((match != MATCH_EXACT) && (elem->master != NULL)) {
105	PerStateInfo *psi = DynamicOption_FindData(elem->master->options, id);
106	if (psi != NULL) {
107	    int matchM;
108	    Tk_Font resultM = PerStateFont_ForState(tree, psi, state, &matchM);
109	    if (matchM > match)
110		result = resultM;
111	}
112    }
113    return result;
114}
115
116/*
117 *----------------------------------------------------------------------
118 *
119 * DO_ObjectForState --
120 *
121 *	Returns the object representation of a per-state option
122 *	for an element.
123 *
124 * Results:
125 *	If the element has the dynamic option allocated, then the
126 *	per-state info is checked for a match. If an exact match for
127 *	the given state is not found, and if the element is an instance
128 *	(not a master), then the master element is checked.
129 *
130 * Side effects:
131 *	None.
132 *
133 *----------------------------------------------------------------------
134 */
135
136static Tcl_Obj *
137DO_ObjectForState(
138    TreeCtrl *tree,		/* Widget info. */
139    PerStateType *typePtr,	/* Type-specific functions and values. */
140    TreeElement elem,		/* Element to examine. */
141    int id,			/* Unique id of dynamic option. */
142    int state			/* STATE_xxx flags. */
143    )
144{
145    Tcl_Obj *result = NULL;
146    PerStateInfo *psi;
147    int match = MATCH_NONE;
148
149    psi = DynamicOption_FindData(elem->options, id);
150    if (psi != NULL)
151	result = PerStateInfo_ObjForState(tree, typePtr, psi, state, &match);
152    if ((match != MATCH_EXACT) && (elem->master != NULL)) {
153	PerStateInfo *psi = DynamicOption_FindData(elem->master->options, id);
154	if (psi != NULL) {
155	    int matchM;
156	    Tcl_Obj *resultM = PerStateInfo_ObjForState(tree, typePtr, psi, state, &matchM);
157	    if (matchM > match)
158		result = resultM;
159	}
160    }
161    return result;
162}
163
164/* BEGIN custom "boolean" option */
165
166/* Just like TK_OPTION_BOOLEAN but supports TK_OPTION_NULL_OK */
167/* Internal value is -1 for no-such-value */
168
169static int BooleanSet(
170    ClientData clientData,
171    Tcl_Interp *interp,
172    Tk_Window tkwin,
173    Tcl_Obj **value,
174    char *recordPtr,
175    int internalOffset,
176    char *saveInternalPtr,
177    int flags)
178{
179    int objEmpty;
180    int new, *internalPtr;
181
182    if (internalOffset >= 0)
183	internalPtr = (int *) (recordPtr + internalOffset);
184    else
185	internalPtr = NULL;
186
187    objEmpty = ObjectIsEmpty((*value));
188
189    if ((flags & TK_OPTION_NULL_OK) && objEmpty)
190	(*value) = NULL;
191    else {
192	if (Tcl_GetBooleanFromObj(interp, (*value), &new) != TCL_OK)
193	    return TCL_ERROR;
194    }
195    if (internalPtr != NULL) {
196	if ((*value) == NULL)
197	    new = -1;
198	*((int *) saveInternalPtr) = *internalPtr;
199	*internalPtr = new;
200    }
201
202    return TCL_OK;
203}
204
205static Tcl_Obj *BooleanGet(
206    ClientData clientData,
207    Tk_Window tkwin,
208    char *recordPtr,
209    int internalOffset)
210{
211    int value = *(int *) (recordPtr + internalOffset);
212    if (value == -1)
213	return NULL;
214    return Tcl_NewBooleanObj(value);
215}
216
217static void BooleanRestore(
218    ClientData clientData,
219    Tk_Window tkwin,
220    char *internalPtr,
221    char *saveInternalPtr)
222{
223    *(int *) internalPtr = *(int *) saveInternalPtr;
224}
225
226static Tk_ObjCustomOption booleanCO =
227{
228    "boolean",
229    BooleanSet,
230    BooleanGet,
231    BooleanRestore,
232    NULL,
233    (ClientData) NULL
234};
235
236static void
237DynamicOptionInitBoolean(
238    void *data
239    )
240{
241    *((int *) data) = -1;
242}
243
244/* END custom "boolean" option */
245
246/* BEGIN custom "integer" option */
247
248/* Just like TK_OPTION_INT but supports TK_OPTION_NULL_OK and bounds checking */
249
250typedef struct IntegerClientData
251{
252    int min;
253    int max;
254    int empty; /* internal form if empty */
255    int flags; /* 0x01 - use min, 0x02 - use max */
256} IntegerClientData;
257
258static int IntegerSet(
259    ClientData clientData,
260    Tcl_Interp *interp,
261    Tk_Window tkwin,
262    Tcl_Obj **value,
263    char *recordPtr,
264    int internalOffset,
265    char *saveInternalPtr,
266    int flags)
267{
268    IntegerClientData *cd = clientData;
269    int objEmpty;
270    int new, *internalPtr;
271
272    if (internalOffset >= 0)
273	internalPtr = (int *) (recordPtr + internalOffset);
274    else
275	internalPtr = NULL;
276
277    objEmpty = ObjectIsEmpty((*value));
278
279    if ((flags & TK_OPTION_NULL_OK) && objEmpty)
280	(*value) = NULL;
281    else {
282	if (Tcl_GetIntFromObj(interp, (*value), &new) != TCL_OK)
283	    return TCL_ERROR;
284	if ((cd->flags & 0x01) && (new < cd->min)) {
285	    FormatResult(interp,
286		    "bad integer value \"%d\": must be >= %d",
287		    new, cd->min);
288	    return TCL_ERROR;
289	}
290	if ((cd->flags & 0x02) && (new > cd->max)) {
291	    FormatResult(interp,
292		    "bad integer value \"%d\": must be <= %d",
293		    new, cd->max);
294	    return TCL_ERROR;
295	}
296    }
297    if (internalPtr != NULL) {
298	if ((*value) == NULL)
299	    new = cd->empty;
300	*((int *) saveInternalPtr) = *internalPtr;
301	*internalPtr = new;
302    }
303
304    return TCL_OK;
305}
306
307static Tcl_Obj *IntegerGet(
308    ClientData clientData,
309    Tk_Window tkwin,
310    char *recordPtr,
311    int internalOffset)
312{
313    IntegerClientData *cd = clientData;
314    int value = *(int *) (recordPtr + internalOffset);
315    if (value == cd->empty)
316	return NULL;
317    return Tcl_NewIntObj(value);
318}
319
320static void IntegerRestore(
321    ClientData clientData,
322    Tk_Window tkwin,
323    char *internalPtr,
324    char *saveInternalPtr)
325{
326    *(int *) internalPtr = *(int *) saveInternalPtr;
327}
328
329/* END custom "integer" option */
330
331/*****/
332
333/* BEGIN custom "stringtable" option */
334
335/* Just like TK_OPTION_STRING_TABLE but supports TK_OPTION_NULL_OK */
336/* The integer rep is -1 if empty string specified */
337
338typedef struct StringTableClientData
339{
340    CONST char **tablePtr; /* NULL-termintated list of strings */
341    CONST char *msg; /* Tcl_GetIndexFromObj() message */
342} StringTableClientData;
343
344static int StringTableSet(
345    ClientData clientData,
346    Tcl_Interp *interp,
347    Tk_Window tkwin,
348    Tcl_Obj **value,
349    char *recordPtr,
350    int internalOffset,
351    char *saveInternalPtr,
352    int flags)
353{
354    StringTableClientData *cd = clientData;
355    int objEmpty;
356    int new, *internalPtr;
357
358    if (internalOffset >= 0)
359	internalPtr = (int *) (recordPtr + internalOffset);
360    else
361	internalPtr = NULL;
362
363    objEmpty = ObjectIsEmpty((*value));
364
365    if ((flags & TK_OPTION_NULL_OK) && objEmpty)
366	(*value) = NULL;
367    else {
368	if (Tcl_GetIndexFromObj(interp, (*value), cd->tablePtr,
369		    cd->msg, 0, &new) != TCL_OK)
370	    return TCL_ERROR;
371    }
372    if (internalPtr != NULL) {
373	if ((*value) == NULL)
374	    new = -1;
375	*((int *) saveInternalPtr) = *internalPtr;
376	*internalPtr = new;
377    }
378
379    return TCL_OK;
380}
381
382static Tcl_Obj *StringTableGet(
383    ClientData clientData,
384    Tk_Window tkwin,
385    char *recordPtr,
386    int internalOffset)
387{
388    StringTableClientData *cd = clientData;
389    int index = *(int *) (recordPtr + internalOffset);
390
391    if (index == -1)
392	return NULL;
393    return Tcl_NewStringObj(cd->tablePtr[index], -1);
394}
395
396static void StringTableRestore(
397    ClientData clientData,
398    Tk_Window tkwin,
399    char *internalPtr,
400    char *saveInternalPtr)
401{
402    *(int *) internalPtr = *(int *) saveInternalPtr;
403}
404
405/* END custom "stringtable" option */
406
407static int
408BooleanCO_Init(
409    Tk_OptionSpec *optionTable,
410    CONST char *optionName)
411{
412    Tk_OptionSpec *specPtr;
413
414    specPtr = Tree_FindOptionSpec(optionTable, optionName);
415    specPtr->clientData = &booleanCO;
416    return TCL_OK;
417}
418
419static Tk_ObjCustomOption *
420IntegerCO_Alloc(
421    CONST char *optionName,
422    int min,
423    int max,
424    int empty,
425    int flags
426    )
427{
428    IntegerClientData *cd;
429    Tk_ObjCustomOption *co;
430
431    /* ClientData for the Tk custom option record */
432    cd = (IntegerClientData *) ckalloc(sizeof(IntegerClientData));
433    cd->min = min;
434    cd->max = max;
435    cd->empty = empty;
436    cd->flags = flags;
437
438    /* The Tk custom option record */
439    co = (Tk_ObjCustomOption *) ckalloc(sizeof(Tk_ObjCustomOption));
440    co->name = (char *) optionName + 1;
441    co->setProc = IntegerSet;
442    co->getProc = IntegerGet;
443    co->restoreProc = IntegerRestore;
444    co->freeProc = NULL;
445    co->clientData = (ClientData) cd;
446
447    return co;
448}
449
450static int
451IntegerCO_Init(
452    Tk_OptionSpec *optionTable,
453    CONST char *optionName,
454    int min,
455    int max,
456    int empty,
457    int flags
458    )
459{
460    Tk_OptionSpec *specPtr;
461
462    specPtr = Tree_FindOptionSpec(optionTable, optionName);
463    if (specPtr->type != TK_OPTION_CUSTOM)
464	panic("IntegerCO_Init: %s is not TK_OPTION_CUSTOM", optionName);
465    if (specPtr->clientData != NULL)
466	return TCL_OK;
467
468    specPtr->clientData = IntegerCO_Alloc(optionName, min, max, empty, flags);
469
470    return TCL_OK;
471}
472
473static Tk_ObjCustomOption *
474StringTableCO_Alloc(
475    CONST char *optionName,
476    CONST char **tablePtr
477    )
478{
479    StringTableClientData *cd;
480    Tk_ObjCustomOption *co;
481
482    /* ClientData for the Tk custom option record */
483    cd = (StringTableClientData *) ckalloc(sizeof(StringTableClientData));
484    cd->tablePtr = tablePtr;
485    cd->msg = optionName + 1;
486
487    /* The Tk custom option record */
488    co = (Tk_ObjCustomOption *) ckalloc(sizeof(Tk_ObjCustomOption));
489    co->name = (char *) optionName + 1;
490    co->setProc = StringTableSet;
491    co->getProc = StringTableGet;
492    co->restoreProc = StringTableRestore;
493    co->freeProc = NULL;
494    co->clientData = (ClientData) cd;
495
496    return co;
497}
498
499int StringTableCO_Init(Tk_OptionSpec *optionTable, CONST char *optionName, CONST char **tablePtr)
500{
501    Tk_OptionSpec *specPtr;
502
503    specPtr = Tree_FindOptionSpec(optionTable, optionName);
504    if (specPtr->type != TK_OPTION_CUSTOM)
505	panic("StringTableCO_Init: %s is not TK_OPTION_CUSTOM", optionName);
506    if (specPtr->clientData != NULL)
507	return TCL_OK;
508
509    specPtr->clientData = StringTableCO_Alloc(optionName, tablePtr);
510
511    return TCL_OK;
512}
513
514/*****/
515
516int TreeStateFromObj(TreeCtrl *tree, Tcl_Obj *obj, int *stateOff, int *stateOn)
517{
518    int states[3];
519
520    states[STATE_OP_ON] = states[STATE_OP_OFF] = states[STATE_OP_TOGGLE] = 0;
521    if (Tree_StateFromObj(tree, obj, states, NULL, SFO_NOT_TOGGLE) != TCL_OK)
522	return TCL_ERROR;
523
524    (*stateOn) |= states[STATE_OP_ON];
525    (*stateOff) |= states[STATE_OP_OFF];
526    return TCL_OK;
527}
528
529static void AdjustForSticky(int sticky, int cavityWidth, int cavityHeight,
530    int expandX, int expandY,
531    int *xPtr, int *yPtr, int *widthPtr, int *heightPtr)
532{
533    int dx = 0;
534    int dy = 0;
535
536    if (cavityWidth > *widthPtr) {
537	dx = cavityWidth - *widthPtr;
538    }
539
540    if (cavityHeight > *heightPtr) {
541	dy = cavityHeight - *heightPtr;
542    }
543
544    if ((sticky & STICKY_W) && (sticky & STICKY_E)) {
545	if (expandX)
546	    *widthPtr += dx;
547	else
548	    sticky &= ~(STICKY_W | STICKY_E);
549    }
550    if ((sticky & STICKY_N) && (sticky & STICKY_S)) {
551	if (expandY)
552	    *heightPtr += dy;
553	else
554	    sticky &= ~(STICKY_N | STICKY_S);
555    }
556    if (!(sticky & STICKY_W)) {
557	*xPtr += (sticky & STICKY_E) ? dx : dx / 2;
558    }
559    if (!(sticky & STICKY_N)) {
560	*yPtr += (sticky & STICKY_S) ? dy : dy / 2;
561    }
562}
563
564/* This macro gets the value of a per-state option for an element, then
565 * looks for a better match from the master element if it exists */
566#define OPTION_FOR_STATE(xFUNC,xTYPE,xVAR,xFIELD,xSTATE) \
567    xVAR = xFUNC(tree, &elemX->xFIELD, xSTATE, &match); \
568    if ((match != MATCH_EXACT) && (masterX != NULL)) { \
569	xTYPE varM = xFUNC(tree, &masterX->xFIELD, xSTATE, &match2); \
570	if (match2 > match) \
571	    xVAR = varM; \
572    }
573#define BITMAP_FOR_STATE(xVAR,xFIELD,xSTATE) \
574    OPTION_FOR_STATE(PerStateBitmap_ForState,Pixmap,xVAR,xFIELD,xSTATE)
575#define BOOLEAN_FOR_STATE(xVAR,xFIELD,xSTATE) \
576    OPTION_FOR_STATE(PerStateBoolean_ForState,int,xVAR,xFIELD,xSTATE)
577#define BORDER_FOR_STATE(xVAR,xFIELD,xSTATE) \
578    OPTION_FOR_STATE(PerStateBorder_ForState,Tk_3DBorder,xVAR,xFIELD,xSTATE)
579#define COLOR_FOR_STATE(xVAR,xFIELD,xSTATE) \
580    OPTION_FOR_STATE(PerStateColor_ForState,XColor*,xVAR,xFIELD,xSTATE)
581#define FONT_FOR_STATE(xVAR,xFIELD,xSTATE) \
582    OPTION_FOR_STATE(PerStateFont_ForState,Tk_Font,xVAR,xFIELD,xSTATE)
583#define IMAGE_FOR_STATE(xVAR,xFIELD,xSTATE) \
584    OPTION_FOR_STATE(PerStateImage_ForState,Tk_Image,xVAR,xFIELD,xSTATE)
585#define RELIEF_FOR_STATE(xVAR,xFIELD,xSTATE) \
586    OPTION_FOR_STATE(PerStateRelief_ForState,int,xVAR,xFIELD,xSTATE)
587
588/* This macro gets the object for a per-state option for an element, then
589 * looks for a better match from the master element if it exists */
590#define OBJECT_FOR_STATE(xVAR,xTYPE,xFIELD,xSTATE) \
591    xVAR = PerStateInfo_ObjForState(tree, &xTYPE, &elemX->xFIELD, xSTATE, &match); \
592    if ((match != MATCH_EXACT) && (masterX != NULL)) { \
593	Tcl_Obj *objM = PerStateInfo_ObjForState(tree, &xTYPE, &masterX->xFIELD, xSTATE, &matchM); \
594	if (matchM > match) \
595	    xVAR = objM; \
596    }
597
598/*****/
599
600typedef struct ElementBitmap ElementBitmap;
601
602struct ElementBitmap
603{
604    TreeElement_ header;
605#ifdef DEPRECATED
606    PerStateInfo draw;
607#endif
608    PerStateInfo bitmap;
609    PerStateInfo fg;
610    PerStateInfo bg;
611};
612
613#define BITMAP_CONF_BITMAP 0x0001
614#define BITMAP_CONF_FG 0x0002
615#define BITMAP_CONF_BG 0x0004
616#ifdef DEPRECATED
617#define BITMAP_CONF_DRAW 0x0008
618#endif
619
620static Tk_OptionSpec bitmapOptionSpecs[] = {
621    {TK_OPTION_CUSTOM, "-background", (char *) NULL, (char *) NULL,
622     (char *) NULL,
623     Tk_Offset(ElementBitmap, bg.obj), Tk_Offset(ElementBitmap, bg),
624     TK_OPTION_NULL_OK, (ClientData) NULL, BITMAP_CONF_BG},
625    {TK_OPTION_CUSTOM, "-bitmap", (char *) NULL, (char *) NULL,
626     (char *) NULL,
627     Tk_Offset(ElementBitmap, bitmap.obj), Tk_Offset(ElementBitmap, bitmap),
628     TK_OPTION_NULL_OK, (ClientData) NULL, BITMAP_CONF_BITMAP},
629#ifdef DEPRECATED
630    {TK_OPTION_CUSTOM, "-draw", (char *) NULL, (char *) NULL,
631     (char *) NULL,
632     Tk_Offset(ElementBitmap, draw.obj), Tk_Offset(ElementBitmap, draw),
633     TK_OPTION_NULL_OK, (ClientData) NULL, BITMAP_CONF_DRAW},
634#endif
635    {TK_OPTION_CUSTOM, "-foreground", (char *) NULL, (char *) NULL,
636     (char *) NULL,
637     Tk_Offset(ElementBitmap, fg.obj), Tk_Offset(ElementBitmap, fg),
638     TK_OPTION_NULL_OK, (ClientData) NULL, BITMAP_CONF_FG},
639    {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
640     (char *) NULL, 0, -1, 0, (ClientData) NULL, 0}
641};
642
643static void DeleteProcBitmap(TreeElementArgs *args)
644{
645/*    TreeCtrl *tree = args->tree;
646    TreeElement elem = args->elem;
647    ElementBitmap *elemX = (ElementBitmap *) elem;*/
648}
649
650static int WorldChangedProcBitmap(TreeElementArgs *args)
651{
652    int flagM = args->change.flagMaster;
653    int flagS = args->change.flagSelf;
654    int mask = 0;
655
656    if ((flagS | flagM) & BITMAP_CONF_BITMAP)
657	mask |= CS_DISPLAY | CS_LAYOUT;
658
659    if ((flagS | flagM) & (
660#ifdef DEPRECATED
661	    BITMAP_CONF_DRAW |
662#endif
663	    BITMAP_CONF_FG | BITMAP_CONF_BG))
664	mask |= CS_DISPLAY;
665
666    return mask;
667}
668
669static int ConfigProcBitmap(TreeElementArgs *args)
670{
671    TreeCtrl *tree = args->tree;
672    TreeElement elem = args->elem;
673    ElementBitmap *elemX = (ElementBitmap *) elem;
674    Tk_SavedOptions savedOptions;
675    int error;
676    Tcl_Obj *errorResult = NULL;
677
678    for (error = 0; error <= 1; error++) {
679	if (error == 0) {
680	    if (Tk_SetOptions(tree->interp, (char *) elemX,
681			elem->typePtr->optionTable,
682			args->config.objc, args->config.objv, tree->tkwin,
683			&savedOptions, &args->config.flagSelf) != TCL_OK) {
684		args->config.flagSelf = 0;
685		continue;
686	    }
687
688	    Tk_FreeSavedOptions(&savedOptions);
689	    break;
690	} else {
691	    errorResult = Tcl_GetObjResult(tree->interp);
692	    Tcl_IncrRefCount(errorResult);
693	    Tk_RestoreSavedOptions(&savedOptions);
694
695	    Tcl_SetObjResult(tree->interp, errorResult);
696	    Tcl_DecrRefCount(errorResult);
697	    return TCL_ERROR;
698	}
699    }
700
701    return TCL_OK;
702}
703
704static int CreateProcBitmap(TreeElementArgs *args)
705{
706    return TCL_OK;
707}
708
709static void DisplayProcBitmap(TreeElementArgs *args)
710{
711    TreeCtrl *tree = args->tree;
712    TreeElement elem = args->elem;
713    ElementBitmap *elemX = (ElementBitmap *) elem;
714    ElementBitmap *masterX = (ElementBitmap *) elem->master;
715    int state = args->state;
716    int x = args->display.x, y = args->display.y;
717    int width, height;
718    int match, match2;
719#ifdef DEPRECATED
720    int draw;
721#endif
722    Pixmap bitmap;
723    XColor *fg, *bg;
724    int imgW, imgH;
725
726#ifdef DEPRECATED
727    BOOLEAN_FOR_STATE(draw, draw, state)
728    if (!draw)
729	return;
730#endif
731
732    BITMAP_FOR_STATE(bitmap, bitmap, state)
733    if (bitmap == None)
734	return;
735
736    COLOR_FOR_STATE(fg, fg, state)
737    COLOR_FOR_STATE(bg, bg, state)
738
739    Tk_SizeOfBitmap(tree->display, bitmap, &imgW, &imgH);
740    width = imgW, height = imgH;
741    AdjustForSticky(args->display.sticky,
742	args->display.width, args->display.height,
743	FALSE, FALSE,
744	&x, &y, &width, &height);
745    if (imgW > args->display.width)
746	imgW = args->display.width;
747    if (imgH > args->display.height)
748	imgH = args->display.height;
749    Tree_DrawBitmap(tree, bitmap, args->display.drawable, fg, bg,
750	0, 0, (unsigned int) imgW, (unsigned int) imgH,
751	x, y);
752}
753
754static void NeededProcBitmap(TreeElementArgs *args)
755{
756    TreeCtrl *tree = args->tree;
757    TreeElement elem = args->elem;
758    ElementBitmap *elemX = (ElementBitmap *) elem;
759    ElementBitmap *masterX = (ElementBitmap *) elem->master;
760    int state = args->state;
761    int width = 0, height = 0;
762    int match, match2;
763    Pixmap bitmap;
764
765    BITMAP_FOR_STATE(bitmap, bitmap, state)
766
767    if (bitmap != None)
768	Tk_SizeOfBitmap(tree->display, bitmap, &width, &height);
769
770    args->needed.width = width;
771    args->needed.height = height;
772}
773
774static int StateProcBitmap(TreeElementArgs *args)
775{
776    TreeCtrl *tree = args->tree;
777    TreeElement elem = args->elem;
778    ElementBitmap *elemX = (ElementBitmap *) elem;
779    ElementBitmap *masterX = (ElementBitmap *) elem->master;
780    int match, match2;
781#ifdef DEPRECATED
782    int draw1, draw2;
783#endif
784    Pixmap bitmap1, bitmap2;
785    XColor *fg1, *fg2;
786    XColor *bg1, *bg2;
787
788    if (!args->states.visible2)
789	return 0;
790
791    BITMAP_FOR_STATE(bitmap1, bitmap, args->states.state1)
792    BITMAP_FOR_STATE(bitmap2, bitmap, args->states.state2)
793    if (bitmap1 != bitmap2) {
794	if ((bitmap1 != None) && (bitmap2 != None)) {
795	    int w1, h1, w2, h2;
796	    Tk_SizeOfBitmap(tree->display, bitmap1, &w1, &h1);
797	    Tk_SizeOfBitmap(tree->display, bitmap2, &w2, &h2);
798	    if ((w1 != w2) || (h1 != h2))
799		return CS_DISPLAY | CS_LAYOUT;
800	    return CS_DISPLAY;
801	}
802	return CS_DISPLAY | CS_LAYOUT;
803    }
804
805    /* Layout hasn't changed, and -draw layout option is false. */
806    if (!args->states.draw2)
807	return 0;
808#ifdef DEPRECATED
809    BOOLEAN_FOR_STATE(draw1, draw, args->states.state1)
810    BOOLEAN_FOR_STATE(draw2, draw, args->states.state2)
811    if ((draw1 != 0) != (draw2 != 0))
812	return CS_DISPLAY;
813    if (draw2 == 0)
814	return 0;
815#endif
816
817    COLOR_FOR_STATE(fg1, fg, args->states.state1)
818    COLOR_FOR_STATE(fg2, fg, args->states.state2)
819    if (fg1 != fg2)
820	return CS_DISPLAY;
821
822    COLOR_FOR_STATE(bg1, bg, args->states.state1)
823    COLOR_FOR_STATE(bg2, bg, args->states.state2)
824    if (bg1 != bg2)
825	return CS_DISPLAY;
826
827    return 0;
828}
829
830static int UndefProcBitmap(TreeElementArgs *args)
831{
832    TreeCtrl *tree = args->tree;
833    ElementBitmap *elemX = (ElementBitmap *) args->elem;
834    int modified = 0;
835
836#ifdef DEPRECATED
837    modified |= PerStateInfo_Undefine(tree, &pstBoolean, &elemX->draw, args->state);
838#endif
839    modified |= PerStateInfo_Undefine(tree, &pstColor, &elemX->fg, args->state);
840    modified |= PerStateInfo_Undefine(tree, &pstColor, &elemX->bg, args->state);
841    modified |= PerStateInfo_Undefine(tree, &pstBitmap, &elemX->bitmap, args->state);
842    return modified;
843}
844
845static int ActualProcBitmap(TreeElementArgs *args)
846{
847    TreeCtrl *tree = args->tree;
848    ElementBitmap *elemX = (ElementBitmap *) args->elem;
849    ElementBitmap *masterX = (ElementBitmap *) args->elem->master;
850    static CONST char *optionName[] = {
851	"-background", "-bitmap",
852#ifdef DEPRECATED
853	"-draw",
854#endif
855	"-foreground",
856	(char *) NULL };
857    int index, match, matchM;
858    Tcl_Obj *obj = NULL;
859
860    if (Tcl_GetIndexFromObj(tree->interp, args->actual.obj, optionName,
861		"option", 0, &index) != TCL_OK)
862	return TCL_ERROR;
863
864    switch (index) {
865	case 0: {
866	    OBJECT_FOR_STATE(obj, pstColor, bg, args->state)
867	    break;
868	}
869	case 1: {
870	    OBJECT_FOR_STATE(obj, pstBitmap, bitmap, args->state)
871	    break;
872	}
873#ifdef DEPRECATED
874	case 2: {
875	    OBJECT_FOR_STATE(obj, pstBoolean, draw, args->state)
876	    break;
877	}
878	case 3: {
879	    OBJECT_FOR_STATE(obj, pstColor, fg, args->state)
880	    break;
881	}
882#else
883	case 2: {
884	    OBJECT_FOR_STATE(obj, pstColor, fg, args->state)
885	    break;
886	}
887#endif
888    }
889    if (obj != NULL)
890	Tcl_SetObjResult(tree->interp, obj);
891    return TCL_OK;
892}
893
894TreeElementType treeElemTypeBitmap = {
895    "bitmap",
896    sizeof(ElementBitmap),
897    bitmapOptionSpecs,
898    NULL,
899    CreateProcBitmap,
900    DeleteProcBitmap,
901    ConfigProcBitmap,
902    DisplayProcBitmap,
903    NeededProcBitmap,
904    NULL, /* heightProc */
905    WorldChangedProcBitmap,
906    StateProcBitmap,
907    UndefProcBitmap,
908    ActualProcBitmap,
909    NULL /* onScreenProc */
910};
911
912/*****/
913
914typedef struct ElementBorder ElementBorder;
915
916struct ElementBorder
917{
918    TreeElement_ header; /* Must be first */
919#ifdef DEPRECATED
920    PerStateInfo draw;
921#endif
922    PerStateInfo border;
923    PerStateInfo relief;
924    int thickness;
925    Tcl_Obj *thicknessObj;
926    int width;
927    Tcl_Obj *widthObj;
928    int height;
929    Tcl_Obj *heightObj;
930    int filled;
931};
932
933#define BORDER_CONF_BG 0x0001
934#define BORDER_CONF_RELIEF 0x0002
935#define BORDER_CONF_SIZE 0x0004
936#define BORDER_CONF_THICKNESS 0x0008
937#define BORDER_CONF_FILLED 0x0010
938#ifdef DEPRECATED
939#define BORDER_CONF_DRAW 0x0020
940#endif
941
942static Tk_OptionSpec borderOptionSpecs[] = {
943    {TK_OPTION_CUSTOM, "-background", (char *) NULL, (char *) NULL,
944     (char *) NULL,
945     Tk_Offset(ElementBorder, border.obj), Tk_Offset(ElementBorder, border),
946     TK_OPTION_NULL_OK, (ClientData) NULL, BORDER_CONF_BG},
947#ifdef DEPRECATED
948    {TK_OPTION_CUSTOM, "-draw", (char *) NULL, (char *) NULL,
949     (char *) NULL,
950     Tk_Offset(ElementBorder, draw.obj), Tk_Offset(ElementBorder, draw),
951     TK_OPTION_NULL_OK, (ClientData) NULL, BORDER_CONF_DRAW},
952#endif
953    {TK_OPTION_CUSTOM, "-filled", (char *) NULL, (char *) NULL,
954     (char *) NULL, -1, Tk_Offset(ElementBorder, filled),
955     TK_OPTION_NULL_OK, (ClientData) &booleanCO, BORDER_CONF_FILLED},
956    {TK_OPTION_PIXELS, "-height", (char *) NULL, (char *) NULL,
957     (char *) NULL, Tk_Offset(ElementBorder, heightObj),
958     Tk_Offset(ElementBorder, height),
959     TK_OPTION_NULL_OK, (ClientData) NULL, BORDER_CONF_SIZE},
960    {TK_OPTION_CUSTOM, "-relief", (char *) NULL, (char *) NULL,
961     (char *) NULL,
962     Tk_Offset(ElementBorder, relief.obj), Tk_Offset(ElementBorder, relief),
963     TK_OPTION_NULL_OK, (ClientData) NULL, BORDER_CONF_RELIEF},
964    {TK_OPTION_PIXELS, "-thickness", (char *) NULL, (char *) NULL,
965     (char *) NULL, Tk_Offset(ElementBorder, thicknessObj),
966     Tk_Offset(ElementBorder, thickness),
967     TK_OPTION_NULL_OK, (ClientData) NULL, BORDER_CONF_THICKNESS},
968    {TK_OPTION_PIXELS, "-width", (char *) NULL, (char *) NULL,
969     (char *) NULL, Tk_Offset(ElementBorder, widthObj),
970     Tk_Offset(ElementBorder, width),
971     TK_OPTION_NULL_OK, (ClientData) NULL, BORDER_CONF_SIZE},
972    {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
973     (char *) NULL, 0, -1, 0, (ClientData) NULL, 0}
974};
975
976static void DeleteProcBorder(TreeElementArgs *args)
977{
978/*    TreeCtrl *tree = args->tree;
979    TreeElement elem = args->elem;
980    ElementBorder *elemX = (ElementBorder *) elem;*/
981}
982
983static int WorldChangedProcBorder(TreeElementArgs *args)
984{
985    int flagM = args->change.flagMaster;
986    int flagS = args->change.flagSelf;
987    int mask = 0;
988
989    if ((flagS | flagM) & BORDER_CONF_SIZE)
990	mask |= CS_DISPLAY | CS_LAYOUT;
991
992    if ((flagS | flagM) & (
993#ifdef DEPRECATED
994	    BORDER_CONF_DRAW |
995#endif
996	    BORDER_CONF_BG | BORDER_CONF_RELIEF | BORDER_CONF_THICKNESS |
997	    BORDER_CONF_FILLED))
998	mask |= CS_DISPLAY;
999
1000    return mask;
1001}
1002
1003static int ConfigProcBorder(TreeElementArgs *args)
1004{
1005    TreeCtrl *tree = args->tree;
1006    TreeElement elem = args->elem;
1007    ElementBorder *elemX = (ElementBorder *) elem;
1008    Tk_SavedOptions savedOptions;
1009    int error;
1010    Tcl_Obj *errorResult = NULL;
1011
1012    for (error = 0; error <= 1; error++) {
1013	if (error == 0) {
1014	    if (Tk_SetOptions(tree->interp, (char *) elemX,
1015			elem->typePtr->optionTable,
1016			args->config.objc, args->config.objv, tree->tkwin,
1017			&savedOptions, &args->config.flagSelf) != TCL_OK) {
1018		args->config.flagSelf = 0;
1019		continue;
1020	    }
1021
1022	    Tk_FreeSavedOptions(&savedOptions);
1023	    break;
1024	} else {
1025	    errorResult = Tcl_GetObjResult(tree->interp);
1026	    Tcl_IncrRefCount(errorResult);
1027	    Tk_RestoreSavedOptions(&savedOptions);
1028
1029	    Tcl_SetObjResult(tree->interp, errorResult);
1030	    Tcl_DecrRefCount(errorResult);
1031	    return TCL_ERROR;
1032	}
1033    }
1034
1035    return TCL_OK;
1036}
1037
1038static int CreateProcBorder(TreeElementArgs *args)
1039{
1040    TreeElement elem = args->elem;
1041    ElementBorder *elemX = (ElementBorder *) elem;
1042
1043    elemX->filled = -1;
1044    return TCL_OK;
1045}
1046
1047static void DisplayProcBorder(TreeElementArgs *args)
1048{
1049    TreeCtrl *tree = args->tree;
1050    TreeElement elem = args->elem;
1051    ElementBorder *elemX = (ElementBorder *) elem;
1052    ElementBorder *masterX = (ElementBorder *) elem->master;
1053    int state = args->state;
1054    int x = args->display.x, y = args->display.y;
1055    int width = args->display.width, height = args->display.height;
1056    int match, match2;
1057#ifdef DEPRECATED
1058    int draw;
1059#endif
1060    Tk_3DBorder border;
1061    int relief, filled = FALSE;
1062    int thickness = 0;
1063
1064#ifdef DEPRECATED
1065    BOOLEAN_FOR_STATE(draw, draw, state)
1066    if (!draw)
1067	return;
1068#endif
1069
1070    BORDER_FOR_STATE(border, border, state)
1071    if (border == NULL)
1072	return;
1073
1074    RELIEF_FOR_STATE(relief, relief, state)
1075    if (relief == TK_RELIEF_NULL)
1076	relief = TK_RELIEF_FLAT;
1077
1078    if (elemX->thicknessObj)
1079	thickness = elemX->thickness;
1080    else if ((masterX != NULL) && (masterX->thicknessObj != NULL))
1081	thickness = masterX->thickness;
1082
1083    if (elemX->filled != -1)
1084	filled = elemX->filled;
1085    else if ((masterX != NULL) && (masterX->filled != -1))
1086	filled = masterX->filled;
1087
1088    if (elemX->widthObj != NULL)
1089	width = elemX->width;
1090    else if ((masterX != NULL) && (masterX->widthObj != NULL))
1091	width = masterX->width;
1092
1093    if (elemX->heightObj != NULL)
1094	height = elemX->height;
1095    else if ((masterX != NULL) && (masterX->heightObj != NULL))
1096	height = masterX->height;
1097
1098    AdjustForSticky(args->display.sticky,
1099	args->display.width, args->display.height,
1100	TRUE, TRUE,
1101	&x, &y, &width, &height);
1102
1103    if (filled) {
1104	Tk_Fill3DRectangle(tree->tkwin, args->display.drawable, border,
1105		x, y, width, height, thickness, relief);
1106    } else if (thickness > 0) {
1107	Tk_Draw3DRectangle(tree->tkwin, args->display.drawable, border,
1108		x, y, width, height, thickness, relief);
1109    }
1110}
1111
1112static void NeededProcBorder(TreeElementArgs *args)
1113{
1114    TreeElement elem = args->elem;
1115    ElementBorder *elemX = (ElementBorder *) elem;
1116    ElementBorder *masterX = (ElementBorder *) elem->master;
1117    int width = 0, height = 0;
1118
1119    if (elemX->widthObj != NULL)
1120	width = elemX->width;
1121    else if ((masterX != NULL) && (masterX->widthObj != NULL))
1122	width = masterX->width;
1123
1124    if (elemX->heightObj != NULL)
1125	height = elemX->height;
1126    else if ((masterX != NULL) && (masterX->heightObj != NULL))
1127	height = masterX->height;
1128
1129    args->needed.width = width;
1130    args->needed.height = height;
1131}
1132
1133static int StateProcBorder(TreeElementArgs *args)
1134{
1135    TreeCtrl *tree = args->tree;
1136    TreeElement elem = args->elem;
1137    ElementBorder *elemX = (ElementBorder *) elem;
1138    ElementBorder *masterX = (ElementBorder *) elem->master;
1139    int match, match2;
1140#ifdef DEPRECATED
1141    int draw1, draw2;
1142#endif
1143    Tk_3DBorder border1, border2;
1144    int relief1, relief2;
1145
1146    if (!args->states.visible2 || !args->states.draw2)
1147	return 0;
1148
1149#ifdef DEPRECATED
1150    BOOLEAN_FOR_STATE(draw1, draw, args->states.state1)
1151    BOOLEAN_FOR_STATE(draw2, draw, args->states.state2)
1152    if ((draw1 != 0) != (draw2 != 0))
1153	return CS_DISPLAY;
1154    if (draw2 == 0)
1155	return 0;
1156#endif
1157
1158    BORDER_FOR_STATE(border1, border, args->states.state1)
1159    BORDER_FOR_STATE(border2, border, args->states.state2)
1160    if (border1 != border2)
1161	return CS_DISPLAY;
1162
1163    RELIEF_FOR_STATE(relief1, relief, args->states.state1)
1164    RELIEF_FOR_STATE(relief2, relief, args->states.state2)
1165    if (relief1 != relief2)
1166	return CS_DISPLAY;
1167
1168    return 0;
1169}
1170
1171static int UndefProcBorder(TreeElementArgs *args)
1172{
1173    TreeCtrl *tree = args->tree;
1174    ElementBorder *elemX = (ElementBorder *) args->elem;
1175    int modified = 0;
1176
1177#ifdef DEPRECATED
1178    modified |= PerStateInfo_Undefine(tree, &pstBoolean, &elemX->draw, args->state);
1179#endif
1180    modified |= PerStateInfo_Undefine(tree, &pstBorder, &elemX->border, args->state);
1181    modified |= PerStateInfo_Undefine(tree, &pstRelief, &elemX->relief, args->state);
1182    return modified;
1183}
1184
1185static int ActualProcBorder(TreeElementArgs *args)
1186{
1187    TreeCtrl *tree = args->tree;
1188    ElementBorder *elemX = (ElementBorder *) args->elem;
1189    ElementBorder *masterX = (ElementBorder *) args->elem->master;
1190    static CONST char *optionName[] = {
1191	"-background",
1192#ifdef DEPRECATED
1193	"-draw",
1194#endif
1195	"-relief",
1196	(char *) NULL };
1197    int index, match, matchM;
1198    Tcl_Obj *obj = NULL;
1199
1200    if (Tcl_GetIndexFromObj(tree->interp, args->actual.obj, optionName,
1201		"option", 0, &index) != TCL_OK)
1202	return TCL_ERROR;
1203
1204    switch (index) {
1205	case 0: {
1206	    OBJECT_FOR_STATE(obj, pstBorder, border, args->state)
1207	    break;
1208	}
1209#ifdef DEPRECATED
1210	case 1: {
1211	    OBJECT_FOR_STATE(obj, pstBoolean, draw, args->state)
1212	    break;
1213	}
1214	case 2: {
1215	    OBJECT_FOR_STATE(obj, pstRelief, relief, args->state)
1216	    break;
1217	}
1218#else
1219	case 1: {
1220	    OBJECT_FOR_STATE(obj, pstRelief, relief, args->state)
1221	    break;
1222	}
1223#endif
1224    }
1225    if (obj != NULL)
1226	Tcl_SetObjResult(tree->interp, obj);
1227    return TCL_OK;
1228}
1229
1230TreeElementType treeElemTypeBorder = {
1231    "border",
1232    sizeof(ElementBorder),
1233    borderOptionSpecs,
1234    NULL,
1235    CreateProcBorder,
1236    DeleteProcBorder,
1237    ConfigProcBorder,
1238    DisplayProcBorder,
1239    NeededProcBorder,
1240    NULL, /* heightProc */
1241    WorldChangedProcBorder,
1242    StateProcBorder,
1243    UndefProcBorder,
1244    ActualProcBorder,
1245    NULL /* onScreenProc */
1246};
1247
1248/*****/
1249#if 0
1250
1251static CONST char *chkbutStateST[] = {
1252    "checked", "mixed", "normal", "active", "pressed", "disabled", (char *) NULL
1253};
1254
1255typedef struct ElementCheckButton ElementCheckButton;
1256
1257struct ElementCheckButton
1258{
1259    TreeElement_ header;
1260    PerStateInfo image;
1261    int state;
1262};
1263
1264#define CHKBUT_CONF_IMAGE 0x0001
1265#define CHKBUT_CONF_STATE 0x0002
1266
1267static Tk_OptionSpec chkbutOptionSpecs[] = {
1268    {TK_OPTION_STRING, "-image", (char *) NULL, (char *) NULL,
1269     (char *) NULL, Tk_Offset(ElementCheckButton, image.obj), -1,
1270     TK_OPTION_NULL_OK, (ClientData) NULL, CHKBUT_CONF_IMAGE},
1271    {TK_OPTION_STRING_TABLE, "-state", (char *) NULL, (char *) NULL,
1272     "normal", -1, Tk_Offset(ElementCheckButton, state),
1273     0, (ClientData) chkbutStateST, CHKBUT_CONF_STATE},
1274    {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
1275     (char *) NULL, 0, -1, 0, (ClientData) NULL, 0}
1276};
1277
1278static void DeleteProcCheckButton(TreeElementArgs *args)
1279{
1280    TreeCtrl *tree = args->tree;
1281    TreeElement elem = args->elem;
1282    ElementCheckButton *elemX = (ElementCheckButton *) elem;
1283
1284    PerStateInfo_Free(tree, &pstImage, &elemX->image);
1285}
1286
1287static int WorldChangedProcCheckButton(TreeElementArgs *args)
1288{
1289    int flagM = args->change.flagMaster;
1290    int flagS = args->change.flagSelf;
1291    int mask = 0;
1292
1293    if ((flagS | flagM) & (CHKBUT_CONF_IMAGE | CHKBUT_CONF_STATE))
1294	mask |= CS_DISPLAY | CS_LAYOUT;
1295
1296    return mask;
1297}
1298
1299static int ChkButStateFromObj(TreeCtrl *tree, Tcl_Obj *obj, int *stateOff, int *stateOn)
1300{
1301    Tcl_Interp *interp = tree->interp;
1302    int i, op = STATE_OP_ON, op2, op3, length, state = 0;
1303    char ch0, *string;
1304    int states[3];
1305
1306    states[STATE_OP_ON] = 0;
1307    states[STATE_OP_OFF] = 0;
1308    states[STATE_OP_TOGGLE] = 0;
1309
1310    string = Tcl_GetStringFromObj(obj, &length);
1311    if (length == 0)
1312	goto unknown;
1313    ch0 = string[0];
1314    if (ch0 == '!') {
1315	op = STATE_OP_OFF;
1316	++string;
1317	ch0 = string[0];
1318    } else if (ch0 == '~') {
1319	if (1) {
1320	    FormatResult(interp, "can't specify '~' for this command");
1321	    return TCL_ERROR;
1322	}
1323	op = STATE_OP_TOGGLE;
1324	++string;
1325	ch0 = string[0];
1326    }
1327    for (i = 0; chkbutStateST[i] != NULL; i++) {
1328	if ((ch0 == chkbutStateST[i][0]) && !strcmp(string, chkbutStateST[i])) {
1329	    state = 1L << i;
1330	    break;
1331	}
1332    }
1333    if (state == 0)
1334	goto unknown;
1335
1336    if (op == STATE_OP_ON) {
1337	op2 = STATE_OP_OFF;
1338	op3 = STATE_OP_TOGGLE;
1339    }
1340    else if (op == STATE_OP_OFF) {
1341	op2 = STATE_OP_ON;
1342	op3 = STATE_OP_TOGGLE;
1343    } else {
1344	op2 = STATE_OP_ON;
1345	op3 = STATE_OP_OFF;
1346    }
1347    states[op2] &= ~state;
1348    states[op3] &= ~state;
1349    states[op] |= state;
1350
1351    *stateOn |= states[STATE_OP_ON];
1352    *stateOff |= states[STATE_OP_OFF];
1353
1354    return TCL_OK;
1355
1356unknown:
1357    FormatResult(interp, "unknown state \"%s\"", string);
1358    return TCL_ERROR;
1359}
1360
1361static int ConfigProcCheckButton(TreeElementArgs *args)
1362{
1363    TreeCtrl *tree = args->tree;
1364    TreeElement elem = args->elem;
1365    ElementCheckButton *elemX = (ElementCheckButton *) elem;
1366    ElementCheckButton savedX;
1367    Tk_SavedOptions savedOptions;
1368    int error;
1369    Tcl_Obj *errorResult = NULL;
1370
1371    for (error = 0; error <= 1; error++) {
1372	if (error == 0) {
1373	    if (Tk_SetOptions(tree->interp, (char *) elemX,
1374			elem->typePtr->optionTable,
1375			args->config.objc, args->config.objv, tree->tkwin,
1376			&savedOptions, &args->config.flagSelf) != TCL_OK) {
1377		args->config.flagSelf = 0;
1378		continue;
1379	    }
1380
1381	    if (args->config.flagSelf & CHKBUT_CONF_IMAGE)
1382		PSTSave(&elemX->image, &savedX.image);
1383
1384	    if (args->config.flagSelf & CHKBUT_CONF_IMAGE) {
1385		if (PerStateInfo_FromObj(tree, ChkButStateFromObj, &pstImage, &elemX->image) != TCL_OK)
1386		    continue;
1387	    }
1388
1389	    if (args->config.flagSelf & CHKBUT_CONF_IMAGE)
1390		PerStateInfo_Free(tree, &pstImage, &savedX.image);
1391	    Tk_FreeSavedOptions(&savedOptions);
1392	    break;
1393	} else {
1394	    errorResult = Tcl_GetObjResult(tree->interp);
1395	    Tcl_IncrRefCount(errorResult);
1396	    Tk_RestoreSavedOptions(&savedOptions);
1397
1398	    if (args->config.flagSelf & CHKBUT_CONF_IMAGE)
1399		PSTRestore(tree, &pstImage, &elemX->image, &savedX.image);
1400
1401	    Tcl_SetObjResult(tree->interp, errorResult);
1402	    Tcl_DecrRefCount(errorResult);
1403	    return TCL_ERROR;
1404	}
1405    }
1406
1407    return TCL_OK;
1408}
1409
1410static int CreateProcCheckButton(TreeElementArgs *args)
1411{
1412    return TCL_OK;
1413}
1414
1415static void DisplayProcCheckButton(TreeElementArgs *args)
1416{
1417    TreeCtrl *tree = args->tree;
1418    TreeElement elem = args->elem;
1419    ElementCheckButton *elemX = (ElementCheckButton *) elem;
1420    ElementCheckButton *masterX = (ElementCheckButton *) elem->master;
1421    int state = args->state;
1422    int match, matchM;
1423    Tk_Image image;
1424    int imgW, imgH;
1425    int dx = 0, dy = 0;
1426
1427    image = PerStateImage_ForState(tree, &elemX->image, state, &match);
1428    if ((match != MATCH_EXACT) && (masterX != NULL)) {
1429	Tk_Image imageM = PerStateImage_ForState(tree, &masterX->image,
1430		state, &matchM);
1431	if (matchM > match)
1432	    image = imageM;
1433    }
1434
1435    if (image != NULL) {
1436	Tk_SizeOfImage(image, &imgW, &imgH);
1437	if (imgW < args->display.width)
1438	    dx = (args->display.width - imgW) / 2;
1439	else if (imgW > args->display.width)
1440	    imgW = args->display.width;
1441	if (imgH < args->display.height)
1442	    dy = (args->display.height - imgH) / 2;
1443	else if (imgH > args->display.height)
1444	    imgH = args->display.height;
1445	Tk_RedrawImage(image, 0, 0, imgW, imgH, args->display.drawable,
1446		args->display.x + dx,
1447		args->display.y + dy);
1448    }
1449}
1450
1451static void NeededProcCheckButton(TreeElementArgs *args)
1452{
1453    TreeCtrl *tree = args->tree;
1454    TreeElement elem = args->elem;
1455    ElementCheckButton *elemX = (ElementCheckButton *) elem;
1456    ElementCheckButton *masterX = (ElementCheckButton *) elem->master;
1457    int state = args->state;
1458    int match, match2;
1459    Tk_Image image;
1460    int width = 0, height = 0;
1461
1462    image = PerStateImage_ForState(tree, &elemX->image, state, &match);
1463    if ((match != MATCH_EXACT) && (masterX != NULL)) {
1464	Tk_Image image2 = PerStateImage_ForState(tree, &masterX->image,
1465		state, &match2);
1466	if (match2 > match)
1467	    image = image2;
1468    }
1469
1470    if (image != NULL)
1471	Tk_SizeOfImage(image, &width, &height);
1472
1473    args->layout.width = width;
1474    args->layout.height = height;
1475}
1476
1477static int StateProcCheckButton(TreeElementArgs *args)
1478{
1479    TreeCtrl *tree = args->tree;
1480    TreeElement elem = args->elem;
1481    ElementCheckButton *elemX = (ElementCheckButton *) elem;
1482    ElementCheckButton *masterX = (ElementCheckButton *) elem->master;
1483    int match, match2;
1484    Tk_Image image1, image2;
1485    int mask = 0;
1486
1487    image1 = PerStateImage_ForState(tree, &elemX->image,
1488	    args->states.state1, &match);
1489    if ((match != MATCH_EXACT) && (masterX != NULL)) {
1490	Tk_Image image = PerStateImage_ForState(tree, &masterX->image, args->states.state1, &match2);
1491	if (match2 > match)
1492	    image1 = image;
1493    }
1494
1495    image2 = PerStateImage_ForState(tree, &elemX->image,
1496	    args->states.state2, &match);
1497    if ((match != MATCH_EXACT) && (masterX != NULL)) {
1498	Tk_Image image = PerStateImage_ForState(tree, &masterX->image,
1499		args->states.state2, &match2);
1500	if (match2 > match)
1501	    image2 = image;
1502    }
1503
1504    if (image1 != image2) {
1505	mask |= CS_DISPLAY;
1506	if ((image1 != NULL) && (image2 != NULL)) {
1507	    int w1, h1, w2, h2;
1508	    Tk_SizeOfImage(image1, &w1, &h1);
1509	    Tk_SizeOfImage(image2, &w2, &h2);
1510	    if ((w1 != w2) || (h1 != h2))
1511		mask |= CS_LAYOUT;
1512	} else
1513	    mask |= CS_LAYOUT;
1514    }
1515
1516    return mask;
1517}
1518
1519static int UndefProcCheckButton(TreeElementArgs *args)
1520{
1521    TreeCtrl *tree = args->tree;
1522    ElementCheckButton *elemX = (ElementCheckButton *) args->elem;
1523
1524    return PerStateInfo_Undefine(tree, &pstImage, &elemX->image, args->state);
1525}
1526
1527static int ActualProcCheckButton(TreeElementArgs *args)
1528{
1529    TreeCtrl *tree = args->tree;
1530    ElementCheckButton *elemX = (ElementCheckButton *) args->elem;
1531    ElementCheckButton *masterX = (ElementCheckButton *) args->elem->master;
1532    static CONST char *optionName[] = {
1533	"-image",
1534	(char *) NULL };
1535    int index, match, matchM;
1536    Tcl_Obj *obj = NULL;
1537
1538    if (Tcl_GetIndexFromObj(tree->interp, args->actual.obj, optionName,
1539		"option", 0, &index) != TCL_OK)
1540	return TCL_ERROR;
1541
1542    switch (index) {
1543	case 0: {
1544	    obj = PerStateInfo_ObjForState(tree, &pstImage,
1545		    &elemX->image, args->state, &match);
1546	    if ((match != MATCH_EXACT) && (masterX != NULL)) {
1547		objM = PerStateInfo_ObjForState(tree, &pstImage,
1548			&masterX->image, args->state, &matchM);
1549		if (matchM > match)
1550		    obj = objM;
1551	    }
1552	    break;
1553	}
1554    }
1555    if (obj != NULL)
1556	Tcl_SetObjResult(tree->interp, obj);
1557    return TCL_OK;
1558}
1559
1560TreeElementType treeElemTypeCheckButton = {
1561    "checkbutton",
1562    sizeof(ElementCheckButton),
1563    chkbutOptionSpecs,
1564    NULL,
1565    CreateProcCheckButton,
1566    DeleteProcCheckButton,
1567    ConfigProcCheckButton,
1568    DisplayProcCheckButton,
1569    NeededProcCheckButton,
1570    NULL, /* heightProc */
1571    WorldChangedProcCheckButton,
1572    StateProcCheckButton,
1573    UndefProcCheckButton,
1574    ActualProcCheckButton,
1575    NULL /* onScreenProc */
1576};
1577
1578#endif
1579
1580/*****/
1581
1582typedef struct ElementImage ElementImage;
1583
1584struct ElementImage
1585{
1586    TreeElement_ header;
1587    PerStateInfo image;
1588};
1589
1590typedef struct ElementImageSize
1591{
1592    int width;
1593    Tcl_Obj *widthObj;
1594    int height;
1595    Tcl_Obj *heightObj;
1596} ElementImageSize;
1597
1598#define IMAGE_CONF_IMAGE 0x0001
1599#define IMAGE_CONF_SIZE 0x0002
1600#define IMAGE_CONF_DISPLAY 0x0004
1601#ifdef DEPRECATED
1602#define IMAGE_CONF_DRAW 0x0008
1603#endif
1604
1605static Tk_OptionSpec imageOptionSpecs[] = {
1606#ifdef DEPRECATED
1607    {TK_OPTION_CUSTOM, "-draw", (char *) NULL, (char *) NULL,
1608     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
1609     TK_OPTION_NULL_OK, (ClientData) NULL, IMAGE_CONF_DRAW},
1610#endif
1611    {TK_OPTION_CUSTOM, "-height", (char *) NULL, (char *) NULL,
1612     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
1613     TK_OPTION_NULL_OK, (ClientData) NULL, IMAGE_CONF_SIZE},
1614    {TK_OPTION_CUSTOM, "-image", (char *) NULL, (char *) NULL,
1615     (char *) NULL,
1616     Tk_Offset(ElementImage, image.obj), Tk_Offset(ElementImage, image),
1617     TK_OPTION_NULL_OK, (ClientData) NULL, IMAGE_CONF_IMAGE},
1618    {TK_OPTION_CUSTOM, "-tiled", (char *) NULL, (char *) NULL,
1619     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
1620     TK_OPTION_NULL_OK, (ClientData) NULL, IMAGE_CONF_DISPLAY},
1621    {TK_OPTION_CUSTOM, "-width", (char *) NULL, (char *) NULL,
1622     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
1623     TK_OPTION_NULL_OK, (ClientData) NULL, IMAGE_CONF_SIZE},
1624    {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
1625     (char *) NULL, 0, -1, 0, (ClientData) NULL, 0}
1626};
1627
1628static void DeleteProcImage(TreeElementArgs *args)
1629{
1630/*    TreeCtrl *tree = args->tree;
1631    TreeElement elem = args->elem;
1632    ElementImage *elemX = (ElementImage *) elem;*/
1633}
1634
1635static int WorldChangedProcImage(TreeElementArgs *args)
1636{
1637    int flagM = args->change.flagMaster;
1638    int flagS = args->change.flagSelf;
1639    int mask = 0;
1640
1641    if ((flagS | flagM) & (
1642#ifdef DEPRECATED
1643	    IMAGE_CONF_DRAW |
1644#endif
1645	    IMAGE_CONF_IMAGE | IMAGE_CONF_SIZE))
1646	mask |= CS_DISPLAY | CS_LAYOUT;
1647    if ((flagS | flagM) & IMAGE_CONF_DISPLAY)
1648	mask |= CS_DISPLAY;
1649
1650    return mask;
1651}
1652
1653static int ConfigProcImage(TreeElementArgs *args)
1654{
1655    TreeCtrl *tree = args->tree;
1656    TreeElement elem = args->elem;
1657    ElementImage *elemX = (ElementImage *) elem;
1658    Tk_SavedOptions savedOptions;
1659    int error;
1660    Tcl_Obj *errorResult = NULL;
1661
1662    for (error = 0; error <= 1; error++) {
1663	if (error == 0) {
1664	    if (Tk_SetOptions(tree->interp, (char *) elemX,
1665			elem->typePtr->optionTable,
1666			args->config.objc, args->config.objv, tree->tkwin,
1667			&savedOptions, &args->config.flagSelf) != TCL_OK) {
1668		args->config.flagSelf = 0;
1669		continue;
1670	    }
1671
1672	    Tk_FreeSavedOptions(&savedOptions);
1673	    break;
1674	} else {
1675	    errorResult = Tcl_GetObjResult(tree->interp);
1676	    Tcl_IncrRefCount(errorResult);
1677	    Tk_RestoreSavedOptions(&savedOptions);
1678
1679	    Tcl_SetObjResult(tree->interp, errorResult);
1680	    Tcl_DecrRefCount(errorResult);
1681	    return TCL_ERROR;
1682	}
1683    }
1684
1685    return TCL_OK;
1686}
1687
1688static int CreateProcImage(TreeElementArgs *args)
1689{
1690/*    ElementImage *elemX = (ElementImage *) args->elem;*/
1691
1692    return TCL_OK;
1693}
1694
1695static void DisplayProcImage(TreeElementArgs *args)
1696{
1697    TreeCtrl *tree = args->tree;
1698    TreeElement elem = args->elem;
1699    ElementImage *elemX = (ElementImage *) elem;
1700    ElementImage *masterX = (ElementImage *) elem->master;
1701    int state = args->state;
1702    int x = args->display.x, y = args->display.y;
1703    int width, height;
1704    int match, match2;
1705#ifdef DEPRECATED
1706    int draw;
1707#endif
1708    Tk_Image image;
1709    int imgW, imgH;
1710    int tiled = 0, *eit, *eitM = NULL;
1711
1712#ifdef DEPRECATED
1713    draw = DO_BooleanForState(tree, elem, 1002, state);
1714    if (!draw)
1715	return;
1716#endif
1717
1718    IMAGE_FOR_STATE(image, image, state)
1719    if (image == NULL)
1720	return;
1721
1722    eit = DynamicOption_FindData(elem->options, 1003);
1723    if (masterX != NULL)
1724	eitM = DynamicOption_FindData(elem->master->options, 1003);
1725
1726    if (eit != NULL && *eit != -1)
1727	tiled = *eit;
1728    else if ((eitM != NULL) && (*eitM != -1))
1729	tiled = *eitM;
1730    if (tiled) {
1731	Tree_DrawTiledImage(tree, args->display.drawable, image, x, y,
1732	    x + args->display.width, y + args->display.height, -x, -y);
1733	return;
1734    }
1735
1736    Tk_SizeOfImage(image, &imgW, &imgH);
1737    width = imgW, height = imgH;
1738    AdjustForSticky(args->display.sticky,
1739	args->display.width, args->display.height,
1740	FALSE, FALSE,
1741	&x, &y, &width, &height);
1742    if (imgW > args->display.width)
1743	imgW = args->display.width;
1744    if (imgH > args->display.height)
1745	imgH = args->display.height;
1746    Tree_RedrawImage(image, 0, 0, imgW, imgH, args->display.td, x, y);
1747}
1748
1749static void NeededProcImage(TreeElementArgs *args)
1750{
1751    TreeCtrl *tree = args->tree;
1752    TreeElement elem = args->elem;
1753    ElementImage *elemX = (ElementImage *) elem;
1754    ElementImage *masterX = (ElementImage *) elem->master;
1755    int state = args->state;
1756    int width = 0, height = 0;
1757    int match, match2;
1758    Tk_Image image;
1759    ElementImageSize *eis, *eisM = NULL;
1760
1761    IMAGE_FOR_STATE(image, image, state)
1762
1763    if (image != NULL)
1764	Tk_SizeOfImage(image, &width, &height);
1765
1766    eis = DynamicOption_FindData(elem->options, 1001);
1767    if (masterX != NULL)
1768	eisM = DynamicOption_FindData(elem->master->options, 1001);
1769
1770    if (eis != NULL && eis->widthObj != NULL)
1771	width = eis->width;
1772    else if ((eisM != NULL) && (eisM->widthObj != NULL))
1773	width = eisM->width;
1774
1775    if (eis != NULL && eis->heightObj != NULL)
1776	height = eis->height;
1777    else if ((eisM != NULL) && (eisM->heightObj != NULL))
1778	height = eisM->height;
1779
1780    args->needed.width = width;
1781    args->needed.height = height;
1782}
1783
1784static int StateProcImage(TreeElementArgs *args)
1785{
1786    TreeCtrl *tree = args->tree;
1787    TreeElement elem = args->elem;
1788    ElementImage *elemX = (ElementImage *) elem;
1789    ElementImage *masterX = (ElementImage *) elem->master;
1790    int match, match2;
1791#ifdef DEPRECATED
1792    int draw1, draw2;
1793#endif
1794    Tk_Image image1, image2;
1795
1796    if (!args->states.visible2)
1797	return 0;
1798
1799    IMAGE_FOR_STATE(image1, image, args->states.state1)
1800    IMAGE_FOR_STATE(image2, image, args->states.state2)
1801
1802    if (image1 != image2) {
1803	if ((image1 != NULL) && (image2 != NULL)) {
1804	    int w1, h1, w2, h2;
1805	    Tk_SizeOfImage(image1, &w1, &h1);
1806	    Tk_SizeOfImage(image2, &w2, &h2);
1807	    if ((w1 != w2) || (h1 != h2))
1808		return CS_DISPLAY | CS_LAYOUT;
1809	    return CS_DISPLAY;
1810	}
1811	return CS_DISPLAY | CS_LAYOUT;
1812    }
1813
1814    if (!args->states.draw2)
1815	return 0;
1816#ifdef DEPRECATED
1817    draw1 = DO_BooleanForState(tree, elem, 1002, args->states.state1);
1818    draw2 = DO_BooleanForState(tree, elem, 1002, args->states.state2);
1819    if ((draw1 != 0) != (draw2 != 0))
1820	return CS_DISPLAY;
1821#endif
1822
1823    return 0;
1824}
1825
1826static int UndefProcImage(TreeElementArgs *args)
1827{
1828    TreeCtrl *tree = args->tree;
1829    TreeElement elem = args->elem;
1830    ElementImage *elemX = (ElementImage *) elem;
1831    int modified = 0;
1832#ifdef DEPRECATED
1833    PerStateInfo *psi;
1834#endif
1835
1836#ifdef DEPRECATED
1837    if ((psi = DynamicOption_FindData(elem->options, 1002)) != NULL)
1838	modified |= PerStateInfo_Undefine(tree, &pstBoolean, psi, args->state);
1839#endif
1840    modified |= PerStateInfo_Undefine(tree, &pstImage, &elemX->image, args->state);
1841    return modified;
1842}
1843
1844static int ActualProcImage(TreeElementArgs *args)
1845{
1846    TreeCtrl *tree = args->tree;
1847    ElementImage *elemX = (ElementImage *) args->elem;
1848    ElementImage *masterX = (ElementImage *) args->elem->master;
1849    static CONST char *optionName[] = {
1850#ifdef DEPRECATED
1851	"-draw",
1852#endif
1853	"-image",
1854	(char *) NULL };
1855    int index, match, matchM;
1856    Tcl_Obj *obj = NULL;
1857
1858    if (Tcl_GetIndexFromObj(tree->interp, args->actual.obj, optionName,
1859		"option", 0, &index) != TCL_OK)
1860	return TCL_ERROR;
1861
1862    switch (index) {
1863#ifdef DEPRECATED
1864	case 0: {
1865	    obj = DO_ObjectForState(tree, &pstBoolean, args->elem, 1002, args->state);
1866	    break;
1867	}
1868	case 1: {
1869	    OBJECT_FOR_STATE(obj, pstImage, image, args->state)
1870	    break;
1871	}
1872#else
1873	case 0: {
1874	    OBJECT_FOR_STATE(obj, pstImage, image, args->state)
1875	    break;
1876	}
1877#endif
1878    }
1879    if (obj != NULL)
1880	Tcl_SetObjResult(tree->interp, obj);
1881    return TCL_OK;
1882}
1883
1884TreeElementType treeElemTypeImage = {
1885    "image",
1886    sizeof(ElementImage),
1887    imageOptionSpecs,
1888    NULL,
1889    CreateProcImage,
1890    DeleteProcImage,
1891    ConfigProcImage,
1892    DisplayProcImage,
1893    NeededProcImage,
1894    NULL, /* heightProc */
1895    WorldChangedProcImage,
1896    StateProcImage,
1897    UndefProcImage,
1898    ActualProcImage,
1899    NULL /* onScreenProc */
1900};
1901
1902/*****/
1903
1904typedef struct ElementRect ElementRect;
1905
1906struct ElementRect
1907{
1908    TreeElement_ header;
1909#ifdef DEPRECATED
1910    PerStateInfo draw;
1911#endif
1912    int width;
1913    Tcl_Obj *widthObj;
1914    int height;
1915    Tcl_Obj *heightObj;
1916    PerStateInfo fill;
1917    PerStateInfo outline;
1918    int outlineWidth;
1919    Tcl_Obj *outlineWidthObj;
1920    int open;
1921    char *openString;
1922    int showFocus;
1923};
1924
1925#define RECT_CONF_FILL 0x0001
1926#define RECT_CONF_OUTLINE 0x0002
1927#define RECT_CONF_OUTWIDTH 0x0004
1928#define RECT_CONF_OPEN 0x0008
1929#define RECT_CONF_SIZE 0x0010
1930#define RECT_CONF_FOCUS 0x0020
1931#ifdef DEPRECATED
1932#define RECT_CONF_DRAW 0x0040
1933#endif
1934
1935static Tk_OptionSpec rectOptionSpecs[] = {
1936#ifdef DEPRECATED
1937    {TK_OPTION_CUSTOM, "-draw", (char *) NULL, (char *) NULL,
1938     (char *) NULL,
1939     Tk_Offset(ElementRect, draw.obj), Tk_Offset(ElementRect, draw),
1940     TK_OPTION_NULL_OK, (ClientData) NULL, RECT_CONF_DRAW},
1941#endif
1942    {TK_OPTION_CUSTOM, "-fill", (char *) NULL, (char *) NULL,
1943     (char *) NULL,
1944     Tk_Offset(ElementRect, fill.obj), Tk_Offset(ElementRect, fill),
1945     TK_OPTION_NULL_OK, (ClientData) NULL, RECT_CONF_FILL},
1946    {TK_OPTION_PIXELS, "-height", (char *) NULL, (char *) NULL,
1947     (char *) NULL, Tk_Offset(ElementRect, heightObj),
1948     Tk_Offset(ElementRect, height),
1949     TK_OPTION_NULL_OK, (ClientData) NULL, RECT_CONF_SIZE},
1950    {TK_OPTION_STRING, "-open", (char *) NULL, (char *) NULL,
1951     (char *) NULL, -1, Tk_Offset(ElementRect, openString),
1952     TK_OPTION_NULL_OK, (ClientData) NULL, RECT_CONF_OPEN},
1953    {TK_OPTION_CUSTOM, "-outline", (char *) NULL, (char *) NULL,
1954     (char *) NULL,
1955     Tk_Offset(ElementRect, outline.obj), Tk_Offset(ElementRect, outline),
1956     TK_OPTION_NULL_OK, (ClientData) NULL, RECT_CONF_OUTLINE},
1957    {TK_OPTION_PIXELS, "-outlinewidth", (char *) NULL, (char *) NULL,
1958     (char *) NULL, Tk_Offset(ElementRect, outlineWidthObj),
1959     Tk_Offset(ElementRect, outlineWidth),
1960     TK_OPTION_NULL_OK, (ClientData) NULL, RECT_CONF_OUTWIDTH},
1961    {TK_OPTION_CUSTOM, "-showfocus", (char *) NULL, (char *) NULL,
1962     (char *) NULL, -1, Tk_Offset(ElementRect, showFocus),
1963     TK_OPTION_NULL_OK, (ClientData) &booleanCO, RECT_CONF_FOCUS},
1964    {TK_OPTION_PIXELS, "-width", (char *) NULL, (char *) NULL,
1965     (char *) NULL, Tk_Offset(ElementRect, widthObj),
1966     Tk_Offset(ElementRect, width),
1967     TK_OPTION_NULL_OK, (ClientData) NULL, RECT_CONF_SIZE},
1968    {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
1969     (char *) NULL, 0, -1, 0, (ClientData) NULL, 0}
1970};
1971
1972static void DeleteProcRect(TreeElementArgs *args)
1973{
1974/*    TreeCtrl *tree = args->tree;
1975    ElementRect *elemX = (ElementRect *) args->elem;*/
1976}
1977
1978static int WorldChangedProcRect(TreeElementArgs *args)
1979{
1980    int flagM = args->change.flagMaster;
1981    int flagS = args->change.flagSelf;
1982    int mask = 0;
1983
1984    if ((flagS | flagM) & (RECT_CONF_SIZE | RECT_CONF_OUTWIDTH))
1985	mask |= CS_DISPLAY | CS_LAYOUT;
1986
1987    if ((flagS | flagM) & (
1988#ifdef DEPRECATED
1989	    RECT_CONF_DRAW |
1990#endif
1991	    RECT_CONF_FILL | RECT_CONF_OUTLINE | RECT_CONF_OPEN |
1992	    RECT_CONF_FOCUS))
1993	mask |= CS_DISPLAY;
1994
1995    return mask;
1996}
1997
1998static int ConfigProcRect(TreeElementArgs *args)
1999{
2000    TreeCtrl *tree = args->tree;
2001    TreeElement elem = args->elem;
2002    ElementRect *elemX = (ElementRect *) elem;
2003    ElementRect savedX;
2004    Tk_SavedOptions savedOptions;
2005    int error;
2006    Tcl_Obj *errorResult = NULL;
2007    int i;
2008
2009    savedX.open = 0; /* Prevent compiler warning */
2010
2011    for (error = 0; error <= 1; error++) {
2012	if (error == 0) {
2013	    if (Tk_SetOptions(tree->interp, (char *) elemX,
2014			elem->typePtr->optionTable,
2015			args->config.objc, args->config.objv, tree->tkwin,
2016			&savedOptions, &args->config.flagSelf) != TCL_OK) {
2017		args->config.flagSelf = 0;
2018		continue;
2019	    }
2020
2021	    if (args->config.flagSelf & RECT_CONF_OPEN)
2022		savedX.open = elemX->open;
2023
2024	    if (args->config.flagSelf & RECT_CONF_OPEN) {
2025		elemX->open = 0;
2026		if (elemX->openString != NULL) {
2027		    int badChar = 0;
2028
2029		    for (i = 0; elemX->openString[i]; i++) {
2030			switch (elemX->openString[i]) {
2031			    case 'w': case 'W': elemX->open |= 0x01; break;
2032			    case 'n': case 'N': elemX->open |= 0x02; break;
2033			    case 'e': case 'E': elemX->open |= 0x04; break;
2034			    case 's': case 'S': elemX->open |= 0x08; break;
2035			    default: {
2036				Tcl_ResetResult(tree->interp);
2037				Tcl_AppendResult(tree->interp, "bad open value \"",
2038					elemX->openString, "\": must be a string ",
2039					"containing zero or more of n, e, s, and w",
2040					(char *) NULL);
2041				badChar = 1;
2042				break;
2043			    }
2044			}
2045			if (badChar)
2046			    break;
2047		    }
2048		    if (badChar)
2049			continue;
2050		}
2051	    }
2052
2053	    Tk_FreeSavedOptions(&savedOptions);
2054	    break;
2055	} else {
2056	    errorResult = Tcl_GetObjResult(tree->interp);
2057	    Tcl_IncrRefCount(errorResult);
2058	    Tk_RestoreSavedOptions(&savedOptions);
2059
2060	    if (args->config.flagSelf & RECT_CONF_OPEN)
2061		elemX->open = savedX.open;
2062
2063	    Tcl_SetObjResult(tree->interp, errorResult);
2064	    Tcl_DecrRefCount(errorResult);
2065	    return TCL_ERROR;
2066	}
2067    }
2068
2069    return TCL_OK;
2070}
2071
2072static int CreateProcRect(TreeElementArgs *args)
2073{
2074    ElementRect *elemX = (ElementRect *) args->elem;
2075
2076    elemX->showFocus = -1;
2077    return TCL_OK;
2078}
2079
2080static void DisplayProcRect(TreeElementArgs *args)
2081{
2082    TreeCtrl *tree = args->tree;
2083    TreeElement elem = args->elem;
2084    ElementRect *elemX = (ElementRect *) elem;
2085    ElementRect *masterX = (ElementRect *) elem->master;
2086    int state = args->state;
2087    int x = args->display.x, y = args->display.y;
2088    int width = args->display.width, height = args->display.height;
2089    int match, match2;
2090#ifdef DEPRECATED
2091    int draw;
2092#endif
2093    XColor *color;
2094    int open = 0;
2095    int outlineWidth = 0;
2096    int showFocus = 0;
2097
2098#ifdef DEPRECATED
2099    BOOLEAN_FOR_STATE(draw, draw, state)
2100    if (!draw)
2101	return;
2102#endif
2103
2104    if (elemX->outlineWidthObj != NULL)
2105	outlineWidth = elemX->outlineWidth;
2106    else if ((masterX != NULL) && (masterX->outlineWidthObj != NULL))
2107	outlineWidth = masterX->outlineWidth;
2108
2109    if (elemX->openString != NULL)
2110	open = elemX->open;
2111    else if ((masterX != NULL) && (masterX->openString != NULL))
2112	open = masterX->open;
2113
2114    if (elemX->showFocus != -1)
2115	showFocus = elemX->showFocus;
2116    else if ((masterX != NULL) && (masterX->showFocus != -1))
2117	showFocus = masterX->showFocus;
2118
2119    if (elemX->widthObj != NULL)
2120	width = elemX->width;
2121    else if ((masterX != NULL) && (masterX->widthObj != NULL))
2122	width = masterX->width;
2123
2124    if (elemX->heightObj != NULL)
2125	height = elemX->height;
2126    else if ((masterX != NULL) && (masterX->heightObj != NULL))
2127	height = masterX->height;
2128
2129    AdjustForSticky(args->display.sticky,
2130	args->display.width, args->display.height,
2131	TRUE, TRUE,
2132	&x, &y, &width, &height);
2133
2134    COLOR_FOR_STATE(color, fill, state)
2135    if (color != NULL) {
2136	GC gc = Tk_GCForColor(color, Tk_WindowId(tree->tkwin));
2137	XFillRectangle(tree->display, args->display.drawable, gc,
2138		x, y, width, height);
2139    }
2140
2141    COLOR_FOR_STATE(color, outline, state)
2142    if ((color != NULL) && (outlineWidth > 0)) {
2143	GC gc = Tk_GCForColor(color, Tk_WindowId(tree->tkwin));
2144	if (!(open & 0x01))
2145	    XFillRectangle(tree->display, args->display.drawable, gc,
2146		    x, y, outlineWidth, height);
2147	if (!(open & 0x02))
2148	    XFillRectangle(tree->display, args->display.drawable, gc,
2149		    x, y, width, outlineWidth);
2150	if (!(open & 0x04))
2151	    XFillRectangle(tree->display, args->display.drawable, gc,
2152		    x + width - outlineWidth, y, outlineWidth, height);
2153	if (!(open & 0x08))
2154	    XFillRectangle(tree->display, args->display.drawable, gc,
2155		    x, y + height - outlineWidth, width, outlineWidth);
2156    }
2157
2158    if (showFocus && (state & STATE_FOCUS) && (state & STATE_ACTIVE)) {
2159	Tree_DrawActiveOutline(tree, args->display.drawable,
2160		args->display.x, args->display.y,
2161		args->display.width, args->display.height,
2162		open);
2163    }
2164}
2165
2166static void NeededProcRect(TreeElementArgs *args)
2167{
2168    TreeElement elem = args->elem;
2169    ElementRect *elemX = (ElementRect *) elem;
2170    ElementRect *masterX = (ElementRect *) elem->master;
2171    int width = 0, height = 0;
2172    int outlineWidth = 0;
2173
2174    if (elemX->outlineWidthObj != NULL)
2175	outlineWidth = elemX->outlineWidth;
2176    else if ((masterX != NULL) && (masterX->outlineWidthObj != NULL))
2177	outlineWidth = masterX->outlineWidth;
2178
2179    if (elemX->widthObj != NULL)
2180	width = elemX->width;
2181    else if ((masterX != NULL) && (masterX->widthObj != NULL))
2182	width = masterX->width;
2183
2184    if (elemX->heightObj != NULL)
2185	height = elemX->height;
2186    else if ((masterX != NULL) && (masterX->heightObj != NULL))
2187	height = masterX->height;
2188
2189    args->needed.width = MAX(width, outlineWidth * 2);
2190    args->needed.height = MAX(height, outlineWidth * 2);
2191}
2192
2193static int StateProcRect(TreeElementArgs *args)
2194{
2195    TreeCtrl *tree = args->tree;
2196    TreeElement elem = args->elem;
2197    ElementRect *elemX = (ElementRect *) elem;
2198    ElementRect *masterX = (ElementRect *) elem->master;
2199    int match, match2;
2200#ifdef DEPRECATED
2201    int draw1, draw2;
2202#endif
2203    XColor *f1, *f2;
2204    XColor *o1, *o2;
2205    int s1, s2;
2206    int showFocus = 0;
2207
2208    /* If either the -draw or -visible layout option is false for the
2209     * current state, then changes to colors etc don't warrant a redisplay. */
2210    if (!args->states.visible2 || !args->states.draw2)
2211	return 0;
2212
2213#ifdef DEPRECATED
2214    BOOLEAN_FOR_STATE(draw1, draw, args->states.state1)
2215    BOOLEAN_FOR_STATE(draw2, draw, args->states.state2)
2216    if ((draw1 != 0) != (draw2 != 0))
2217	return CS_DISPLAY;
2218    /* If the element isn't drawn, then changes to colors etc don't
2219     * warrant a redisplay. */
2220    if (draw2 == 0)
2221	return 0;
2222#endif
2223
2224    if (elemX->showFocus != -1)
2225	showFocus = elemX->showFocus;
2226    else if ((masterX != NULL) && (masterX->showFocus != -1))
2227	showFocus = masterX->showFocus;
2228
2229    s1 = showFocus &&
2230	(args->states.state1 & STATE_FOCUS) &&
2231	(args->states.state1 & STATE_ACTIVE);
2232    s2 = showFocus &&
2233	(args->states.state2 & STATE_FOCUS) &&
2234	(args->states.state2 & STATE_ACTIVE);
2235    if (s1 != s2)
2236	return CS_DISPLAY;
2237
2238    COLOR_FOR_STATE(f1, fill, args->states.state1)
2239    COLOR_FOR_STATE(f2, fill, args->states.state2)
2240    if (f1 != f2)
2241	return CS_DISPLAY;
2242
2243    COLOR_FOR_STATE(o1, outline, args->states.state1)
2244    COLOR_FOR_STATE(o2, outline, args->states.state2)
2245    if (o1 != o2)
2246	return CS_DISPLAY;
2247
2248    return 0;
2249}
2250
2251static int UndefProcRect(TreeElementArgs *args)
2252{
2253    TreeCtrl *tree = args->tree;
2254    ElementRect *elemX = (ElementRect *) args->elem;
2255    int modified = 0;
2256
2257#ifdef DEPRECATED
2258    modified |= PerStateInfo_Undefine(tree, &pstBoolean, &elemX->draw, args->state);
2259#endif
2260    modified |= PerStateInfo_Undefine(tree, &pstColor, &elemX->fill, args->state);
2261    modified |= PerStateInfo_Undefine(tree, &pstColor, &elemX->outline, args->state);
2262    return modified;
2263}
2264
2265static int ActualProcRect(TreeElementArgs *args)
2266{
2267    TreeCtrl *tree = args->tree;
2268    ElementRect *elemX = (ElementRect *) args->elem;
2269    ElementRect *masterX = (ElementRect *) args->elem->master;
2270    static CONST char *optionName[] = {
2271#ifdef DEPRECATED
2272	"-draw",
2273#endif
2274	"-fill", "-outline",
2275	(char *) NULL };
2276    int index, match, matchM;
2277    Tcl_Obj *obj = NULL;
2278
2279    if (Tcl_GetIndexFromObj(tree->interp, args->actual.obj, optionName,
2280		"option", 0, &index) != TCL_OK)
2281	return TCL_ERROR;
2282
2283    switch (index) {
2284#ifdef DEPRECATED
2285	case 0: {
2286	    OBJECT_FOR_STATE(obj, pstBoolean, draw, args->state)
2287	    break;
2288	}
2289	case 1: {
2290	    OBJECT_FOR_STATE(obj, pstColor, fill, args->state)
2291	    break;
2292	}
2293	case 2: {
2294	    OBJECT_FOR_STATE(obj, pstColor, outline, args->state)
2295	    break;
2296	}
2297#else
2298	case 0: {
2299	    OBJECT_FOR_STATE(obj, pstColor, fill, args->state)
2300	    break;
2301	}
2302	case 1: {
2303	    OBJECT_FOR_STATE(obj, pstColor, outline, args->state)
2304	    break;
2305	}
2306#endif
2307    }
2308    if (obj != NULL)
2309	Tcl_SetObjResult(tree->interp, obj);
2310    return TCL_OK;
2311}
2312
2313TreeElementType treeElemTypeRect = {
2314    "rect",
2315    sizeof(ElementRect),
2316    rectOptionSpecs,
2317    NULL,
2318    CreateProcRect,
2319    DeleteProcRect,
2320    ConfigProcRect,
2321    DisplayProcRect,
2322    NeededProcRect,
2323    NULL, /* heightProc */
2324    WorldChangedProcRect,
2325    StateProcRect,
2326    UndefProcRect,
2327    ActualProcRect,
2328    NULL /* onScreenProc */
2329};
2330
2331/*****/
2332
2333typedef struct ElementText ElementText;
2334
2335struct ElementText
2336{
2337    TreeElement_ header;
2338    char *textCfg;		/* -text */
2339    char *text;			/* This will be the same as textCfg, or it
2340				 * will be a dynamically allocated string
2341				 * from any -data or -textvariable. */
2342#define STRINGREP_INVALID -1
2343    int textLen;		/* Number of bytes (not characters) in the
2344				 * UTF-8 string. If -1, it means the string
2345				 * representation is invalid. */
2346};
2347#define TEXTVAR
2348
2349/* for Tk_SetOptions() */
2350#define TEXT_CONF_LAYOUT 0x0001
2351#define TEXT_CONF_DISPLAY 0x0002
2352#define TEXT_CONF_STRINGREP 0x0040
2353#ifdef TEXTVAR
2354#define TEXT_CONF_TEXTVAR 0x0080
2355#endif
2356
2357/*
2358 * Dynamic option ids for the text element.
2359 */
2360#define DOID_TEXT_VAR 1001
2361#define DOID_TEXT_DRAW 1002
2362#define DOID_TEXT_FILL 1003
2363#define DOID_TEXT_FONT 1004
2364#define DOID_TEXT_LAYOUT 1005
2365#define DOID_TEXT_DATA 1006
2366#define DOID_TEXT_LAYOUT2 1007
2367#define DOID_TEXT_STYLE 1008
2368#define DOID_TEXT_LAYOUT3 1009
2369
2370typedef struct ElementTextData {
2371    Tcl_Obj *dataObj;			/* -data */
2372#define TDT_NULL -1
2373#define TDT_DOUBLE 0
2374#define TDT_INTEGER 1
2375#define TDT_LONG 2
2376#define TDT_STRING 3
2377#define TDT_TIME 4
2378    int dataType;			/* -datatype */
2379    Tcl_Obj *formatObj;			/* -format */
2380} ElementTextData;
2381
2382typedef struct ElementTextLayout {
2383#define TK_JUSTIFY_NULL -1
2384    int justify;			/* -justify */
2385    int lines;				/* -lines */
2386    Tcl_Obj *widthObj;			/* -width */
2387    int width;				/* -width */
2388#define TEXT_WRAP_NULL -1
2389#define TEXT_WRAP_CHAR 0
2390#define TEXT_WRAP_NONE 1
2391#define TEXT_WRAP_WORD 2
2392    int wrap;				/* -wrap */
2393} ElementTextLayout;
2394
2395/* This structure doesn't hold any option values, but it is managed by
2396 * the dynamic-option code. */
2397typedef struct ElementTextLayout2 {
2398    TextLayout layout;
2399    int layoutWidth;
2400    int neededWidth;
2401    int totalWidth;
2402} ElementTextLayout2;
2403
2404typedef struct ElementTextLayout3 {
2405    Tcl_Obj *lMargin1Obj;		/* -lmargin1 */
2406    int lMargin1;			/* -lmargin2 */
2407    Tcl_Obj *lMargin2Obj;		/* -lmargin1 */
2408    int lMargin2;			/* -lmargin2 */
2409} ElementTextLayout3;
2410
2411#define TEXT_STYLE
2412#ifdef TEXT_STYLE
2413typedef struct ElementTextStyle {
2414    int underline;			/* -underline */
2415} ElementTextStyle;
2416
2417/* Called by the dynamic-option code when an ElementTextData is allocated. */
2418static void
2419ElementTextStyleInit(
2420    void *data
2421    )
2422{
2423    ElementTextStyle *ets = data;
2424
2425#define TEXT_UNDERLINE_EMPTYVAL -100000
2426    ets->underline = TEXT_UNDERLINE_EMPTYVAL;
2427}
2428#endif
2429
2430#ifdef TEXTVAR
2431typedef struct ElementTextVar {
2432    Tcl_Obj *varNameObj;		/* -textvariable */
2433    TreeCtrl *tree;			/* needed to redisplay */
2434    TreeItem item;			/* needed to redisplay */
2435    TreeItemColumn column;		/* needed to redisplay */
2436} ElementTextVar;
2437#endif
2438
2439/* Called by the dynamic-option code when an ElementTextData is allocated. */
2440static void
2441ElementTextDataInit(
2442    void *data
2443    )
2444{
2445    ElementTextData *etd = data;
2446
2447    etd->dataType = TDT_NULL;
2448}
2449
2450/* Called by the dynamic-option code when an ElementTextLayout is allocated. */
2451static void
2452ElementTextLayoutInit(
2453    void *data
2454    )
2455{
2456    ElementTextLayout *etl = data;
2457
2458    etl->justify = TK_JUSTIFY_NULL;
2459    etl->lines = -1;
2460    etl->wrap = TEXT_WRAP_NULL;
2461}
2462
2463static CONST char *textDataTypeST[] = { "double", "integer", "long", "string",
2464					"time", (char *) NULL };
2465static CONST char *textJustifyST[] = { "left", "right", "center", (char *) NULL };
2466static CONST char *textWrapST[] = { "char", "none", "word", (char *) NULL };
2467
2468static Tk_OptionSpec textOptionSpecs[] = {
2469    {TK_OPTION_CUSTOM, "-data", (char *) NULL, (char *) NULL,
2470     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
2471     TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_STRINGREP},
2472    {TK_OPTION_CUSTOM, "-datatype", (char *) NULL, (char *) NULL,
2473     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
2474     TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_STRINGREP},
2475#ifdef DEPRECATED
2476    {TK_OPTION_CUSTOM, "-draw", (char *) NULL, (char *) NULL,
2477     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
2478     TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_DISPLAY},
2479#endif
2480    {TK_OPTION_CUSTOM, "-fill", (char *) NULL, (char *) NULL,
2481     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
2482     TK_OPTION_NULL_OK, (ClientData) NULL,  TEXT_CONF_DISPLAY},
2483    {TK_OPTION_CUSTOM, "-font", (char *) NULL, (char *) NULL,
2484     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
2485     TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_LAYOUT},
2486    {TK_OPTION_CUSTOM, "-format", (char *) NULL, (char *) NULL,
2487     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
2488     TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_STRINGREP},
2489    {TK_OPTION_CUSTOM, "-justify", (char *) NULL, (char *) NULL,
2490     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
2491     TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_LAYOUT},
2492    {TK_OPTION_CUSTOM, "-lines", (char *) NULL, (char *) NULL,
2493     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
2494     TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_LAYOUT},
2495    {TK_OPTION_CUSTOM, "-lmargin1", (char *) NULL, (char *) NULL,
2496     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
2497     TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_LAYOUT},
2498    {TK_OPTION_CUSTOM, "-lmargin2", (char *) NULL, (char *) NULL,
2499     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
2500     TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_LAYOUT},
2501    {TK_OPTION_STRING, "-text", (char *) NULL, (char *) NULL,
2502     (char *) NULL, -1, Tk_Offset(ElementText, textCfg),
2503     TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_STRINGREP},
2504#ifdef TEXTVAR
2505    {TK_OPTION_CUSTOM, "-textvariable", (char *) NULL, (char *) NULL,
2506     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
2507     TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_STRINGREP | TEXT_CONF_TEXTVAR},
2508#endif
2509#ifdef TEXT_STYLE
2510    {TK_OPTION_CUSTOM, "-underline", (char *) NULL, (char *) NULL,
2511     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
2512     TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_DISPLAY},
2513#endif
2514    {TK_OPTION_CUSTOM, "-width", (char *) NULL, (char *) NULL,
2515     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
2516     TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_LAYOUT},
2517    {TK_OPTION_CUSTOM, "-wrap", (char *) NULL, (char *) NULL,
2518     (char *) NULL, -1, Tk_Offset(TreeElement_, options),
2519     TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_LAYOUT},
2520    {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
2521     (char *) NULL, 0, -1, 0, 0, 0}
2522};
2523
2524static int WorldChangedProcText(TreeElementArgs *args)
2525{
2526/*	TreeCtrl *tree = args->tree;*/
2527    TreeElement elem = args->elem;
2528    ElementText *elemX = (ElementText *) elem;
2529/*	ElementText *masterX = (ElementText *) elem->master;*/
2530    int flagT = args->change.flagTree;
2531    int flagM = args->change.flagMaster;
2532    int flagS = args->change.flagSelf;
2533    int mask = 0;
2534
2535    if ((flagS | flagM) & TEXT_CONF_STRINGREP) {
2536	elemX->textLen = STRINGREP_INVALID;
2537    }
2538
2539    if ((elemX->textLen == STRINGREP_INVALID) ||
2540	    ((flagS | flagM) & TEXT_CONF_LAYOUT) ||
2541	    /* Not needed if this element has its own font. */
2542	    (flagT & TREE_CONF_FONT)) {
2543	mask |= CS_DISPLAY | CS_LAYOUT;
2544    }
2545
2546    if ((flagS | flagM) & TEXT_CONF_DISPLAY)
2547	mask |= CS_DISPLAY;
2548
2549    return mask;
2550}
2551
2552static void TextUpdateStringRep(TreeElementArgs *args)
2553{
2554    TreeCtrl *tree = args->tree;
2555    TreeElement elem = args->elem;
2556    ElementText *elemX = (ElementText *) elem;
2557    ElementText *masterX = (ElementText *) elem->master;
2558    Tcl_Obj *dataObj, *formatObj;
2559    char *text;
2560    ElementTextData *etd, *etdM = NULL;
2561#ifdef TEXTVAR
2562    ElementTextVar *etv;
2563    Tcl_Obj *varNameObj;
2564#endif
2565    int dataType;
2566
2567    /* Free any string allocated as a result of -data or -textvariable. */
2568    if ((elemX->text != NULL) && (elemX->text != elemX->textCfg)) {
2569	ckfree(elemX->text);
2570    }
2571
2572    /* Forget any string, and mark the string rep as no-longer invalid. */
2573    elemX->text = NULL;
2574    elemX->textLen = 0;
2575
2576    /* If -text is specified, then -data and -textvariable are ignored. */
2577    if (elemX->textCfg != NULL) {
2578	elemX->text = elemX->textCfg;
2579	elemX->textLen = strlen(elemX->textCfg);
2580	return;
2581    }
2582
2583#ifdef TEXTVAR
2584    etv = DynamicOption_FindData(elem->options, DOID_TEXT_VAR);
2585    varNameObj = etv ? etv->varNameObj : NULL;
2586
2587    if (varNameObj != NULL) {
2588	Tcl_Obj *valueObj = Tcl_ObjGetVar2(tree->interp, varNameObj, NULL,
2589		TCL_GLOBAL_ONLY);
2590
2591	if (valueObj == NULL) {
2592	    /* not possible I think */
2593	} else {
2594	    /* FIXME: do I need to allocate a copy, or can I just point
2595	     * to the internal rep of the string object? */
2596	    text = Tcl_GetStringFromObj(valueObj, &elemX->textLen);
2597	    if (elemX->textLen > 0) {
2598		elemX->text = ckalloc(elemX->textLen);
2599		memcpy(elemX->text, text, elemX->textLen);
2600	    }
2601	}
2602	return;
2603    }
2604#endif
2605
2606    etd = DynamicOption_FindData(elem->options, DOID_TEXT_DATA);
2607    if (masterX != NULL)
2608	etdM = DynamicOption_FindData(elem->master->options, DOID_TEXT_DATA);
2609
2610    dataObj = etd ? etd->dataObj : NULL;
2611    if ((dataObj == NULL) && (etdM != NULL))
2612	dataObj = etdM->dataObj;
2613
2614    dataType = etd ? etd->dataType : TDT_NULL;
2615    if ((dataType == TDT_NULL) && (etdM != NULL))
2616	dataType = etdM->dataType;
2617
2618    formatObj = etd ? etd->formatObj : NULL;
2619    if ((formatObj == NULL) && (etdM != NULL))
2620	formatObj = etdM->formatObj;
2621
2622    /* Only create a string rep if elemX (not masterX) has dataObj,
2623       dataType or formatObj. */
2624    if ((dataObj != NULL) && (dataType != TDT_NULL) &&
2625	    ((etd != NULL) && ((etd->dataObj != NULL) ||
2626		    (etd->dataType != TDT_NULL) ||
2627		    (etd->formatObj != NULL)))) {
2628	int i, objc = 0;
2629	Tcl_Obj *objv[5], *resultObj = NULL;
2630	static Tcl_Obj *staticObj[3] = { NULL };
2631	static Tcl_Obj *staticFormat[4] = { NULL };
2632	Tcl_ObjCmdProc *clockObjCmd = NULL, *formatObjCmd = NULL;
2633	ClientData clockClientData = NULL, formatClientData = NULL;
2634	Tcl_CmdInfo cmdInfo;
2635
2636	if (staticFormat[0] == NULL) {
2637	    staticFormat[0] = Tcl_NewStringObj("%g", -1);
2638	    staticFormat[1] = Tcl_NewStringObj("%d", -1);
2639	    staticFormat[2] = Tcl_NewStringObj("%ld", -1);
2640	    staticFormat[3] = Tcl_NewStringObj("%s", -1);
2641	    for (i = 0; i < 4; i++)
2642		Tcl_IncrRefCount(staticFormat[i]);
2643	}
2644	if (staticObj[0] == NULL) {
2645	    staticObj[0] = Tcl_NewStringObj("clock", -1);
2646	    staticObj[1] = Tcl_NewStringObj("format", -1);
2647	    staticObj[2] = Tcl_NewStringObj("-format", -1);
2648	    for (i = 0; i < 3; i++)
2649		Tcl_IncrRefCount(staticObj[i]);
2650	}
2651	if (Tcl_GetCommandInfo(tree->interp, "::clock", &cmdInfo) == 1) {
2652	    clockObjCmd = cmdInfo.objProc;
2653	    clockClientData = cmdInfo.objClientData;
2654	}
2655	if (Tcl_GetCommandInfo(tree->interp, "::format", &cmdInfo) == 1) {
2656	    formatObjCmd = cmdInfo.objProc;
2657	    formatClientData = cmdInfo.objClientData;
2658	}
2659
2660	/* Important to remove any shared result object, otherwise
2661	 * calls like Tcl_SetStringObj(Tcl_GetObjResult()) fail. */
2662	Tcl_ResetResult(tree->interp);
2663
2664	switch (dataType) {
2665	    case TDT_DOUBLE:
2666		if (formatObjCmd == NULL)
2667		    break;
2668		if (formatObj == NULL) formatObj = staticFormat[0];
2669		objv[objc++] = staticObj[1]; /* format */
2670		objv[objc++] = formatObj;
2671		objv[objc++] = dataObj;
2672		if (formatObjCmd(formatClientData, tree->interp, objc, objv)
2673			    == TCL_OK)
2674		    resultObj = Tcl_GetObjResult(tree->interp);
2675		break;
2676	    case TDT_INTEGER:
2677		if (formatObjCmd == NULL)
2678		    break;
2679		if (formatObj == NULL) formatObj = staticFormat[1];
2680		objv[objc++] = staticObj[1]; /* format */
2681		objv[objc++] = formatObj;
2682		objv[objc++] = dataObj;
2683		if (formatObjCmd(formatClientData, tree->interp, objc, objv)
2684			    == TCL_OK)
2685		    resultObj = Tcl_GetObjResult(tree->interp);
2686		break;
2687	    case TDT_LONG:
2688		if (formatObjCmd == NULL)
2689		    break;
2690		if (formatObj == NULL) formatObj = staticFormat[2];
2691		objv[objc++] = staticObj[1]; /* format */
2692		objv[objc++] = formatObj;
2693		objv[objc++] = dataObj;
2694		if (formatObjCmd(formatClientData, tree->interp, objc, objv)
2695			    == TCL_OK)
2696		    resultObj = Tcl_GetObjResult(tree->interp);
2697		break;
2698	    case TDT_STRING:
2699		if (formatObjCmd == NULL)
2700		    break;
2701		if (formatObj == NULL) formatObj = staticFormat[3];
2702		objv[objc++] = staticObj[1]; /* format */
2703		objv[objc++] = formatObj;
2704		objv[objc++] = dataObj;
2705		if (formatObjCmd(formatClientData, tree->interp, objc, objv)
2706			    == TCL_OK)
2707		    resultObj = Tcl_GetObjResult(tree->interp);
2708		break;
2709	    case TDT_TIME:
2710		if (clockObjCmd == NULL)
2711		    break;
2712		objv[objc++] = staticObj[0];
2713		objv[objc++] = staticObj[1];
2714		objv[objc++] = dataObj;
2715		if (formatObj != NULL) {
2716		    objv[objc++] = staticObj[2];
2717		    objv[objc++] = formatObj;
2718		}
2719		if (clockObjCmd(clockClientData, tree->interp, objc, objv)
2720			    == TCL_OK)
2721		    resultObj = Tcl_GetObjResult(tree->interp);
2722		break;
2723	    default:
2724		panic("unknown ElementText dataType");
2725		break;
2726	}
2727
2728	if (resultObj != NULL) {
2729	    text = Tcl_GetStringFromObj(resultObj, &elemX->textLen);
2730	    if (elemX->textLen > 0) {
2731		elemX->text = ckalloc(elemX->textLen);
2732		memcpy(elemX->text, text, elemX->textLen);
2733	    }
2734	}
2735    }
2736}
2737
2738static ElementTextLayout2 *
2739TextUpdateLayout(
2740    char *func,
2741    TreeElementArgs *args,
2742    int fixedWidth,
2743    int maxWidth
2744    )
2745{
2746    TreeCtrl *tree = args->tree;
2747    TreeElement elem = args->elem;
2748    ElementText *elemX = (ElementText *) elem;
2749    ElementText *masterX = (ElementText *) elem->master;
2750    int state = args->state;
2751    Tk_Font tkfont;
2752    char *text = NULL;
2753    int textLen = 0;
2754    int justify = TK_JUSTIFY_LEFT;
2755    int lines = 0;
2756    int wrap = TEXT_WRAP_WORD;
2757    int width = 0;
2758    int flags = 0;
2759    int i, multiLine = FALSE;
2760    int textWidth;
2761    ElementTextLayout *etl, *etlM = NULL;
2762    ElementTextLayout2 *etl2;
2763    ElementTextLayout3 *etl3, *etl3M = NULL;
2764    DynamicOption *opt;
2765    int lMargin1 = 0, lMargin2 = 0;
2766
2767    if (tree->debug.enable && tree->debug.textLayout)
2768	dbwin("TextUpdateLayout: %s %p (%s) %s\n    fixedWidth %d maxWidth %d\n",
2769	    Tk_PathName(tree->tkwin), elemX, masterX ? "instance" : "master",
2770	    func, fixedWidth, maxWidth);
2771
2772    etl2 = DynamicOption_FindData(elem->options, DOID_TEXT_LAYOUT2);
2773    if (etl2 != NULL && etl2->layout != NULL) {
2774	if (tree->debug.enable && tree->debug.textLayout)
2775	    dbwin("    FREE\n");
2776	TextLayout_Free(etl2->layout);
2777	etl2->layout = NULL;
2778    }
2779
2780    if (elemX->text != NULL) {
2781	text = elemX->text;
2782	textLen = elemX->textLen;
2783    } else if ((masterX != NULL) && (masterX->text != NULL)) {
2784	text = masterX->text;
2785	textLen = masterX->textLen;
2786    }
2787    if ((text == NULL) || (textLen == 0))
2788	return etl2;
2789
2790    etl = DynamicOption_FindData(elem->options, DOID_TEXT_LAYOUT);
2791    if (masterX != NULL)
2792	etlM = DynamicOption_FindData(elem->master->options, DOID_TEXT_LAYOUT);
2793
2794    if (etl != NULL && etl->lines != -1)
2795	lines = etl->lines;
2796    else if (etlM != NULL && etlM->lines != -1)
2797	lines = etlM->lines;
2798    if (lines == 1)
2799	return etl2;
2800
2801    tkfont = DO_FontForState(tree, elem, DOID_TEXT_FONT, state);
2802    if (tkfont == NULL)
2803	tkfont = tree->tkfont;
2804
2805    if (etl != NULL && etl->wrap != TEXT_WRAP_NULL)
2806	wrap = etl->wrap;
2807    else if (etlM != NULL && etlM->wrap != TEXT_WRAP_NULL)
2808	wrap = etlM->wrap;
2809
2810    if (wrap != TEXT_WRAP_NONE) {
2811	if (fixedWidth >= 0)
2812	    width = fixedWidth;
2813	else if (maxWidth >= 0)
2814	    width = maxWidth;
2815	if (etl != NULL && etl->widthObj != NULL) {
2816	    if (!width || (etl->width < width))
2817		width = etl->width;
2818	} else if ((etlM != NULL) && (etlM->widthObj != NULL)) {
2819	    if (!width || (etlM->width < width))
2820		width = etlM->width;
2821	}
2822    }
2823
2824    for (i = 0; i < textLen; i++) {
2825	if ((text[i] == '\n') || (text[i] == '\r')) {
2826	    multiLine = TRUE;
2827	    break;
2828	}
2829    }
2830    if (tree->debug.enable && tree->debug.textLayout)
2831	dbwin("    lines %d multiLine %d width %d wrap %s\n",
2832		lines, multiLine, width, textWrapST[wrap]);
2833    if (!multiLine) {
2834	if (width == 0)
2835	    return etl2;
2836	textWidth = Tk_TextWidth(tkfont, text, textLen);
2837if (tree->debug.enable && tree->debug.textLayout) dbwin("    available width %d textWidth %d\n", width, textWidth);
2838	if (width >= textWidth)
2839	    return etl2;
2840    }
2841
2842    if (etl != NULL && etl->justify != TK_JUSTIFY_NULL)
2843	justify = etl->justify;
2844    else if (etlM != NULL && etlM->justify != TK_JUSTIFY_NULL)
2845	justify = etlM->justify;
2846
2847    if (wrap == TEXT_WRAP_WORD)
2848	flags |= TK_WHOLE_WORDS;
2849
2850    if (etl2 == NULL) {
2851	opt = (DynamicOption *) DynamicOption_AllocIfNeeded(tree,
2852	    &elem->options, DOID_TEXT_LAYOUT2, sizeof(ElementTextLayout2), NULL);
2853	etl2 = (ElementTextLayout2 *) opt->data;
2854	/* It is possible that the needed size of this element does not
2855	 * require a TextLayout, in which case neededWidth never gets
2856	 * set. */
2857	etl2->neededWidth = -1;
2858    }
2859
2860    etl3 = DynamicOption_FindData(elem->options, DOID_TEXT_LAYOUT3);
2861    if (masterX != NULL)
2862	etl3M = DynamicOption_FindData(elem->master->options, DOID_TEXT_LAYOUT3);
2863    if (etl3 != NULL && etl3->lMargin1Obj != NULL)
2864	lMargin1 = etl3->lMargin1;
2865    else if (etl3M != NULL && etl3M->lMargin1Obj != NULL)
2866	lMargin1 = etl3M->lMargin1;
2867    if (etl3 != NULL && etl3->lMargin2Obj != NULL)
2868	lMargin2 = etl3->lMargin2;
2869    else if (etl3M != NULL && etl3M->lMargin2Obj != NULL)
2870	lMargin2 = etl3M->lMargin2;
2871
2872    etl2->layout = TextLayout_Compute(tkfont, text,
2873	    Tcl_NumUtfChars(text, textLen), width, justify, lines,
2874	    lMargin1, lMargin2, flags);
2875
2876    if (tree->debug.enable && tree->debug.textLayout)
2877	dbwin("    ALLOC\n");
2878    return etl2;
2879}
2880
2881#ifdef TEXTVAR
2882static Tcl_VarTraceProc VarTraceProc_Text;
2883
2884static void TextTraceSet(Tcl_Interp *interp, ElementText *elemX)
2885{
2886    ElementTextVar *etv = DynamicOption_FindData(elemX->header.options,
2887	DOID_TEXT_VAR);
2888    Tcl_Obj *varNameObj = etv ? etv->varNameObj : NULL;
2889
2890    if (varNameObj != NULL) {
2891	Tcl_TraceVar2(interp, Tcl_GetString(varNameObj),
2892	    NULL,
2893	    TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
2894	    VarTraceProc_Text, (ClientData) elemX);
2895    }
2896}
2897
2898static void TextTraceUnset(Tcl_Interp *interp, ElementText *elemX)
2899{
2900    ElementTextVar *etv = DynamicOption_FindData(elemX->header.options,
2901	DOID_TEXT_VAR);
2902    Tcl_Obj *varNameObj = etv ? etv->varNameObj : NULL;
2903
2904    if (varNameObj != NULL) {
2905	Tcl_UntraceVar2(interp, Tcl_GetString(varNameObj),
2906	    NULL,
2907	    TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
2908	    VarTraceProc_Text, (ClientData) elemX);
2909    }
2910}
2911
2912static char *VarTraceProc_Text(ClientData clientData, Tcl_Interp *interp,
2913    CONST char *name1, CONST char *name2, int flags)
2914{
2915    ElementText *elemX = (ElementText *) clientData;
2916    ElementTextVar *etv = DynamicOption_FindData(elemX->header.options,
2917	DOID_TEXT_VAR);
2918    Tcl_Obj *varNameObj = etv ? etv->varNameObj : NULL;
2919    Tcl_Obj *valueObj;
2920
2921    /*
2922     * If the variable is unset, then immediately recreate it unless
2923     * the whole interpreter is going away.
2924     */
2925
2926    if (flags & TCL_TRACE_UNSETS) {
2927	if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
2928	    /* Use the current string if it is valid. */
2929	    if (elemX->textLen > 0) {
2930		valueObj = Tcl_NewStringObj(elemX->text, elemX->textLen);
2931	    } else {
2932		valueObj = Tcl_NewStringObj("", 0);
2933	    }
2934	    Tcl_IncrRefCount(valueObj);
2935	    Tcl_ObjSetVar2(interp, varNameObj, NULL, valueObj,
2936		TCL_GLOBAL_ONLY);
2937	    Tcl_DecrRefCount(valueObj);
2938	    TextTraceSet(interp, elemX);
2939	}
2940	return (char *) NULL;
2941    }
2942
2943    elemX->textLen = STRINGREP_INVALID;
2944    Tree_ElementChangedItself(etv->tree, etv->item, etv->column,
2945	(TreeElement) elemX, TEXT_CONF_TEXTVAR, CS_LAYOUT | CS_DISPLAY);
2946    return (char *) NULL;
2947}
2948#endif /* TEXTVAR */
2949
2950static void DeleteProcText(TreeElementArgs *args)
2951{
2952    TreeCtrl *tree = args->tree;
2953    TreeElement elem = args->elem;
2954    ElementText *elemX = (ElementText *) elem;
2955    ElementTextLayout2 *etl2;
2956
2957    if ((elemX->textCfg == NULL) && (elemX->text != NULL)) {
2958	ckfree(elemX->text);
2959	elemX->text = NULL;
2960    }
2961    etl2 = DynamicOption_FindData(elem->options, DOID_TEXT_LAYOUT2);
2962    if (etl2 != NULL && etl2->layout != NULL)
2963	TextLayout_Free(etl2->layout);
2964    DynamicOption_Free1(tree, &elem->options, DOID_TEXT_LAYOUT2,
2965	sizeof(ElementTextLayout2));
2966#ifdef TEXTVAR
2967    TextTraceUnset(tree->interp, elemX);
2968#endif
2969}
2970
2971static int ConfigProcText(TreeElementArgs *args)
2972{
2973    TreeCtrl *tree = args->tree;
2974    Tcl_Interp *interp = tree->interp;
2975    TreeElement elem = args->elem;
2976    ElementText *elemX = (ElementText *) elem;
2977    Tk_SavedOptions savedOptions;
2978    int error;
2979    Tcl_Obj *errorResult = NULL;
2980    char *textCfg = elemX->textCfg;
2981#ifdef TEXTVAR
2982    ElementTextVar *etv;
2983    Tcl_Obj *varNameObj;
2984#endif
2985
2986#ifdef TEXTVAR
2987    TextTraceUnset(interp, elemX);
2988#endif
2989
2990    for (error = 0; error <= 1; error++) {
2991	if (error == 0) {
2992	    if (Tk_SetOptions(interp, (char *) elemX,
2993			elem->typePtr->optionTable,
2994			args->config.objc, args->config.objv, tree->tkwin,
2995			&savedOptions, &args->config.flagSelf) != TCL_OK) {
2996		args->config.flagSelf = 0;
2997		continue;
2998	    }
2999
3000#ifdef TEXTVAR
3001	    etv = DynamicOption_FindData(elem->options, DOID_TEXT_VAR);
3002	    if (etv != NULL) {
3003		etv->tree = tree;
3004		etv->item = args->config.item;
3005		etv->column = args->config.column;
3006		varNameObj = etv->varNameObj;
3007	    } else
3008		varNameObj = NULL;
3009	    if (varNameObj != NULL) {
3010		Tcl_Obj *valueObj;
3011		valueObj = Tcl_ObjGetVar2(interp, varNameObj,
3012			NULL, TCL_GLOBAL_ONLY);
3013		if (valueObj == NULL) {
3014		    valueObj = Tcl_NewStringObj("", 0);
3015		    Tcl_IncrRefCount(valueObj);
3016		    /* This validates the variable name. We get an error
3017		     * if it is the name of an array */
3018		    if (Tcl_ObjSetVar2(interp, varNameObj, NULL,
3019			    valueObj, TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG)
3020			    == NULL) {
3021			Tcl_DecrRefCount(valueObj);
3022			continue;
3023		    }
3024		    Tcl_DecrRefCount(valueObj);
3025		}
3026	    }
3027#endif
3028
3029	    Tk_FreeSavedOptions(&savedOptions);
3030	    break;
3031	} else {
3032	    errorResult = Tcl_GetObjResult(interp);
3033	    Tcl_IncrRefCount(errorResult);
3034	    Tk_RestoreSavedOptions(&savedOptions);
3035	}
3036    }
3037
3038    /* If -text was specified, then do not try to free the old value in
3039     * TextUpdateStringRep. */
3040    if (textCfg != elemX->textCfg && elemX->text == textCfg)
3041	elemX->text = NULL;
3042
3043#ifdef TEXTVAR
3044    TextTraceSet(interp, elemX);
3045#endif
3046
3047    if (error) {
3048	Tcl_SetObjResult(interp, errorResult);
3049	Tcl_DecrRefCount(errorResult);
3050	return TCL_ERROR;
3051    }
3052
3053    return TCL_OK;
3054}
3055
3056static int CreateProcText(TreeElementArgs *args)
3057{
3058/*    ElementText *elemX = (ElementText *) args->elem;*/
3059
3060    return TCL_OK;
3061}
3062
3063static ElementTextLayout2 *
3064TextRedoLayoutIfNeeded(
3065    char *func,
3066    TreeElementArgs *args,
3067    int fixedWidth
3068    )
3069{
3070    TreeElement elem = args->elem;
3071/*    ElementText *elemX = (ElementText *) elem;*/
3072    ElementText *masterX = (ElementText *) elem->master;
3073    int doLayout = 0;
3074    int wrap = TEXT_WRAP_WORD;
3075    ElementTextLayout *etl, *etlM = NULL;
3076    ElementTextLayout2 *etl2;
3077
3078    etl = DynamicOption_FindData(elem->options, DOID_TEXT_LAYOUT);
3079    if (masterX != NULL)
3080	etlM = DynamicOption_FindData(elem->master->options, DOID_TEXT_LAYOUT);
3081
3082    etl2 = DynamicOption_FindData(elem->options, DOID_TEXT_LAYOUT2);
3083
3084    /* If text wrapping is disabled, the layout doesn't change */
3085    if (etl != NULL && etl->wrap != TEXT_WRAP_NULL)
3086	wrap = etl->wrap;
3087    else if ((etlM != NULL) && (etlM->wrap != TEXT_WRAP_NULL))
3088	wrap = etlM->wrap;
3089    if (wrap == TEXT_WRAP_NONE)
3090	return etl2;
3091
3092    if (etl2 != NULL && etl2->layout != NULL) {
3093
3094	/* See comment in NeededProc about totalWidth */
3095	if ((etl2->neededWidth != -1) && (fixedWidth >= etl2->neededWidth))
3096	    fixedWidth = etl2->totalWidth;
3097
3098	/* Already layed out at this width */
3099	if (fixedWidth == etl2->layoutWidth)
3100	    return etl2;
3101    }
3102
3103    /* May switch from layout -> no layout or vice versa */
3104    if (etl2 == NULL || etl2->layout == NULL)
3105	doLayout = 1;
3106
3107    /* Width was constrained and we have more space now */
3108    else if ((etl2->layoutWidth != -1) && (fixedWidth > etl2->layoutWidth))
3109	doLayout = 1;
3110
3111    /* Width was unconstrained or we have less space now */
3112    else {
3113	int width;
3114	TextLayout_Size(etl2->layout, &width, NULL);
3115	/* Redo if we are narrower than the layout */
3116	if (fixedWidth < width)
3117	    doLayout = 1;
3118    }
3119    if (doLayout)
3120	etl2 = TextUpdateLayout(func, args, fixedWidth, -1);
3121    if (etl2 != NULL)
3122	etl2->layoutWidth = (etl2->layout != NULL) ? fixedWidth : -1;
3123    return etl2;
3124}
3125
3126static void DisplayProcText(TreeElementArgs *args)
3127{
3128    TreeCtrl *tree = args->tree;
3129    TreeElement elem = args->elem;
3130    ElementText *elemX = (ElementText *) elem;
3131    ElementText *masterX = (ElementText *) elem->master;
3132    int state = args->state;
3133    int x = args->display.x, y = args->display.y;
3134    int width, height;
3135#ifdef DEPRECATED
3136    int draw;
3137#endif
3138    XColor *color;
3139    char *text = elemX->text;
3140    int textLen = elemX->textLen;
3141    Tk_Font tkfont;
3142    TextLayout layout = NULL;
3143    Tk_FontMetrics fm;
3144    GC gc;
3145    int bytesThatFit, pixelsForText;
3146    char *ellipsis = "...";
3147    TkRegion clipRgn = NULL;
3148    ElementTextLayout2 *etl2;
3149#ifdef TEXT_STYLE
3150    ElementTextStyle *ets, *etsM = NULL;
3151    int underline = TEXT_UNDERLINE_EMPTYVAL;
3152#endif
3153
3154#ifdef DEPRECATED
3155    draw = DO_BooleanForState(tree, elem, DOID_TEXT_DRAW, state);
3156    if (!draw)
3157	return;
3158#endif
3159
3160    if ((text == NULL) && (masterX != NULL)) {
3161	text = masterX->text;
3162	textLen = masterX->textLen;
3163    }
3164
3165    if (text == NULL) /* always false (or layout sets height/width to zero) */
3166	return;
3167
3168    color = DO_ColorForState(tree, elem, DOID_TEXT_FILL, state);
3169    tkfont = DO_FontForState(tree, elem, DOID_TEXT_FONT, state);
3170
3171    /* FIXME: -font {"" {state...}}*/
3172    if ((color != NULL) || (tkfont != NULL)) {
3173	XGCValues gcValues;
3174	unsigned long gcMask = 0;
3175	if (color == NULL)
3176	    color = tree->fgColorPtr;
3177	gcValues.foreground = color->pixel;
3178	gcMask |= GCForeground;
3179	if (tkfont == NULL)
3180	    tkfont = tree->tkfont;
3181	gcValues.font = Tk_FontId(tkfont);
3182	gcMask |= GCFont;
3183	gcValues.graphics_exposures = False;
3184	gcMask |= GCGraphicsExposures;
3185	gc = Tree_GetGC(tree, gcMask, &gcValues);
3186    } else {
3187	tkfont = tree->tkfont;
3188	gc = tree->textGC;
3189    }
3190
3191#ifdef TEXT_STYLE
3192    ets = DynamicOption_FindData(elem->options, DOID_TEXT_STYLE);
3193    if (ets != NULL && ets->underline != TEXT_UNDERLINE_EMPTYVAL)
3194	underline = ets->underline;
3195    else if (masterX != NULL) {
3196	etsM = DynamicOption_FindData(elem->master->options, DOID_TEXT_STYLE);
3197	if (etsM != NULL && etsM->underline != TEXT_UNDERLINE_EMPTYVAL)
3198	    underline = etsM->underline;
3199    }
3200#endif
3201
3202    etl2 = TextRedoLayoutIfNeeded("DisplayProcText", args, args->display.width);
3203    if (etl2 != NULL && etl2->layout != NULL)
3204	layout = etl2->layout;
3205
3206    if (layout != NULL) {
3207	TextLayout_Size(layout, &width, &height);
3208	pixelsForText = width;
3209	/* Hack -- The actual size of the text may be slightly smaller than
3210	 * the available space when squeezed. If so we don't want to center
3211	 * the text horizontally */
3212	if ((etl2->neededWidth == -1) || (etl2->neededWidth > width))
3213	    width = args->display.width;
3214	AdjustForSticky(args->display.sticky,
3215	    args->display.width, args->display.height,
3216	    FALSE, FALSE,
3217	    &x, &y, &width, &height);
3218	/* Use clipping if text is larger than the display area. */
3219	if (pixelsForText > args->display.width || height > args->display.height) {
3220	    XRectangle rect;
3221	    clipRgn = Tree_GetRegion(tree);
3222	    rect.x = x;
3223	    rect.y = y;
3224	    rect.width = args->display.width;
3225	    rect.height = args->display.height;
3226	    TkUnionRectWithRegion(&rect, clipRgn, clipRgn);
3227	    TkSetRegion(tree->display, gc, clipRgn);
3228	}
3229	TextLayout_Draw(tree->display, args->display.drawable, gc,
3230		layout, x, y, 0, -1, underline);
3231	if (clipRgn != NULL) {
3232	    Tree_UnsetClipMask(tree, args->display.drawable, gc);
3233	    Tree_FreeRegion(tree, clipRgn);
3234	}
3235	return;
3236    }
3237
3238    Tk_GetFontMetrics(tkfont, &fm);
3239
3240    pixelsForText = args->display.width;
3241    bytesThatFit = Tree_Ellipsis(tkfont, text, textLen, &pixelsForText,
3242	    ellipsis, FALSE);
3243    width = pixelsForText, height = fm.linespace;
3244    /* Hack -- The actual size of the text may be slightly smaller than
3245    * the available space when squeezed. If so we don't want to center
3246    * the text horizontally */
3247    if (bytesThatFit != textLen)
3248	width = args->display.width;
3249    AdjustForSticky(args->display.sticky,
3250	args->display.width, args->display.height,
3251	FALSE, FALSE,
3252	&x, &y, &width, &height);
3253    /* Use clipping if text is larger than the display area. */
3254    if (pixelsForText > args->display.width || height > args->display.height) {
3255	XRectangle rect;
3256	clipRgn = Tree_GetRegion(tree);
3257	rect.x = x;
3258	rect.y = y;
3259	rect.width = args->display.width;
3260	rect.height = args->display.height;
3261	TkUnionRectWithRegion(&rect, clipRgn, clipRgn);
3262	TkSetRegion(tree->display, gc, clipRgn);
3263    }
3264    if (bytesThatFit != textLen) {
3265	char staticStr[256], *buf = staticStr;
3266	int bufLen = abs(bytesThatFit);
3267	int ellipsisLen = strlen(ellipsis);
3268
3269	if (bufLen + ellipsisLen > sizeof(staticStr))
3270	    buf = ckalloc(bufLen + ellipsisLen);
3271	memcpy(buf, text, bufLen);
3272	if (bytesThatFit > 0) {
3273	    memcpy(buf + bufLen, ellipsis, ellipsisLen);
3274	    bufLen += ellipsisLen;
3275	}
3276	Tk_DrawChars(tree->display, args->display.drawable, gc,
3277		tkfont, buf, bufLen, x, y + fm.ascent);
3278#ifdef TEXT_STYLE
3279	if (underline >= 0 && underline < Tcl_NumUtfChars(buf, abs(bytesThatFit))) {
3280	    CONST char *fstBytePtr = Tcl_UtfAtIndex(buf, underline);
3281	    CONST char *sndBytePtr = Tcl_UtfNext(fstBytePtr);
3282	    Tk_UnderlineChars(tree->display, args->display.drawable, gc,
3283		    tkfont, buf, x, y + fm.ascent,
3284		    fstBytePtr - buf, sndBytePtr - buf);
3285	}
3286#endif
3287	if (buf != staticStr)
3288	    ckfree(buf);
3289    } else {
3290	Tk_DrawChars(tree->display, args->display.drawable, gc,
3291		tkfont, text, textLen, x, y + fm.ascent);
3292#ifdef TEXT_STYLE
3293	if (underline >= 0 && underline < Tcl_NumUtfChars(text, textLen)) {
3294	    CONST char *fstBytePtr = Tcl_UtfAtIndex(text, underline);
3295	    CONST char *sndBytePtr = Tcl_UtfNext(fstBytePtr);
3296	    Tk_UnderlineChars(tree->display, args->display.drawable, gc,
3297		    tkfont, text, x, y + fm.ascent,
3298		    fstBytePtr - text, sndBytePtr - text);
3299	}
3300#endif
3301    }
3302    if (clipRgn != NULL) {
3303	Tree_UnsetClipMask(tree, args->display.drawable, gc);
3304	Tree_FreeRegion(tree, clipRgn);
3305    }
3306}
3307
3308static void NeededProcText(TreeElementArgs *args)
3309{
3310    TreeCtrl *tree = args->tree;
3311    TreeElement elem = args->elem;
3312    ElementText *elemX = (ElementText *) elem;
3313    ElementText *masterX = (ElementText *) elem->master;
3314    int state = args->state;
3315    char *text = NULL;
3316    int textLen = 0;
3317    Tk_Font tkfont;
3318    Tk_FontMetrics fm;
3319    int width = 0, height = 0;
3320    ElementTextLayout *etl, *etlM = NULL;
3321    ElementTextLayout2 *etl2;
3322
3323    etl = DynamicOption_FindData(args->elem->options, DOID_TEXT_LAYOUT);
3324    if (masterX != NULL)
3325	etlM = DynamicOption_FindData(args->elem->master->options, DOID_TEXT_LAYOUT);
3326
3327    if ((masterX != NULL) && (masterX->textLen == STRINGREP_INVALID)) {
3328	args->elem = (TreeElement) masterX;
3329	TextUpdateStringRep(args);
3330	args->elem = elem;
3331    }
3332    if (elemX->textLen == STRINGREP_INVALID) {
3333	TextUpdateStringRep(args);
3334    }
3335
3336    etl2 = TextUpdateLayout("NeededProcText", args, args->needed.fixedWidth,
3337	    args->needed.maxWidth);
3338    if (etl2 != NULL) {
3339	etl2->layoutWidth = -1;
3340	etl2->neededWidth = -1;
3341    }
3342
3343    if (etl2 != NULL && etl2->layout != NULL) {
3344	TextLayout_Size(etl2->layout, &width, &height);
3345	if (args->needed.fixedWidth >= 0)
3346	    etl2->layoutWidth = args->needed.fixedWidth;
3347	else if (args->needed.maxWidth >= 0)
3348	    etl2->layoutWidth = args->needed.maxWidth;
3349	etl2->neededWidth = width;
3350
3351	/*
3352	 * Hack -- If we call TextLayout_Compute() with the same width
3353	 * returned by TextLayout_Size(), we may get a different layout.
3354	 * I think this has to do with whitespace at the end of lines.
3355	 * So if HeightProc or DisplayProc is given neededWidth, I do the
3356	 * layout at totalWidth, not neededWidth.
3357	 */
3358	etl2->totalWidth = TextLayout_TotalWidth(etl2->layout);
3359    } else {
3360	if (elemX->text != NULL) {
3361	    text = elemX->text;
3362	    textLen = elemX->textLen;
3363	} else if ((masterX != NULL) && (masterX->text != NULL)) {
3364	    text = masterX->text;
3365	    textLen = masterX->textLen;
3366	}
3367	if (textLen > 0) {
3368	    int maxWidth = -1;
3369
3370	    tkfont = DO_FontForState(tree, elem, DOID_TEXT_FONT, state);
3371	    if (tkfont == NULL)
3372		tkfont = tree->tkfont;
3373
3374	    width = Tk_TextWidth(tkfont, text, textLen);
3375	    if (etl != NULL && etl->widthObj != NULL)
3376		maxWidth = etl->width;
3377	    else if ((etlM != NULL) && (etlM->widthObj != NULL))
3378		maxWidth = etlM->width;
3379	    if ((maxWidth >= 0) && (maxWidth < width))
3380		width = maxWidth;
3381
3382	    Tk_GetFontMetrics(tkfont, &fm);
3383	    height = fm.linespace;
3384	}
3385    }
3386
3387    args->needed.width = width;
3388    args->needed.height = height;
3389}
3390
3391static void HeightProcText(TreeElementArgs *args)
3392{
3393    TreeCtrl *tree = args->tree;
3394    TreeElement elem = args->elem;
3395    ElementText *elemX = (ElementText *) elem;
3396    ElementText *masterX = (ElementText *) elem->master;
3397    int state = args->state;
3398    int height = 0;
3399    char *text = NULL;
3400    int textLen = 0;
3401    Tk_Font tkfont;
3402    Tk_FontMetrics fm;
3403    ElementTextLayout2 *etl2;
3404
3405    etl2 = TextRedoLayoutIfNeeded("HeightProcText", args, args->height.fixedWidth);
3406
3407    if (etl2 != NULL && etl2->layout != NULL) {
3408	TextLayout_Size(etl2->layout, NULL, &height);
3409    } else {
3410	if (elemX->text != NULL) {
3411	    text = elemX->text;
3412	    textLen = elemX->textLen;
3413	} else if ((masterX != NULL) && (masterX->text != NULL)) {
3414	    text = masterX->text;
3415	    textLen = masterX->textLen;
3416	}
3417	if (textLen > 0) {
3418	    tkfont = DO_FontForState(tree, elem, DOID_TEXT_FONT, state);
3419	    if (tkfont == NULL)
3420		tkfont = tree->tkfont;
3421	    Tk_GetFontMetrics(tkfont, &fm);
3422	    height = fm.linespace;
3423	}
3424    }
3425
3426    args->height.height = height;
3427}
3428
3429int
3430TreeElement_GetSortData(
3431    TreeCtrl *tree,
3432    TreeElement elem,
3433    int type,
3434    long *lv,
3435    double *dv,
3436    char **sv)
3437{
3438    ElementText *elemX = (ElementText *) elem;
3439    ElementText *masterX = (ElementText *) elem->master;
3440    ElementTextData *etd, *etdM = NULL;
3441    Tcl_Obj *dataObj = NULL;
3442    int dataType = TDT_NULL;
3443
3444    etd = DynamicOption_FindData(elem->options, DOID_TEXT_DATA);
3445    if (etd != NULL) {
3446	dataObj = etd->dataObj;
3447	dataType = etd->dataType;
3448    }
3449    if (dataType == TDT_NULL && masterX != NULL) {
3450	etdM = DynamicOption_FindData(elem->master->options, DOID_TEXT_DATA);
3451	/* FIXME: get dataObj from master? */
3452	if (etdM != NULL)
3453	    dataType = etdM->dataType;
3454    }
3455
3456    switch (type) {
3457	case SORT_ASCII:
3458	case SORT_DICT:
3459	    if (dataObj != NULL && dataType != TDT_NULL)
3460		(*sv) = Tcl_GetString(dataObj);
3461	    else
3462		(*sv) = elemX->textCfg;
3463	    break;
3464	case SORT_DOUBLE:
3465	    if (dataObj != NULL && dataType == TDT_DOUBLE) {
3466		if (Tcl_GetDoubleFromObj(tree->interp, dataObj, dv) != TCL_OK)
3467		    return TCL_ERROR;
3468		break;
3469	    }
3470	    if (elemX->textCfg != NULL) {
3471		if (Tcl_GetDouble(tree->interp, elemX->textCfg, dv) != TCL_OK)
3472		    return TCL_ERROR;
3473		break;
3474	    }
3475	    FormatResult(tree->interp, "can't get a double from an empty -text value");
3476	    return TCL_ERROR;
3477	case SORT_LONG:
3478	    if (dataObj != NULL && dataType != TDT_NULL) {
3479		if (dataType == TDT_LONG || dataType == TDT_TIME) {
3480		    if (Tcl_GetLongFromObj(tree->interp, dataObj, lv) != TCL_OK)
3481			return TCL_ERROR;
3482		    break;
3483		}
3484		if (dataType == TDT_INTEGER) {
3485		    int iv;
3486		    if (Tcl_GetIntFromObj(tree->interp, dataObj, &iv) != TCL_OK)
3487			return TCL_ERROR;
3488		    (*lv) = iv;
3489		    break;
3490		}
3491	    }
3492	    if (elemX->textCfg != NULL) {
3493		Tcl_Obj obj;
3494
3495		obj.refCount = 1;
3496		obj.bytes = (char *) elemX->textCfg;
3497		obj.length = strlen(elemX->textCfg);
3498		obj.typePtr = NULL;
3499
3500		if (Tcl_GetLongFromObj(tree->interp, &obj, lv) != TCL_OK)
3501		    return TCL_ERROR;
3502		break;
3503	    }
3504	    FormatResult(tree->interp, "can't get a long from an empty -text value");
3505	    return TCL_ERROR;
3506    }
3507    return TCL_OK;
3508}
3509
3510static int StateProcText(TreeElementArgs *args)
3511{
3512    TreeCtrl *tree = args->tree;
3513    TreeElement elem = args->elem;
3514/*    ElementText *elemX = (ElementText *) elem;
3515    ElementText *masterX = (ElementText *) elem->master;*/
3516#ifdef DEPRECATED
3517    int draw1, draw2;
3518#endif
3519    XColor *f1, *f2;
3520    Tk_Font tkfont1, tkfont2;
3521
3522    if (!args->states.visible2)
3523	return 0;
3524
3525    tkfont1 = DO_FontForState(tree, elem, DOID_TEXT_FONT, args->states.state1);
3526    tkfont2 = DO_FontForState(tree, elem, DOID_TEXT_FONT, args->states.state2);
3527    if (tkfont1 != tkfont2)
3528	return CS_DISPLAY | CS_LAYOUT;
3529
3530    if (!args->states.draw2)
3531	return 0;
3532#ifdef DEPRECATED
3533    draw1 = DO_BooleanForState(tree, elem, DOID_TEXT_DRAW, args->states.state1);
3534    draw2 = DO_BooleanForState(tree, elem, DOID_TEXT_DRAW, args->states.state2);
3535    if ((draw1 != 0) != (draw2 != 0))
3536	return CS_DISPLAY;
3537    if (draw2 == 0)
3538	return 0;
3539#endif
3540
3541    f1 = DO_ColorForState(tree, elem, DOID_TEXT_FILL, args->states.state1);
3542    f2 = DO_ColorForState(tree, elem, DOID_TEXT_FILL, args->states.state2);
3543    if (f1 != f2)
3544	return CS_DISPLAY;
3545
3546    return 0;
3547}
3548
3549static int UndefProcText(TreeElementArgs *args)
3550{
3551    TreeCtrl *tree = args->tree;
3552/*    ElementText *elemX = (ElementText *) args->elem;*/
3553    int modified = 0;
3554    PerStateInfo *psi;
3555
3556    if ((psi = DynamicOption_FindData(args->elem->options, DOID_TEXT_DRAW)) != NULL)
3557	modified |= PerStateInfo_Undefine(tree, &pstBoolean, psi, args->state);
3558    if ((psi = DynamicOption_FindData(args->elem->options, DOID_TEXT_FILL)) != NULL)
3559	modified |= PerStateInfo_Undefine(tree, &pstColor, psi, args->state);
3560    if ((psi = DynamicOption_FindData(args->elem->options, DOID_TEXT_FONT)) != NULL)
3561	modified |= PerStateInfo_Undefine(tree, &pstFont, psi, args->state);
3562
3563    return modified;
3564}
3565
3566static int ActualProcText(TreeElementArgs *args)
3567{
3568    TreeCtrl *tree = args->tree;
3569/*    ElementText *elemX = (ElementText *) args->elem;
3570    ElementText *masterX = (ElementText *) args->elem->master;*/
3571    static CONST char *optionName[] = {
3572#ifdef DEPRECATED
3573	"-draw",
3574#endif
3575	"-fill", "-font",
3576	(char *) NULL };
3577    int index;
3578    Tcl_Obj *obj = NULL;
3579
3580    if (Tcl_GetIndexFromObj(tree->interp, args->actual.obj, optionName,
3581		"option", 0, &index) != TCL_OK)
3582	return TCL_ERROR;
3583
3584    switch (index) {
3585#ifdef DEPRECATED
3586	case 0: {
3587	    obj = DO_ObjectForState(tree, &pstBoolean, args->elem, DOID_TEXT_DRAW, args->state);
3588	    break;
3589	}
3590	case 1: {
3591	    obj = DO_ObjectForState(tree, &pstColor, args->elem, DOID_TEXT_FILL, args->state);
3592	    break;
3593	}
3594	case 2: {
3595	    obj = DO_ObjectForState(tree, &pstFont, args->elem, DOID_TEXT_FONT, args->state);
3596	    break;
3597	}
3598#else
3599	case 0: {
3600	    obj = DO_ObjectForState(tree, &pstColor, args->elem, DOID_TEXT_FILL, args->state);
3601	    break;
3602	}
3603	case 1: {
3604	    obj = DO_ObjectForState(tree, &pstFont, args->elem, DOID_TEXT_FONT, args->state);
3605	    break;
3606	}
3607#endif
3608    }
3609    if (obj != NULL)
3610	Tcl_SetObjResult(tree->interp, obj);
3611    return TCL_OK;
3612}
3613
3614TreeElementType treeElemTypeText = {
3615    "text",
3616    sizeof(ElementText),
3617    textOptionSpecs,
3618    NULL,
3619    CreateProcText,
3620    DeleteProcText,
3621    ConfigProcText,
3622    DisplayProcText,
3623    NeededProcText,
3624    HeightProcText,
3625    WorldChangedProcText,
3626    StateProcText,
3627    UndefProcText,
3628    ActualProcText,
3629    NULL /* onScreenProc */
3630};
3631
3632/*****/
3633
3634typedef struct ElementWindow ElementWindow;
3635
3636struct ElementWindow
3637{
3638    TreeElement_ header;
3639#ifdef DEPRECATED
3640    PerStateInfo draw;		/* -draw */
3641#endif
3642    TreeCtrl *tree;
3643    TreeItem item; 		/* Needed if window changes size */
3644    TreeItemColumn column; 	/* Needed if window changes size */
3645    Tk_Window tkwin;		/* Window associated with item.  NULL means
3646				 * window has been destroyed. */
3647    int destroy;		/* Destroy window when element
3648				 * is deleted */
3649#define CLIP_WINDOW 1
3650#ifdef CLIP_WINDOW
3651    int clip;			/* TRUE if tkwin is a borderless frame
3652				 * widget whose first child is the actual
3653				 * window we want displayed. */
3654    Tk_Window child;		/* The first child of tkwin. tkwin is resized
3655				 * so that it is never out-of-bounds, and
3656				 * the child is positioned within tkwin to
3657				 * provide clipping of the child. */
3658#endif
3659};
3660
3661#define EWIN_CONF_WINDOW 0x0001
3662#ifdef DEPRECATED
3663#define EWIN_CONF_DRAW 0x0002
3664#endif
3665
3666static Tk_OptionSpec windowOptionSpecs[] = {
3667#ifdef CLIP_WINDOW
3668    {TK_OPTION_CUSTOM, "-clip", (char *) NULL, (char *) NULL,
3669     (char *) NULL, -1, Tk_Offset(ElementWindow, clip),
3670     TK_OPTION_NULL_OK, (ClientData) &booleanCO, 0},
3671#endif
3672    {TK_OPTION_CUSTOM, "-destroy", (char *) NULL, (char *) NULL,
3673     (char *) NULL, -1, Tk_Offset(ElementWindow, destroy),
3674     TK_OPTION_NULL_OK, (ClientData) &booleanCO, 0},
3675#ifdef DEPRECATED
3676    {TK_OPTION_CUSTOM, "-draw", (char *) NULL, (char *) NULL,
3677     (char *) NULL,
3678     Tk_Offset(ElementWindow, draw.obj), Tk_Offset(ElementWindow, draw),
3679     TK_OPTION_NULL_OK, (ClientData) NULL, EWIN_CONF_DRAW},
3680#endif
3681    {TK_OPTION_WINDOW, "-window", (char *) NULL, (char *) NULL,
3682     (char *) NULL, -1, Tk_Offset(ElementWindow, tkwin),
3683     TK_OPTION_NULL_OK, (ClientData) NULL, EWIN_CONF_WINDOW},
3684    {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
3685     (char *) NULL, 0, -1, 0, (ClientData) NULL, 0}
3686};
3687
3688static void
3689WinItemStructureProc(
3690    ClientData clientData,	/* Pointer to record describing window elem. */
3691    XEvent *eventPtr)		/* Describes what just happened. */
3692{
3693    ElementWindow *elemX = clientData;
3694
3695    if (eventPtr->type == DestroyNotify) {
3696	elemX->tkwin = elemX->child = NULL;
3697	Tree_ElementChangedItself(elemX->tree, elemX->item, elemX->column,
3698	    (TreeElement) elemX, EWIN_CONF_WINDOW, CS_LAYOUT | CS_DISPLAY);
3699    }
3700}
3701
3702static void
3703WinItemRequestProc(
3704    ClientData clientData,		/* Pointer to record for window item. */
3705    Tk_Window tkwin)			/* Window that changed its desired
3706					 * size. */
3707{
3708    ElementWindow *elemX = clientData;
3709
3710#ifdef CLIP_WINDOW
3711    /* We don't care about size changes for the clip window. */
3712    if (elemX->child != NULL && tkwin != elemX->child)
3713	return;
3714#endif
3715    Tree_ElementChangedItself(elemX->tree, elemX->item, elemX->column,
3716	(TreeElement) elemX, EWIN_CONF_WINDOW, CS_LAYOUT | CS_DISPLAY);
3717}
3718
3719static void
3720WinItemLostSlaveProc(
3721    ClientData clientData,	/* WindowItem structure for slave window that
3722				 * was stolen away. */
3723    Tk_Window tkwin)		/* Tk's handle for the slave window. */
3724{
3725    ElementWindow *elemX = clientData;
3726    TreeCtrl *tree = elemX->tree;
3727
3728#ifdef CLIP_WINDOW
3729    /* If either window is lost to another geometry manager, forget
3730     * about both windows. */
3731    if (elemX->child != NULL) {
3732	Tk_DeleteEventHandler(elemX->child, StructureNotifyMask,
3733		WinItemStructureProc, (ClientData) elemX);
3734	if (tkwin != elemX->child) {
3735	    Tk_ManageGeometry(elemX->child, (Tk_GeomMgr *) NULL,
3736		    (ClientData) NULL);
3737	    Tk_UnmapWindow(elemX->child);
3738	}
3739	elemX->child = NULL;
3740    }
3741    if (elemX->tkwin != NULL) {
3742	Tk_DeleteEventHandler(elemX->tkwin, StructureNotifyMask,
3743		WinItemStructureProc, (ClientData) elemX);
3744	if (tkwin != elemX->tkwin) {
3745	    Tk_ManageGeometry(elemX->tkwin, (Tk_GeomMgr *) NULL,
3746		    (ClientData) NULL);
3747	    if (tree->tkwin != Tk_Parent(elemX->tkwin)) {
3748		Tk_UnmaintainGeometry(elemX->tkwin, tree->tkwin);
3749	    }
3750	    Tk_UnmapWindow(elemX->tkwin);
3751	}
3752	elemX->tkwin = NULL;
3753    }
3754#else
3755    Tk_DeleteEventHandler(elemX->tkwin, StructureNotifyMask,
3756	    WinItemStructureProc, (ClientData) elemX);
3757    if (tree->tkwin != Tk_Parent(elemX->tkwin)) {
3758	Tk_UnmaintainGeometry(elemX->tkwin, tree->tkwin);
3759    }
3760    Tk_UnmapWindow(elemX->tkwin);
3761    elemX->tkwin = NULL;
3762#endif
3763    Tree_ElementChangedItself(elemX->tree, elemX->item, elemX->column,
3764	(TreeElement) elemX, EWIN_CONF_WINDOW, CS_LAYOUT | CS_DISPLAY);
3765}
3766
3767static Tk_GeomMgr winElemGeomType = {
3768    "treectrl",				/* name */
3769    WinItemRequestProc,			/* requestProc */
3770    WinItemLostSlaveProc,		/* lostSlaveProc */
3771};
3772
3773static void DeleteProcWindow(TreeElementArgs *args)
3774{
3775    TreeCtrl *tree = args->tree;
3776    TreeElement elem = args->elem;
3777    ElementWindow *elemX = (ElementWindow *) elem;
3778    ElementWindow *masterX = (ElementWindow *) elem->master;
3779
3780    if (elemX->tkwin != NULL) {
3781#ifdef CLIP_WINDOW
3782	if (elemX->child != NULL) {
3783	    Tk_DeleteEventHandler(elemX->child, StructureNotifyMask,
3784		    WinItemStructureProc, (ClientData) elemX);
3785	    Tk_ManageGeometry(elemX->child, (Tk_GeomMgr *) NULL,
3786		    (ClientData) NULL);
3787	    Tk_UnmapWindow(elemX->child);
3788	    elemX->child = NULL;
3789	}
3790#endif
3791	Tk_DeleteEventHandler(elemX->tkwin, StructureNotifyMask,
3792		WinItemStructureProc, (ClientData) elemX);
3793	Tk_ManageGeometry(elemX->tkwin, (Tk_GeomMgr *) NULL,
3794		(ClientData) NULL);
3795	if (tree->tkwin != Tk_Parent(elemX->tkwin)) {
3796	    Tk_UnmaintainGeometry(elemX->tkwin, tree->tkwin);
3797	}
3798	Tk_UnmapWindow(elemX->tkwin);
3799
3800	if ((elemX->destroy == 1) ||
3801		((masterX != NULL) && (masterX->destroy == 1))) {
3802	    Tk_DestroyWindow(elemX->tkwin);
3803	}
3804
3805	elemX->tkwin = NULL;
3806    }
3807}
3808
3809static int WorldChangedProcWindow(TreeElementArgs *args)
3810{
3811    int flagM = args->change.flagMaster;
3812    int flagS = args->change.flagSelf;
3813    int mask = 0;
3814
3815#ifdef DEPRECATED
3816    if ((flagS | flagM) & (EWIN_CONF_DRAW))
3817	mask |= CS_DISPLAY;
3818#endif
3819
3820    if ((flagS | flagM) & (EWIN_CONF_WINDOW))
3821	mask |= CS_DISPLAY | CS_LAYOUT;
3822
3823    return mask;
3824}
3825
3826static int ConfigProcWindow(TreeElementArgs *args)
3827{
3828    TreeCtrl *tree = args->tree;
3829    TreeElement elem = args->elem;
3830    ElementWindow *elemX = (ElementWindow *) elem;
3831    ElementWindow *masterX = (ElementWindow *) elem->master;
3832    ElementWindow savedX;
3833    Tk_SavedOptions savedOptions;
3834    int error;
3835    Tcl_Obj *errorResult = NULL;
3836
3837    savedX.tkwin = elemX->tkwin;
3838
3839    for (error = 0; error <= 1; error++) {
3840	if (error == 0) {
3841	    if (Tk_SetOptions(tree->interp, (char *) elemX,
3842			elem->typePtr->optionTable,
3843			args->config.objc, args->config.objv, tree->tkwin,
3844			&savedOptions, &args->config.flagSelf) != TCL_OK) {
3845		args->config.flagSelf = 0;
3846		continue;
3847	    }
3848
3849	    if (args->config.flagSelf & EWIN_CONF_WINDOW) {
3850		if ((elem->master == NULL) && (elemX->tkwin != NULL)){
3851		    FormatResult(tree->interp, "can't specify -window for a master element");
3852		    continue;
3853		}
3854	    }
3855
3856	    Tk_FreeSavedOptions(&savedOptions);
3857	    break;
3858	} else {
3859	    errorResult = Tcl_GetObjResult(tree->interp);
3860	    Tcl_IncrRefCount(errorResult);
3861	    Tk_RestoreSavedOptions(&savedOptions);
3862
3863	    Tcl_SetObjResult(tree->interp, errorResult);
3864	    Tcl_DecrRefCount(errorResult);
3865	    return TCL_ERROR;
3866	}
3867    }
3868
3869    if (savedX.tkwin != elemX->tkwin) {
3870	if (savedX.tkwin != NULL) {
3871#ifdef CLIP_WINDOW
3872	    if (elemX->child != NULL) {
3873		Tk_DeleteEventHandler(elemX->child, StructureNotifyMask,
3874			WinItemStructureProc, (ClientData) elemX);
3875		Tk_ManageGeometry(elemX->child, (Tk_GeomMgr *) NULL,
3876			(ClientData) NULL);
3877		Tk_UnmapWindow(elemX->child);
3878		elemX->child = NULL;
3879	    }
3880#endif
3881	    Tk_DeleteEventHandler(savedX.tkwin, StructureNotifyMask,
3882		    WinItemStructureProc, (ClientData) elemX);
3883	    Tk_ManageGeometry(savedX.tkwin, (Tk_GeomMgr *) NULL,
3884		    (ClientData) NULL);
3885	    Tk_UnmaintainGeometry(savedX.tkwin, tree->tkwin);
3886	    Tk_UnmapWindow(savedX.tkwin);
3887	}
3888	if (elemX->tkwin != NULL) {
3889	    Tk_Window ancestor, parent;
3890
3891	    /*
3892	     * Make sure that the treectrl is either the parent of the
3893	     * window associated with the element or a descendant of that
3894	     * parent.  Also, don't allow a top-of-hierarchy window to be
3895	     * managed inside a treectrl.
3896	     */
3897
3898	    parent = Tk_Parent(elemX->tkwin);
3899	    for (ancestor = tree->tkwin; ;
3900		    ancestor = Tk_Parent(ancestor)) {
3901		if (ancestor == parent) {
3902		    break;
3903		}
3904		if (((Tk_FakeWin *) (ancestor))->flags & TK_TOP_HIERARCHY) {
3905		    badWindow:
3906		    FormatResult(tree->interp,
3907			    "can't use %s in a window element of %s",
3908			    Tk_PathName(elemX->tkwin),
3909			    Tk_PathName(tree->tkwin));
3910		    elemX->tkwin = NULL;
3911		    return TCL_ERROR;
3912		}
3913	    }
3914	    if (((Tk_FakeWin *) (elemX->tkwin))->flags & TK_TOP_HIERARCHY) {
3915		goto badWindow;
3916	    }
3917	    if (elemX->tkwin == tree->tkwin) {
3918		goto badWindow;
3919	    }
3920#ifdef CLIP_WINDOW
3921	    if ((elemX->clip == 1) || ((masterX != NULL) && (masterX->clip == 1))) {
3922		elemX->child = (Tk_Window) ((TkWindow *) elemX->tkwin)->childList;
3923		if (elemX->child != NULL) {
3924		    Tk_CreateEventHandler(elemX->child, StructureNotifyMask,
3925			    WinItemStructureProc, (ClientData) elemX);
3926		    Tk_ManageGeometry(elemX->child, &winElemGeomType,
3927			    (ClientData) elemX);
3928		}
3929	    }
3930#endif
3931	    Tk_CreateEventHandler(elemX->tkwin, StructureNotifyMask,
3932		    WinItemStructureProc, (ClientData) elemX);
3933	    Tk_ManageGeometry(elemX->tkwin, &winElemGeomType,
3934		    (ClientData) elemX);
3935	}
3936    }
3937#if 0
3938    if ((elemX->tkwin != NULL)
3939	    && (itemPtr->state == TK_STATE_HIDDEN)) {
3940	if (tree->tkwin == Tk_Parent(elemX->tkwin)) {
3941	    Tk_UnmapWindow(elemX->tkwin);
3942	} else {
3943	    Tk_UnmaintainGeometry(elemX->tkwin, tree->tkwin);
3944	}
3945    }
3946#endif
3947    return TCL_OK;
3948}
3949
3950static int CreateProcWindow(TreeElementArgs *args)
3951{
3952    TreeCtrl *tree = args->tree;
3953    TreeElement elem = args->elem;
3954    ElementWindow *elemX = (ElementWindow *) elem;
3955
3956    elemX->tree = tree;
3957    elemX->item = args->create.item;
3958    elemX->column = args->create.column;
3959    elemX->destroy = -1;
3960#ifdef CLIP_WINDOW
3961    elemX->clip = -1;
3962#endif
3963    return TCL_OK;
3964}
3965
3966static void DisplayProcWindow(TreeElementArgs *args)
3967{
3968    TreeCtrl *tree = args->tree;
3969    TreeElement elem = args->elem;
3970    ElementWindow *elemX = (ElementWindow *) elem;
3971    ElementWindow *masterX = (ElementWindow *) elem->master;
3972    int state = args->state;
3973    int x = args->display.x, y = args->display.y;
3974    int minX, maxX, minY, maxY;
3975    int width, height;
3976    int match, match2;
3977#ifdef DEPRECATED
3978    int draw;
3979#endif
3980    int requests;
3981
3982#ifdef DEPRECATED
3983    BOOLEAN_FOR_STATE(draw, draw, state);
3984    if (!draw)
3985	goto hideIt;
3986#endif
3987
3988    if (elemX->tkwin == NULL)
3989	return;
3990
3991#ifdef CLIP_WINDOW
3992    if (elemX->child != NULL) {
3993	width = Tk_ReqWidth(elemX->child);
3994	height = Tk_ReqHeight(elemX->child);
3995    } else {
3996	width = Tk_ReqWidth(elemX->tkwin);
3997	height = Tk_ReqHeight(elemX->tkwin);
3998    }
3999    if (width < 1 || height < 1)
4000	goto hideIt;
4001#else
4002    width = Tk_ReqWidth(elemX->tkwin);
4003    height = Tk_ReqHeight(elemX->tkwin);
4004#endif
4005    AdjustForSticky(args->display.sticky,
4006	args->display.width, args->display.height,
4007	TRUE, TRUE,
4008	&x, &y, &width, &height);
4009
4010    x += tree->drawableXOrigin - tree->xOrigin;
4011    y += tree->drawableYOrigin - tree->yOrigin;
4012
4013    /* -squeeze layout may give the element less space than requested. */
4014    if (width > args->display.width)
4015	width = args->display.width;
4016    if (height > args->display.height)
4017	height = args->display.height;
4018
4019    minX = args->display.bounds[0];
4020    minY = args->display.bounds[1];
4021    maxX = args->display.bounds[2];
4022    maxY = args->display.bounds[3];
4023
4024    /*
4025     * If the window is completely out of the visible area of the treectrl
4026     * then unmap it.  The window could suddenly reappear if the treectrl
4027     * window gets resized.
4028     */
4029
4030    if (((x + width) <= minX) || ((y + height) <= minY)
4031	    || (x >= maxX) || (y >= maxY)) {
4032hideIt:
4033	if (tree->tkwin == Tk_Parent(elemX->tkwin)) {
4034	    Tk_UnmapWindow(elemX->tkwin);
4035	} else {
4036	    Tk_UnmaintainGeometry(elemX->tkwin, tree->tkwin);
4037	}
4038	return;
4039    }
4040
4041    TreeDisplay_GetReadyForTrouble(tree, &requests);
4042
4043#ifdef CLIP_WINDOW
4044    if (elemX->child != NULL) {
4045	int cx = x, cy = y, cw = width, ch = height; /* clip win coords */
4046
4047	if (cx < minX) {
4048	    cw -= minX - cx;
4049	    cx = minX;
4050	}
4051	if (cy < minY) {
4052	    ch -= minY - cy;
4053	    cy = minY;
4054	}
4055	if (cx + cw > maxX)
4056	    cw = maxX - cx;
4057	if (cy + ch > maxY)
4058	    ch = maxY - cy;
4059
4060	/*
4061	* Reposition and map the window (but in different ways depending
4062	* on whether the treectrl is the window's parent).
4063	*/
4064
4065	if (tree->tkwin == Tk_Parent(elemX->tkwin)) {
4066	    if ((cx != Tk_X(elemX->tkwin)) || (cy != Tk_Y(elemX->tkwin))
4067		    || (cw != Tk_Width(elemX->tkwin))
4068		    || (ch != Tk_Height(elemX->tkwin))) {
4069		Tk_MoveResizeWindow(elemX->tkwin, cx, cy, cw, ch);
4070		if (TreeDisplay_WasThereTrouble(tree, requests))
4071		    return;
4072	    }
4073	    Tk_MapWindow(elemX->tkwin);
4074	} else {
4075	    Tk_MaintainGeometry(elemX->tkwin, tree->tkwin, cx, cy, cw, ch);
4076	}
4077	if (TreeDisplay_WasThereTrouble(tree, requests))
4078	    return;
4079
4080	/*
4081	 * Position the child window within the clip window.
4082	 */
4083	x -= cx;
4084	y -= cy;
4085	if ((x != Tk_X(elemX->child)) || (y != Tk_Y(elemX->child))
4086		|| (width != Tk_Width(elemX->child))
4087		|| (height != Tk_Height(elemX->child))) {
4088	    Tk_MoveResizeWindow(elemX->child, x, y, width, height);
4089	    if (TreeDisplay_WasThereTrouble(tree, requests))
4090		return;
4091	}
4092	Tk_MapWindow(elemX->child);
4093	return;
4094    }
4095#endif /* CLIP_WINDOW */
4096
4097    /*
4098     * Reposition and map the window (but in different ways depending
4099     * on whether the treectrl is the window's parent).
4100     */
4101
4102    if (tree->tkwin == Tk_Parent(elemX->tkwin)) {
4103	if ((x != Tk_X(elemX->tkwin)) || (y != Tk_Y(elemX->tkwin))
4104		|| (width != Tk_Width(elemX->tkwin))
4105		|| (height != Tk_Height(elemX->tkwin))) {
4106	    Tk_MoveResizeWindow(elemX->tkwin, x, y, width, height);
4107	    if (TreeDisplay_WasThereTrouble(tree, requests))
4108		return;
4109	}
4110	Tk_MapWindow(elemX->tkwin);
4111    } else {
4112	Tk_MaintainGeometry(elemX->tkwin, tree->tkwin, x, y,
4113		width, height);
4114    }
4115}
4116
4117static void NeededProcWindow(TreeElementArgs *args)
4118{
4119/*    TreeCtrl *tree = args->tree;*/
4120    TreeElement elem = args->elem;
4121    ElementWindow *elemX = (ElementWindow *) elem;
4122/*    ElementWindow *masterX = (ElementWindow *) elem->master;
4123    int state = args->state;*/
4124    int width = 0, height = 0;
4125
4126#ifdef CLIP_WINDOW
4127    if (elemX->child != NULL) {
4128	width = Tk_ReqWidth(elemX->child);
4129	if (width <= 0) {
4130	    width = 1;
4131	}
4132	height = Tk_ReqHeight(elemX->child);
4133	if (height <= 0) {
4134	    height = 1;
4135	}
4136    } else
4137#endif
4138    if (elemX->tkwin) {
4139	width = Tk_ReqWidth(elemX->tkwin);
4140	if (width <= 0) {
4141	    width = 1;
4142	}
4143	height = Tk_ReqHeight(elemX->tkwin);
4144	if (height <= 0) {
4145	    height = 1;
4146	}
4147    }
4148    args->needed.width = width;
4149    args->needed.height = height;
4150}
4151
4152static int StateProcWindow(TreeElementArgs *args)
4153{
4154#ifdef DEPRECATED
4155    TreeCtrl *tree = args->tree;
4156    TreeElement elem = args->elem;
4157    ElementBitmap *elemX = (ElementBitmap *) elem;
4158    ElementBitmap *masterX = (ElementBitmap *) elem->master;
4159    int match, match2;
4160    int draw1, draw2;
4161
4162    if (!args->states.visible2 || !args->states.draw2)
4163	return 0;
4164
4165    BOOLEAN_FOR_STATE(draw1, draw, args->states.state1)
4166    BOOLEAN_FOR_STATE(draw2, draw, args->states.state2)
4167    if ((draw1 != 0) != (draw2 != 0))
4168	return CS_DISPLAY;
4169#endif
4170
4171    return 0;
4172}
4173
4174static int UndefProcWindow(TreeElementArgs *args)
4175{
4176    TreeCtrl *tree = args->tree;
4177    TreeElement elem = args->elem;
4178    ElementWindow *elemX = (ElementWindow *) elem;
4179    int modified = 0;
4180
4181#ifdef DEPRECATED
4182    modified |= PerStateInfo_Undefine(tree, &pstBoolean, &elemX->draw, args->state);
4183#endif
4184    return modified;
4185}
4186
4187static int ActualProcWindow(TreeElementArgs *args)
4188{
4189#ifdef DEPRECATED
4190    TreeCtrl *tree = args->tree;
4191    ElementWindow *elemX = (ElementWindow *) args->elem;
4192    ElementWindow *masterX = (ElementWindow *) args->elem->master;
4193    static CONST char *optionName[] = {
4194	"-draw",
4195	(char *) NULL };
4196    int index, match, matchM;
4197    Tcl_Obj *obj = NULL;
4198
4199    if (Tcl_GetIndexFromObj(tree->interp, args->actual.obj, optionName,
4200		"option", 0, &index) != TCL_OK)
4201	return TCL_ERROR;
4202
4203    switch (index) {
4204	case 0: {
4205	    OBJECT_FOR_STATE(obj, pstBoolean, draw, args->state)
4206	    break;
4207	}
4208    }
4209    if (obj != NULL)
4210	Tcl_SetObjResult(tree->interp, obj);
4211#endif
4212    return TCL_OK;
4213}
4214
4215static void OnScreenProcWindow(TreeElementArgs *args)
4216{
4217    TreeCtrl *tree = args->tree;
4218    TreeElement elem = args->elem;
4219    ElementWindow *elemX = (ElementWindow *) elem;
4220
4221    if (!args->screen.visible && (elemX->tkwin != NULL)) {
4222	if (tree->tkwin == Tk_Parent(elemX->tkwin)) {
4223	    Tk_UnmapWindow(elemX->tkwin);
4224	} else {
4225	    Tk_UnmaintainGeometry(elemX->tkwin, tree->tkwin);
4226	}
4227    }
4228}
4229
4230TreeElementType treeElemTypeWindow = {
4231    "window",
4232    sizeof(ElementWindow),
4233    windowOptionSpecs,
4234    NULL,
4235    CreateProcWindow,
4236    DeleteProcWindow,
4237    ConfigProcWindow,
4238    DisplayProcWindow,
4239    NeededProcWindow,
4240    NULL, /* heightProc */
4241    WorldChangedProcWindow,
4242    StateProcWindow,
4243    UndefProcWindow,
4244    ActualProcWindow,
4245    OnScreenProcWindow
4246};
4247
4248/*****/
4249
4250typedef struct ElementAssocData ElementAssocData;
4251
4252struct ElementAssocData
4253{
4254    TreeElementType *typeList;
4255};
4256
4257int TreeElement_TypeFromObj(TreeCtrl *tree, Tcl_Obj *objPtr, TreeElementType **typePtrPtr)
4258{
4259    Tcl_Interp *interp = tree->interp;
4260    ElementAssocData *assocData;
4261    char *typeStr;
4262    int length;
4263    TreeElementType *typeList;
4264    TreeElementType *typePtr, *matchPtr = NULL;
4265
4266    assocData = Tcl_GetAssocData(interp, "TreeCtrlElementTypes", NULL);
4267    typeList = assocData->typeList;
4268
4269    typeStr = Tcl_GetStringFromObj(objPtr, &length);
4270    if (!length) {
4271	FormatResult(interp, "invalid element type \"\"");
4272	return TCL_ERROR;
4273    }
4274    for (typePtr = typeList;
4275	typePtr != NULL;
4276	typePtr = typePtr->next) {
4277	if ((typeStr[0] == typePtr->name[0]) &&
4278		!strncmp(typeStr, typePtr->name, length)) {
4279	    if (matchPtr != NULL) {
4280		FormatResult(interp,
4281			"ambiguous element type \"%s\"",
4282			typeStr);
4283		return TCL_ERROR;
4284	    }
4285	    matchPtr = typePtr;
4286	}
4287    }
4288    if (matchPtr == NULL) {
4289	FormatResult(interp, "unknown element type \"%s\"", typeStr);
4290	return TCL_ERROR;
4291    }
4292    *typePtrPtr = matchPtr;
4293
4294    return TCL_OK;
4295}
4296
4297int TreeCtrl_RegisterElementType(Tcl_Interp *interp, TreeElementType *newTypePtr)
4298{
4299    ElementAssocData *assocData;
4300    TreeElementType *typeList;
4301    TreeElementType *prevPtr, *typePtr, *nextPtr;
4302
4303    assocData = Tcl_GetAssocData(interp, "TreeCtrlElementTypes", NULL);
4304    typeList = assocData->typeList;
4305
4306    for (typePtr = typeList, prevPtr = NULL;
4307	 typePtr != NULL;
4308	 prevPtr = typePtr, typePtr = nextPtr) {
4309	nextPtr = typePtr->next;
4310	/* Remove duplicate type */
4311	if (!strcmp(typePtr->name, newTypePtr->name)) {
4312	    if (prevPtr == NULL)
4313		typeList = typePtr->next;
4314	    else
4315		prevPtr->next = typePtr->next;
4316	    ckfree((char *) typePtr);
4317	}
4318    }
4319    typePtr = (TreeElementType *) ckalloc(sizeof(TreeElementType));
4320    memcpy(typePtr, newTypePtr, sizeof(TreeElementType));
4321
4322    typePtr->next = typeList;
4323    typeList = typePtr;
4324
4325    typePtr->optionTable = Tk_CreateOptionTable(interp,
4326	    newTypePtr->optionSpecs);
4327
4328    assocData->typeList = typeList;
4329
4330    return TCL_OK;
4331}
4332
4333static TreeCtrlStubs stubs = {
4334    TreeCtrl_RegisterElementType,
4335    Tree_RedrawElement,
4336    Tree_ElementIterateBegin,
4337    Tree_ElementIterateNext,
4338    Tree_ElementIterateGet,
4339    Tree_ElementIterateChanged,
4340    PerStateInfo_Free,
4341    PerStateInfo_FromObj,
4342    PerStateInfo_ForState,
4343    PerStateInfo_ObjForState,
4344    PerStateInfo_Undefine,
4345    &pstBoolean,
4346    PerStateBoolean_ForState,
4347    PSTSave,
4348    PSTRestore,
4349    TreeStateFromObj,
4350    BooleanCO_Init,
4351    StringTableCO_Init,
4352    PerStateCO_Init
4353};
4354
4355static void FreeAssocData(ClientData clientData, Tcl_Interp *interp)
4356{
4357    ElementAssocData *assocData = clientData;
4358    TreeElementType *typeList = assocData->typeList;
4359    TreeElementType *next;
4360
4361    while (typeList != NULL) {
4362	next = typeList->next;
4363	/* The ElementType.optionTables are freed when the interp is deleted */
4364	ckfree((char *) typeList);
4365	typeList = next;
4366    }
4367    ckfree((char *) assocData);
4368}
4369
4370int TreeElement_Init(Tcl_Interp *interp)
4371{
4372    ElementAssocData *assocData;
4373
4374    /* FIXME: memory leak with dynamically-allocated ClientData. */
4375
4376    /*
4377     * bitmap
4378     */
4379    PerStateCO_Init(treeElemTypeBitmap.optionSpecs, "-background",
4380	&pstColor, TreeStateFromObj);
4381    PerStateCO_Init(treeElemTypeBitmap.optionSpecs, "-bitmap",
4382	&pstBitmap, TreeStateFromObj);
4383#ifdef DEPRECATED
4384    PerStateCO_Init(treeElemTypeBitmap.optionSpecs, "-draw",
4385	&pstBoolean, TreeStateFromObj);
4386#endif
4387    PerStateCO_Init(treeElemTypeBitmap.optionSpecs, "-foreground",
4388	&pstColor, TreeStateFromObj);
4389
4390    /*
4391     * border
4392     */
4393#ifdef DEPRECATED
4394    PerStateCO_Init(treeElemTypeBorder.optionSpecs, "-draw",
4395	&pstBoolean, TreeStateFromObj);
4396#endif
4397    PerStateCO_Init(treeElemTypeBorder.optionSpecs, "-background",
4398	&pstBorder, TreeStateFromObj);
4399    PerStateCO_Init(treeElemTypeBorder.optionSpecs, "-relief",
4400	&pstRelief, TreeStateFromObj);
4401
4402    /*
4403     * image
4404     */
4405#ifdef DEPRECATED
4406    DynamicCO_Init(treeElemTypeImage.optionSpecs, "-draw",
4407	1002, sizeof(PerStateInfo),
4408	Tk_Offset(PerStateInfo, obj),
4409	0, PerStateCO_Alloc("-draw", &pstBoolean, TreeStateFromObj),
4410	(DynamicOptionInitProc *) NULL);
4411#endif
4412    PerStateCO_Init(treeElemTypeImage.optionSpecs, "-image",
4413	&pstImage, TreeStateFromObj);
4414
4415    /* 2 options in the same structure. */
4416    DynamicCO_Init(treeElemTypeImage.optionSpecs, "-height",
4417	1001, sizeof(ElementImageSize),
4418	Tk_Offset(ElementImageSize, heightObj),
4419	Tk_Offset(ElementImageSize, height), &TreeCtrlCO_pixels,
4420	(DynamicOptionInitProc *) NULL);
4421    DynamicCO_Init(treeElemTypeImage.optionSpecs, "-width",
4422	1001, sizeof(ElementImageSize),
4423	Tk_Offset(ElementImageSize, widthObj),
4424	Tk_Offset(ElementImageSize, width), &TreeCtrlCO_pixels,
4425	(DynamicOptionInitProc *) NULL);
4426
4427    DynamicCO_Init(treeElemTypeImage.optionSpecs, "-tiled",
4428	1003, sizeof(int),
4429	-1,
4430	0, &booleanCO,
4431	DynamicOptionInitBoolean);
4432
4433    /*
4434     * rect
4435     */
4436#ifdef DEPRECATED
4437    PerStateCO_Init(treeElemTypeRect.optionSpecs, "-draw",
4438	&pstBoolean, TreeStateFromObj);
4439#endif
4440    PerStateCO_Init(treeElemTypeRect.optionSpecs, "-fill",
4441	&pstColor, TreeStateFromObj);
4442    PerStateCO_Init(treeElemTypeRect.optionSpecs, "-outline",
4443	&pstColor, TreeStateFromObj);
4444
4445    /*
4446     * text
4447     */
4448    /* 3 options in the same structure. */
4449    DynamicCO_Init(treeElemTypeText.optionSpecs, "-data",
4450	DOID_TEXT_DATA, sizeof(ElementTextData),
4451	Tk_Offset(ElementTextData, dataObj),
4452	-1, &TreeCtrlCO_string,
4453	ElementTextDataInit);
4454    DynamicCO_Init(treeElemTypeText.optionSpecs, "-datatype",
4455	DOID_TEXT_DATA, sizeof(ElementTextData),
4456	-1,
4457	Tk_Offset(ElementTextData, dataType),
4458	StringTableCO_Alloc("-datatype", textDataTypeST),
4459	ElementTextDataInit);
4460    DynamicCO_Init(treeElemTypeText.optionSpecs, "-format",
4461	DOID_TEXT_DATA, sizeof(ElementTextData),
4462	Tk_Offset(ElementTextData, formatObj),
4463	-1, &TreeCtrlCO_string,
4464	ElementTextDataInit);
4465
4466    /* 4 options in the same structure. */
4467    DynamicCO_Init(treeElemTypeText.optionSpecs, "-justify",
4468	DOID_TEXT_LAYOUT, sizeof(ElementTextLayout),
4469	-1,
4470	Tk_Offset(ElementTextLayout, justify),
4471	StringTableCO_Alloc("-justify", textJustifyST),
4472	ElementTextLayoutInit);
4473    DynamicCO_Init(treeElemTypeText.optionSpecs, "-lines",
4474	DOID_TEXT_LAYOUT, sizeof(ElementTextLayout),
4475	-1,
4476	Tk_Offset(ElementTextLayout, lines),
4477	IntegerCO_Alloc("-lines",
4478	    0,		/* min */
4479	    0,		/* max (ignored) */
4480	    -1,		/* empty */
4481	    0x01),	/* flags: min */
4482	ElementTextLayoutInit);
4483    DynamicCO_Init(treeElemTypeText.optionSpecs, "-width",
4484	DOID_TEXT_LAYOUT, sizeof(ElementTextLayout),
4485	Tk_Offset(ElementTextLayout, widthObj),
4486	Tk_Offset(ElementTextLayout, width), &TreeCtrlCO_pixels,
4487	ElementTextLayoutInit);
4488    DynamicCO_Init(treeElemTypeText.optionSpecs, "-wrap",
4489	DOID_TEXT_LAYOUT, sizeof(ElementTextLayout),
4490	-1,
4491	Tk_Offset(ElementTextLayout, wrap),
4492	StringTableCO_Alloc("-wrap", textWrapST),
4493	ElementTextLayoutInit);
4494
4495#ifdef DEPRECATED
4496    DynamicCO_Init(treeElemTypeText.optionSpecs, "-draw",
4497	DOID_TEXT_DRAW, sizeof(PerStateInfo),
4498	Tk_Offset(PerStateInfo, obj),
4499	0, PerStateCO_Alloc("-draw", &pstBoolean, TreeStateFromObj),
4500	(DynamicOptionInitProc *) NULL);
4501#endif
4502    DynamicCO_Init(treeElemTypeText.optionSpecs, "-fill",
4503	DOID_TEXT_FILL, sizeof(PerStateInfo),
4504	Tk_Offset(PerStateInfo, obj),
4505	0, PerStateCO_Alloc("-fill", &pstColor, TreeStateFromObj),
4506	(DynamicOptionInitProc *) NULL);
4507    DynamicCO_Init(treeElemTypeText.optionSpecs, "-font",
4508	DOID_TEXT_FONT, sizeof(PerStateInfo),
4509	Tk_Offset(PerStateInfo, obj),
4510	0, PerStateCO_Alloc("-font", &pstFont, TreeStateFromObj),
4511	(DynamicOptionInitProc *) NULL);
4512    DynamicCO_Init(treeElemTypeText.optionSpecs, "-textvariable",
4513	DOID_TEXT_VAR, sizeof(ElementTextVar),
4514	Tk_Offset(struct ElementTextVar, varNameObj),
4515	-1, &TreeCtrlCO_string,
4516	(DynamicOptionInitProc *) NULL);
4517
4518#ifdef TEXT_STYLE
4519    DynamicCO_Init(treeElemTypeText.optionSpecs, "-underline",
4520	DOID_TEXT_STYLE, sizeof(ElementTextStyle),
4521	-1,
4522	Tk_Offset(ElementTextStyle, underline),
4523	IntegerCO_Alloc("-underline",
4524	    0,		/* min (ignored) */
4525	    0,		/* max (ignored) */
4526	    TEXT_UNDERLINE_EMPTYVAL,	/* empty */
4527	    0x00),	/* flags */
4528	ElementTextStyleInit);
4529#endif
4530
4531    /* 2 options in the same structure */
4532    DynamicCO_Init(treeElemTypeText.optionSpecs, "-lmargin1",
4533	DOID_TEXT_LAYOUT3, sizeof(ElementTextLayout3),
4534	Tk_Offset(ElementTextLayout3, lMargin1Obj),
4535	Tk_Offset(ElementTextLayout3, lMargin1), &TreeCtrlCO_pixels,
4536	(DynamicOptionInitProc *) NULL);
4537    DynamicCO_Init(treeElemTypeText.optionSpecs, "-lmargin2",
4538	DOID_TEXT_LAYOUT3, sizeof(ElementTextLayout3),
4539	Tk_Offset(ElementTextLayout3, lMargin2Obj),
4540	Tk_Offset(ElementTextLayout3, lMargin2), &TreeCtrlCO_pixels,
4541	(DynamicOptionInitProc *) NULL);
4542
4543    /*
4544     * window
4545     */
4546#ifdef DEPRECATED
4547    PerStateCO_Init(treeElemTypeWindow.optionSpecs, "-draw",
4548	&pstBoolean, TreeStateFromObj);
4549#endif
4550
4551    assocData = (ElementAssocData *) ckalloc(sizeof(ElementAssocData));
4552    assocData->typeList = NULL;
4553    Tcl_SetAssocData(interp, "TreeCtrlElementTypes", FreeAssocData, assocData);
4554
4555    TreeCtrl_RegisterElementType(interp, &treeElemTypeBitmap);
4556    TreeCtrl_RegisterElementType(interp, &treeElemTypeBorder);
4557/*    TreeCtrl_RegisterElementType(interp, &treeElemTypeCheckButton);*/
4558    TreeCtrl_RegisterElementType(interp, &treeElemTypeImage);
4559    TreeCtrl_RegisterElementType(interp, &treeElemTypeRect);
4560    TreeCtrl_RegisterElementType(interp, &treeElemTypeText);
4561    TreeCtrl_RegisterElementType(interp, &treeElemTypeWindow);
4562
4563    Tcl_SetAssocData(interp, "TreeCtrlStubs", NULL, &stubs);
4564
4565    return TCL_OK;
4566}
4567