1/*
2 * tkStyle.c --
3 *
4 *	This file implements the widget styles and themes support.
5 *
6 * Copyright (c) 1990-1993 The Regents of the University of California.
7 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
8 *
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id$
13 */
14
15#include "tkInt.h"
16
17/*
18 * The following structure is used to cache widget option specs matching an
19 * element's required options defined by Tk_ElementOptionSpecs. It also holds
20 * information behind Tk_StyledElement opaque tokens.
21 */
22
23typedef struct StyledWidgetSpec {
24    struct StyledElement *elementPtr;
25				/* Pointer to the element holding this
26				 * structure. */
27    Tk_OptionTable optionTable;	/* Option table for the widget class using the
28				 * element. */
29    CONST Tk_OptionSpec **optionsPtr;
30				/* Table of option spec pointers, matching the
31				 * option list provided during element
32				 * registration. Malloc'd. */
33} StyledWidgetSpec;
34
35/*
36 * Elements are declared using static templates. But static information must
37 * be completed by dynamic information only accessible at runtime. For each
38 * registered element, an instance of the following structure is stored in
39 * each style engine and used to cache information about the widget types
40 * (identified by their optionTable) that use the given element.
41 */
42
43typedef struct StyledElement {
44    struct Tk_ElementSpec *specPtr;
45				/* Filled with template provided during
46				 * registration. NULL means no implementation
47				 * is available for the current engine. */
48    int nbWidgetSpecs;		/* Size of the array below. Number of distinct
49				 * widget classes (actually, distinct option
50				 * tables) that used the element so far. */
51    StyledWidgetSpec *widgetSpecs;
52				/* See above for the structure definition.
53				 * Table grows dynamically as new widgets use
54				 * the element. Malloc'd. */
55} StyledElement;
56
57/*
58 * The following structure holds information behind Tk_StyleEngine opaque
59 * tokens.
60 */
61
62typedef struct StyleEngine {
63    CONST char *name;		/* Name of engine. Points to a hash key. */
64    StyledElement *elements;	/* Table of widget element descriptors. Each
65				 * element is indexed by a unique system-wide
66				 * ID. Table grows dynamically as new elements
67				 * are registered. Malloc'd*/
68    struct StyleEngine *parentPtr;
69				/* Parent engine. Engines may be layered to
70				 * form a fallback chain, terminated by the
71				 * default system engine. */
72} StyleEngine;
73
74/*
75 * Styles are instances of style engines. The following structure holds
76 * information behind Tk_Style opaque tokens.
77 */
78
79typedef struct Style {
80    CONST char *name;		/* Name of style. Points to a hash key. */
81    StyleEngine *enginePtr;	/* Style engine of which the style is an
82				 * instance. */
83    ClientData clientData;	/* Data provided during registration. */
84} Style;
85
86/*
87 * Each registered element uses an instance of the following structure.
88 */
89
90typedef struct Element {
91    CONST char *name;		/* Name of element. Points to a hash key. */
92    int id;			/* Id of element. */
93    int genericId;		/* Id of generic element. */
94    int created;		/* Boolean, whether the element was created
95				 * explicitly (was registered) or implicitly
96				 * (by a derived element). */
97} Element;
98
99/*
100 * Thread-local data.
101 */
102
103typedef struct ThreadSpecificData {
104    int nbInit;			/* Number of calls to the init proc. */
105    Tcl_HashTable engineTable;	/* Map a name to a style engine. Keys are
106				 * strings, values are Tk_StyleEngine
107				 * pointers. */
108    StyleEngine *defaultEnginePtr;
109				/* Default, core-defined style engine. Global
110				 * fallback for all engines. */
111    Tcl_HashTable styleTable;	/* Map a name to a style. Keys are strings,
112				 * values are Tk_Style pointers.*/
113    int nbElements;		/* Size of the below tables. */
114    Tcl_HashTable elementTable;	/* Map a name to an element Id. Keys are
115				 * strings, values are integer element IDs. */
116    Element *elements;		/* Array of Elements. */
117} ThreadSpecificData;
118
119static Tcl_ThreadDataKey dataKey;
120
121/*
122 * Forward declarations for functions defined later in this file:
123 */
124
125static int		CreateElement(CONST char *name, int create);
126static void		DupStyleObjProc(Tcl_Obj *srcObjPtr,
127			    Tcl_Obj *dupObjPtr);
128static void		FreeElement(Element *elementPtr);
129static void		FreeStyledElement(StyledElement *elementPtr);
130static void		FreeStyleEngine(StyleEngine *enginePtr);
131static void		FreeStyleObjProc(Tcl_Obj *objPtr);
132static void		FreeWidgetSpec(StyledWidgetSpec *widgetSpecPtr);
133static StyledElement *	GetStyledElement(StyleEngine *enginePtr,
134			    int elementId);
135static StyledWidgetSpec*GetWidgetSpec(StyledElement *elementPtr,
136			    Tk_OptionTable optionTable);
137static void		InitElement(Element *elementPtr, CONST char *name,
138			    int id, int genericId, int created);
139static void		InitStyle(Style *stylePtr, CONST char *name,
140			    StyleEngine *enginePtr, ClientData clientData);
141static void		InitStyledElement(StyledElement *elementPtr);
142static void		InitStyleEngine(StyleEngine *enginePtr,
143			    CONST char *name, StyleEngine *parentPtr);
144static void		InitWidgetSpec(StyledWidgetSpec *widgetSpecPtr,
145			    StyledElement *elementPtr,
146			    Tk_OptionTable optionTable);
147static int		SetStyleFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
148
149/*
150 * The following structure defines the implementation of the "style" Tcl
151 * object, used for drawing. The internalRep.otherValuePtr field of each style
152 * object points to the Style structure for the stylefont, or NULL.
153 */
154
155static Tcl_ObjType styleObjType = {
156    "style",			/* name */
157    FreeStyleObjProc,		/* freeIntRepProc */
158    DupStyleObjProc,		/* dupIntRepProc */
159    NULL,			/* updateStringProc */
160    SetStyleFromAny		/* setFromAnyProc */
161};
162
163/*
164 *---------------------------------------------------------------------------
165 *
166 * TkStylePkgInit --
167 *
168 *	This function is called when an application is created. It initializes
169 *	all the structures that are used by the style package on a per
170 *	application basis.
171 *
172 * Results:
173 *	Stores data in thread-local storage.
174 *
175 * Side effects:
176 *	Memory allocated.
177 *
178 *---------------------------------------------------------------------------
179 */
180
181void
182TkStylePkgInit(
183    TkMainInfo *mainPtr)	/* The application being created. */
184{
185    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
186	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
187
188    if (tsdPtr->nbInit != 0) {
189	return;
190    }
191
192    /*
193     * Initialize tables.
194     */
195
196    Tcl_InitHashTable(&tsdPtr->engineTable, TCL_STRING_KEYS);
197    Tcl_InitHashTable(&tsdPtr->styleTable, TCL_STRING_KEYS);
198    Tcl_InitHashTable(&tsdPtr->elementTable, TCL_STRING_KEYS);
199    tsdPtr->nbElements = 0;
200    tsdPtr->elements = NULL;
201
202    /*
203     * Create the default system engine.
204     */
205
206    tsdPtr->defaultEnginePtr = (StyleEngine *)
207	    Tk_RegisterStyleEngine(NULL, NULL);
208
209    /*
210     * Create the default system style.
211     */
212
213    Tk_CreateStyle(NULL, (Tk_StyleEngine) tsdPtr->defaultEnginePtr,
214	    (ClientData) 0);
215
216    tsdPtr->nbInit++;
217}
218
219/*
220 *---------------------------------------------------------------------------
221 *
222 * TkStylePkgFree --
223 *
224 *	This function is called when an application is deleted. It deletes all
225 *	the structures that were used by the style package for this
226 *	application.
227 *
228 * Results:
229 *	None.
230 *
231 * Side effects:
232 *	Memory freed.
233 *
234 *---------------------------------------------------------------------------
235 */
236
237void
238TkStylePkgFree(
239    TkMainInfo *mainPtr)	/* The application being deleted. */
240{
241    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
242	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
243    Tcl_HashSearch search;
244    Tcl_HashEntry *entryPtr;
245    StyleEngine *enginePtr;
246    int i;
247
248    tsdPtr->nbInit--;
249    if (tsdPtr->nbInit != 0) {
250	return;
251    }
252
253    /*
254     * Free styles.
255     */
256
257    entryPtr = Tcl_FirstHashEntry(&tsdPtr->styleTable, &search);
258    while (entryPtr != NULL) {
259	ckfree((char *) Tcl_GetHashValue(entryPtr));
260	entryPtr = Tcl_NextHashEntry(&search);
261    }
262    Tcl_DeleteHashTable(&tsdPtr->styleTable);
263
264    /*
265     * Free engines.
266     */
267
268    entryPtr = Tcl_FirstHashEntry(&tsdPtr->engineTable, &search);
269    while (entryPtr != NULL) {
270	enginePtr = (StyleEngine *) Tcl_GetHashValue(entryPtr);
271	FreeStyleEngine(enginePtr);
272	ckfree((char *) enginePtr);
273	entryPtr = Tcl_NextHashEntry(&search);
274    }
275    Tcl_DeleteHashTable(&tsdPtr->engineTable);
276
277    /*
278     * Free elements.
279     */
280
281    for (i = 0; i < tsdPtr->nbElements; i++) {
282	FreeElement(tsdPtr->elements+i);
283    }
284    Tcl_DeleteHashTable(&tsdPtr->elementTable);
285    ckfree((char *) tsdPtr->elements);
286}
287
288/*
289 *---------------------------------------------------------------------------
290 *
291 * Tk_RegisterStyleEngine --
292 *
293 *	This function is called to register a new style engine. Style engines
294 *	are stored in thread-local space.
295 *
296 * Results:
297 *	The newly allocated engine, or NULL if an engine with the same name
298 *	exists.
299 *
300 * Side effects:
301 *	Memory allocated. Data added to thread-local table.
302 *
303 *---------------------------------------------------------------------------
304 */
305
306Tk_StyleEngine
307Tk_RegisterStyleEngine(
308    CONST char *name,		/* Name of the engine to create. NULL or empty
309				 * means the default system engine. */
310    Tk_StyleEngine parent)	/* The engine's parent. NULL means the default
311				 * system engine. */
312{
313    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
314	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
315    Tcl_HashEntry *entryPtr;
316    int newEntry;
317    StyleEngine *enginePtr;
318
319    /*
320     * Attempt to create a new entry in the engine table.
321     */
322
323    entryPtr = Tcl_CreateHashEntry(&tsdPtr->engineTable,
324	    (name != NULL ? name : ""), &newEntry);
325    if (!newEntry) {
326	/*
327	 * An engine was already registered by that name.
328	 */
329
330	return NULL;
331    }
332
333    /*
334     * Allocate and intitialize a new engine.
335     */
336
337    enginePtr = (StyleEngine *) ckalloc(sizeof(StyleEngine));
338    InitStyleEngine(enginePtr, Tcl_GetHashKey(&tsdPtr->engineTable, entryPtr),
339	    (StyleEngine *) parent);
340    Tcl_SetHashValue(entryPtr, (ClientData) enginePtr);
341
342    return (Tk_StyleEngine) enginePtr;
343}
344
345/*
346 *---------------------------------------------------------------------------
347 *
348 * InitStyleEngine --
349 *
350 *	Initialize a newly allocated style engine.
351 *
352 * Results:
353 *	None.
354 *
355 * Side effects:
356 *	Memory allocated.
357 *
358 *---------------------------------------------------------------------------
359 */
360
361static void
362InitStyleEngine(
363    StyleEngine *enginePtr,	/* Points to an uninitialized engine. */
364    CONST char *name,		/* Name of the registered engine. NULL or empty
365				 * means the default system engine. Usually
366				 * points to the hash key. */
367    StyleEngine *parentPtr)	/* The engine's parent. NULL means the default
368				 * system engine. */
369{
370    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
371	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
372    int elementId;
373
374    if (name == NULL || *name == '\0') {
375	/*
376	 * This is the default style engine.
377	 */
378
379	enginePtr->parentPtr = NULL;
380
381    } else if (parentPtr == NULL) {
382	/*
383	 * The default style engine is the parent.
384	 */
385
386	enginePtr->parentPtr = tsdPtr->defaultEnginePtr;
387
388    } else {
389	enginePtr->parentPtr = parentPtr;
390    }
391
392    /*
393     * Allocate and initialize elements array.
394     */
395
396    if (tsdPtr->nbElements > 0) {
397	enginePtr->elements = (StyledElement *) ckalloc(
398		sizeof(StyledElement) * tsdPtr->nbElements);
399	for (elementId = 0; elementId < tsdPtr->nbElements; elementId++) {
400	    InitStyledElement(enginePtr->elements+elementId);
401	}
402    } else {
403	enginePtr->elements = NULL;
404    }
405}
406
407/*
408 *---------------------------------------------------------------------------
409 *
410 * FreeStyleEngine --
411 *
412 *	Free an engine and its associated data.
413 *
414 * Results:
415 *	None
416 *
417 * Side effects:
418 *	Memory freed.
419 *
420 *---------------------------------------------------------------------------
421 */
422
423static void
424FreeStyleEngine(
425    StyleEngine *enginePtr)	/* The style engine to free. */
426{
427    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
428	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
429    int elementId;
430
431    /*
432     * Free allocated elements.
433     */
434
435    for (elementId = 0; elementId < tsdPtr->nbElements; elementId++) {
436	FreeStyledElement(enginePtr->elements+elementId);
437    }
438    ckfree((char *) enginePtr->elements);
439}
440
441/*
442 *---------------------------------------------------------------------------
443 *
444 * Tk_GetStyleEngine --
445 *
446 *	Retrieve a registered style engine by its name.
447 *
448 * Results:
449 *	A pointer to the style engine, or NULL if none found.
450 *
451 * Side effects:
452 *	None.
453 *
454 *---------------------------------------------------------------------------
455 */
456
457Tk_StyleEngine
458Tk_GetStyleEngine(
459    CONST char *name)		/* Name of the engine to retrieve. NULL or
460				 * empty means the default system engine. */
461{
462    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
463	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
464    Tcl_HashEntry *entryPtr;
465
466    if (name == NULL) {
467	return (Tk_StyleEngine) tsdPtr->defaultEnginePtr;
468    }
469
470    entryPtr = Tcl_FindHashEntry(&tsdPtr->engineTable, (name!=NULL?name:""));
471    if (!entryPtr) {
472	return NULL;
473    }
474
475    return (Tk_StyleEngine) Tcl_GetHashValue(entryPtr);
476}
477
478/*
479 *---------------------------------------------------------------------------
480 *
481 * InitElement --
482 *
483 *	Initialize a newly allocated element.
484 *
485 * Results:
486 *	None.
487 *
488 * Side effects:
489 *	None.
490 *
491 *---------------------------------------------------------------------------
492 */
493
494static void
495InitElement(
496    Element *elementPtr,	/* Points to an uninitialized element.*/
497    CONST char *name,		/* Name of the registered element. Usually
498				 * points to the hash key. */
499    int id,			/* Unique element ID. */
500    int genericId,		/* ID of generic element. -1 means none. */
501    int created)		/* Boolean, whether the element was created
502				 * explicitly (was registered) or implicitly
503				 * (by a derived element). */
504{
505    elementPtr->name = name;
506    elementPtr->id = id;
507    elementPtr->genericId = genericId;
508    elementPtr->created = (created?1:0);
509}
510
511/*
512 *---------------------------------------------------------------------------
513 *
514 * FreeElement --
515 *
516 *	Free an element and its associated data.
517 *
518 * Results:
519 *	None.
520 *
521 * Side effects:
522 *	Memory freed.
523 *
524 *---------------------------------------------------------------------------
525 */
526
527static void
528FreeElement(
529    Element *elementPtr)	/* The element to free. */
530{
531    /* Nothing to do. */
532}
533
534/*
535 *---------------------------------------------------------------------------
536 *
537 * InitStyledElement --
538 *
539 *	Initialize a newly allocated styled element.
540 *
541 * Results:
542 *	None.
543 *
544 * Side effects:
545 *	None.
546 *
547 *---------------------------------------------------------------------------
548 */
549
550static void
551InitStyledElement(
552    StyledElement *elementPtr)	/* Points to an uninitialized element.*/
553{
554    memset(elementPtr, 0, sizeof(StyledElement));
555}
556
557/*
558 *---------------------------------------------------------------------------
559 *
560 * FreeStyledElement --
561 *
562 *	Free a styled element and its associated data.
563 *
564 * Results:
565 *	None.
566 *
567 * Side effects:
568 *	Memory freed.
569 *
570 *---------------------------------------------------------------------------
571 */
572
573static void
574FreeStyledElement(
575    StyledElement *elementPtr)	/* The styled element to free. */
576{
577    int i;
578
579    /*
580     * Free allocated widget specs.
581     */
582
583    for (i = 0; i < elementPtr->nbWidgetSpecs; i++) {
584	FreeWidgetSpec(elementPtr->widgetSpecs+i);
585    }
586    ckfree((char *) elementPtr->widgetSpecs);
587}
588
589/*
590 *---------------------------------------------------------------------------
591 *
592 * CreateElement --
593 *
594 *	Find an existing or create a new element.
595 *
596 * Results:
597 *	The unique ID for the created or found element.
598 *
599 * Side effects:
600 *	Memory allocated.
601 *
602 *---------------------------------------------------------------------------
603 */
604
605static int
606CreateElement(
607    CONST char *name,	/* Name of the element. */
608    int create)		/* Boolean, whether the element is being created
609			 * explicitly (being registered) or implicitly (by a
610			 * derived element). */
611{
612    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
613	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
614    Tcl_HashEntry *entryPtr, *engineEntryPtr;
615    Tcl_HashSearch search;
616    int newEntry;
617    int elementId, genericId = -1;
618    char *dot;
619    StyleEngine *enginePtr;
620
621    /*
622     * Find or create the element.
623     */
624
625    entryPtr = Tcl_CreateHashEntry(&tsdPtr->elementTable, name, &newEntry);
626    if (!newEntry) {
627	elementId = PTR2INT(Tcl_GetHashValue(entryPtr));
628	if (create) {
629	    tsdPtr->elements[elementId].created = 1;
630	}
631	return elementId;
632    }
633
634    /*
635     * The element didn't exist. If it's a derived element, find or create its
636     * generic element ID.
637     */
638
639    dot = strchr(name, '.');
640    if (dot) {
641	genericId = CreateElement(dot+1, 0);
642    }
643
644    elementId = tsdPtr->nbElements++;
645    Tcl_SetHashValue(entryPtr, (ClientData) INT2PTR(elementId));
646
647    /*
648     * Reallocate element table.
649     */
650
651    tsdPtr->elements = (Element *) ckrealloc((char *) tsdPtr->elements,
652	    sizeof(Element) * tsdPtr->nbElements);
653    InitElement(tsdPtr->elements+elementId,
654	    Tcl_GetHashKey(&tsdPtr->elementTable, entryPtr), elementId,
655	    genericId, create);
656
657    /*
658     * Reallocate style engines' element table.
659     */
660
661    engineEntryPtr = Tcl_FirstHashEntry(&tsdPtr->engineTable, &search);
662    while (engineEntryPtr != NULL) {
663	enginePtr = (StyleEngine *) Tcl_GetHashValue(engineEntryPtr);
664
665	enginePtr->elements = (StyledElement *) ckrealloc(
666		(char *) enginePtr->elements,
667		sizeof(StyledElement) * tsdPtr->nbElements);
668	InitStyledElement(enginePtr->elements+elementId);
669
670	engineEntryPtr = Tcl_NextHashEntry(&search);
671    }
672
673    return elementId;
674}
675
676/*
677 *---------------------------------------------------------------------------
678 *
679 * Tk_GetElementId --
680 *
681 *	Find an existing element.
682 *
683 * Results:
684 *	The unique ID for the found element, or -1 if not found.
685 *
686 * Side effects:
687 *	Generic elements may be created.
688 *
689 *---------------------------------------------------------------------------
690 */
691
692int
693Tk_GetElementId(
694    CONST char *name)		/* Name of the element. */
695{
696    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
697	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
698    Tcl_HashEntry *entryPtr;
699    int genericId = -1;
700    char *dot;
701
702    /*
703     * Find the element Id.
704     */
705
706    entryPtr = Tcl_FindHashEntry(&tsdPtr->elementTable, name);
707    if (entryPtr) {
708	return PTR2INT(Tcl_GetHashValue(entryPtr));
709    }
710
711    /*
712     * Element not found. If the given name was derived, then first search for
713     * the generic element. If found, create the new derived element.
714     */
715
716    dot = strchr(name, '.');
717    if (!dot) {
718	return -1;
719    }
720    genericId = Tk_GetElementId(dot+1);
721    if (genericId == -1) {
722	return -1;
723    }
724    if (!tsdPtr->elements[genericId].created) {
725	/*
726	 * The generic element was created implicitly and thus has no real
727	 * existence.
728	 */
729
730	return -1;
731    } else {
732	/*
733	 * The generic element was created explicitly. Create the derived
734	 * element.
735	 */
736
737	return CreateElement(name, 1);
738    }
739}
740
741/*
742 *---------------------------------------------------------------------------
743 *
744 * Tk_RegisterStyledElement --
745 *
746 *	Register an implementation of a new or existing element for the given
747 *	style engine.
748 *
749 * Results:
750 *	The unique ID for the created or found element.
751 *
752 * Side effects:
753 *	Elements may be created. Memory allocated.
754 *
755 *---------------------------------------------------------------------------
756 */
757
758int
759Tk_RegisterStyledElement(
760    Tk_StyleEngine engine,	/* Style engine providing the
761				 * implementation. */
762    Tk_ElementSpec *templatePtr)/* Static template information about the
763				 * element. */
764{
765    int elementId;
766    StyledElement *elementPtr;
767    Tk_ElementSpec *specPtr;
768    int nbOptions;
769    register Tk_ElementOptionSpec *srcOptions, *dstOptions;
770
771    if (templatePtr->version != TK_STYLE_VERSION_1) {
772	/*
773	 * Version mismatch. Do nothing.
774	 */
775
776	return -1;
777    }
778
779    if (engine == NULL) {
780	engine = Tk_GetStyleEngine(NULL);
781    }
782
783    /*
784     * Register the element, allocating storage in the various engines if
785     * necessary.
786     */
787
788    elementId = CreateElement(templatePtr->name, 1);
789
790    /*
791     * Initialize the styled element.
792     */
793
794    elementPtr = ((StyleEngine *) engine)->elements+elementId;
795
796    specPtr = (Tk_ElementSpec *) ckalloc(sizeof(Tk_ElementSpec));
797    specPtr->version = templatePtr->version;
798    specPtr->name = ckalloc(strlen(templatePtr->name)+1);
799    strcpy(specPtr->name, templatePtr->name);
800    nbOptions = 0;
801    for (nbOptions = 0, srcOptions = templatePtr->options;
802	    srcOptions->name != NULL; nbOptions++, srcOptions++) {
803	/* empty body */
804    }
805    specPtr->options = (Tk_ElementOptionSpec *)
806	    ckalloc(sizeof(Tk_ElementOptionSpec) * (nbOptions+1));
807    for (srcOptions = templatePtr->options, dstOptions = specPtr->options;
808	    /* End condition within loop */; srcOptions++, dstOptions++) {
809	if (srcOptions->name == NULL) {
810	    dstOptions->name = NULL;
811	    break;
812	}
813
814	dstOptions->name = ckalloc(strlen(srcOptions->name)+1);
815	strcpy(dstOptions->name, srcOptions->name);
816	dstOptions->type = srcOptions->type;
817    }
818    specPtr->getSize = templatePtr->getSize;
819    specPtr->getBox = templatePtr->getBox;
820    specPtr->getBorderWidth = templatePtr->getBorderWidth;
821    specPtr->draw = templatePtr->draw;
822
823    elementPtr->specPtr = specPtr;
824    elementPtr->nbWidgetSpecs = 0;
825    elementPtr->widgetSpecs = NULL;
826
827    return elementId;
828}
829
830/*
831 *---------------------------------------------------------------------------
832 *
833 * GetStyledElement --
834 *
835 *	Get a registered implementation of an existing element for the given
836 *	style engine.
837 *
838 * Results:
839 *	The styled element descriptor, or NULL if not found.
840 *
841 * Side effects:
842 *	None.
843 *
844 *---------------------------------------------------------------------------
845 */
846
847static StyledElement *
848GetStyledElement(
849    StyleEngine *enginePtr,	/* Style engine providing the implementation.
850				 * NULL means the default system engine. */
851    int elementId)		/* Unique element ID */
852{
853    StyledElement *elementPtr;
854    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
855	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
856    StyleEngine *enginePtr2;
857
858    if (enginePtr == NULL) {
859	enginePtr = tsdPtr->defaultEnginePtr;
860    }
861
862    while (elementId >= 0 && elementId < tsdPtr->nbElements) {
863	/*
864	 * Look for an implemented element through the engine chain.
865	 */
866
867	enginePtr2 = enginePtr;
868	do {
869	    elementPtr = enginePtr2->elements+elementId;
870	    if (elementPtr->specPtr != NULL) {
871		return elementPtr;
872	    }
873	    enginePtr2 = enginePtr2->parentPtr;
874	} while (enginePtr2 != NULL);
875
876	/*
877	 * None found, try with the generic element.
878	 */
879
880	elementId = tsdPtr->elements[elementId].genericId;
881    }
882
883    /*
884     * No matching element found.
885     */
886
887    return NULL;
888}
889
890/*
891 *---------------------------------------------------------------------------
892 *
893 * InitWidgetSpec --
894 *
895 *	Initialize a newly allocated widget spec.
896 *
897 * Results:
898 *	None.
899 *
900 * Side effects:
901 *	Memory allocated.
902 *
903 *---------------------------------------------------------------------------
904 */
905
906static void
907InitWidgetSpec(
908    StyledWidgetSpec *widgetSpecPtr,
909				/* Points to an uninitialized widget spec. */
910    StyledElement *elementPtr,	/* Styled element descriptor. */
911    Tk_OptionTable optionTable)	/* The widget's option table. */
912{
913    int i, nbOptions;
914    Tk_ElementOptionSpec *elementOptionPtr;
915    CONST Tk_OptionSpec *widgetOptionPtr;
916
917    widgetSpecPtr->elementPtr = elementPtr;
918    widgetSpecPtr->optionTable = optionTable;
919
920    /*
921     * Count the number of options.
922     */
923
924    for (nbOptions = 0, elementOptionPtr = elementPtr->specPtr->options;
925	    elementOptionPtr->name != NULL; nbOptions++, elementOptionPtr++) {
926	/* empty body */
927    }
928
929    /*
930     * Build the widget option list.
931     */
932
933    widgetSpecPtr->optionsPtr = (CONST Tk_OptionSpec **)
934	    ckalloc(sizeof(Tk_OptionSpec *) * nbOptions);
935    for (i = 0, elementOptionPtr = elementPtr->specPtr->options;
936	    i < nbOptions; i++, elementOptionPtr++) {
937	widgetOptionPtr = TkGetOptionSpec(elementOptionPtr->name, optionTable);
938
939	/*
940	 * Check that the widget option type is compatible with one of the
941	 * element's required types.
942	 */
943
944	if (elementOptionPtr->type == TK_OPTION_END
945	    || elementOptionPtr->type == widgetOptionPtr->type) {
946	    widgetSpecPtr->optionsPtr[i] = widgetOptionPtr;
947	} else {
948	    widgetSpecPtr->optionsPtr[i] = NULL;
949	}
950    }
951}
952
953/*
954 *---------------------------------------------------------------------------
955 *
956 * FreeWidgetSpec --
957 *
958 *	Free a widget spec and its associated data.
959 *
960 * Results:
961 *	None
962 *
963 * Side effects:
964 *	Memory freed.
965 *
966 *---------------------------------------------------------------------------
967 */
968
969static void
970FreeWidgetSpec(
971    StyledWidgetSpec *widgetSpecPtr)
972				/* The widget spec to free. */
973{
974    ckfree((char *) widgetSpecPtr->optionsPtr);
975}
976
977/*
978 *---------------------------------------------------------------------------
979 *
980 * GetWidgetSpec --
981 *
982 *	Return a new or existing widget spec for the given element and widget
983 *	type (identified by its option table).
984 *
985 * Results:
986 *	A pointer to the matching widget spec.
987 *
988 * Side effects:
989 *	Memory may be allocated.
990 *
991 *---------------------------------------------------------------------------
992 */
993
994static StyledWidgetSpec *
995GetWidgetSpec(
996    StyledElement *elementPtr,	/* Styled element descriptor. */
997    Tk_OptionTable optionTable)	/* The widget's option table. */
998{
999    StyledWidgetSpec *widgetSpecPtr;
1000    int i;
1001
1002    /*
1003     * Try to find an existing widget spec.
1004     */
1005
1006    for (i = 0; i < elementPtr->nbWidgetSpecs; i++) {
1007	widgetSpecPtr = elementPtr->widgetSpecs+i;
1008	if (widgetSpecPtr->optionTable == optionTable) {
1009	    return widgetSpecPtr;
1010	}
1011    }
1012
1013    /*
1014     * Create and initialize a new widget spec.
1015     */
1016
1017    i = elementPtr->nbWidgetSpecs++;
1018    elementPtr->widgetSpecs = (StyledWidgetSpec *) ckrealloc(
1019	    (char *) elementPtr->widgetSpecs,
1020	    sizeof(StyledWidgetSpec) * elementPtr->nbWidgetSpecs);
1021    widgetSpecPtr = elementPtr->widgetSpecs+i;
1022    InitWidgetSpec(widgetSpecPtr, elementPtr, optionTable);
1023
1024    return widgetSpecPtr;
1025}
1026
1027/*
1028 *---------------------------------------------------------------------------
1029 *
1030 * Tk_GetStyledElement --
1031 *
1032 *	This function returns a styled instance of the given element.
1033 *
1034 * Results:
1035 *	None.
1036 *
1037 * Side effects:
1038 *	Cached data may be allocated or updated.
1039 *
1040 *---------------------------------------------------------------------------
1041 */
1042
1043Tk_StyledElement
1044Tk_GetStyledElement(
1045    Tk_Style style,		/* The widget style. */
1046    int elementId,		/* Unique element ID. */
1047    Tk_OptionTable optionTable)	/* Option table for the widget. */
1048{
1049    Style *stylePtr = (Style *) style;
1050    StyledElement *elementPtr;
1051
1052    /*
1053     * Get an element implementation and call corresponding hook.
1054     */
1055
1056    elementPtr = GetStyledElement((stylePtr?stylePtr->enginePtr:NULL),
1057	    elementId);
1058    if (!elementPtr) {
1059	return NULL;
1060    }
1061
1062    return (Tk_StyledElement) GetWidgetSpec(elementPtr, optionTable);
1063}
1064
1065/*
1066 *---------------------------------------------------------------------------
1067 *
1068 * Tk_GetElementSize --
1069 *
1070 *	This function computes the size of the given widget element according
1071 *	to its style.
1072 *
1073 * Results:
1074 *	None.
1075 *
1076 * Side effects:
1077 *	Cached data may be allocated or updated.
1078 *
1079 *---------------------------------------------------------------------------
1080 */
1081
1082void
1083Tk_GetElementSize(
1084    Tk_Style style,		/* The widget style. */
1085    Tk_StyledElement element,	/* The styled element, previously returned by
1086				 * Tk_GetStyledElement. */
1087    char *recordPtr,		/* The widget record. */
1088    Tk_Window tkwin,		/* The widget window. */
1089    int width, int height,	/* Requested size. */
1090    int inner,			/* If TRUE, compute the outer size according
1091				 * to the requested minimum inner size. If
1092				 * FALSE, compute the inner size according to
1093				 * the requested maximum outer size. */
1094    int *widthPtr, int *heightPtr)
1095				/* Returned size. */
1096{
1097    Style *stylePtr = (Style *) style;
1098    StyledWidgetSpec *widgetSpecPtr = (StyledWidgetSpec *) element;
1099
1100    widgetSpecPtr->elementPtr->specPtr->getSize(stylePtr->clientData,
1101	    recordPtr, widgetSpecPtr->optionsPtr, tkwin, width, height, inner,
1102	    widthPtr, heightPtr);
1103}
1104
1105/*
1106 *---------------------------------------------------------------------------
1107 *
1108 * Tk_GetElementBox --
1109 *
1110 *	This function computes the bounding or inscribed box coordinates of
1111 *	the given widget element according to its style and within the given
1112 *	limits.
1113 *
1114 * Results:
1115 *	None.
1116 *
1117 * Side effects:
1118 *	Cached data may be allocated or updated.
1119 *
1120 *---------------------------------------------------------------------------
1121 */
1122
1123void
1124Tk_GetElementBox(
1125    Tk_Style style,		/* The widget style. */
1126    Tk_StyledElement element,	/* The styled element, previously returned by
1127				 * Tk_GetStyledElement. */
1128    char *recordPtr,		/* The widget record. */
1129    Tk_Window tkwin,		/* The widget window. */
1130    int x, int y,		/* Top left corner of available area. */
1131    int width, int height,	/* Size of available area. */
1132    int inner,			/* Boolean. If TRUE, compute the bounding box
1133				 * according to the requested inscribed box
1134				 * size. If FALSE, compute the inscribed box
1135				 * according to the requested bounding box. */
1136    int *xPtr, int *yPtr,	/* Returned top left corner. */
1137    int *widthPtr, int *heightPtr)
1138				/* Returned size. */
1139{
1140    Style *stylePtr = (Style *) style;
1141    StyledWidgetSpec *widgetSpecPtr = (StyledWidgetSpec *) element;
1142
1143    widgetSpecPtr->elementPtr->specPtr->getBox(stylePtr->clientData,
1144	    recordPtr, widgetSpecPtr->optionsPtr, tkwin, x, y, width, height,
1145	    inner, xPtr, yPtr, widthPtr, heightPtr);
1146}
1147
1148/*
1149 *---------------------------------------------------------------------------
1150 *
1151 * Tk_GetElementBorderWidth --
1152 *
1153 *	This function computes the border widthof the given widget element
1154 *	according to its style and within the given limits.
1155 *
1156 * Results:
1157 *	Border width in pixels. This value is uniform for all four sides.
1158 *
1159 * Side effects:
1160 *	Cached data may be allocated or updated.
1161 *
1162 *---------------------------------------------------------------------------
1163 */
1164
1165int
1166Tk_GetElementBorderWidth(
1167    Tk_Style style,		/* The widget style. */
1168    Tk_StyledElement element,	/* The styled element, previously returned by
1169				 * Tk_GetStyledElement. */
1170    char *recordPtr,		/* The widget record. */
1171    Tk_Window tkwin)		/* The widget window. */
1172{
1173    Style *stylePtr = (Style *) style;
1174    StyledWidgetSpec *widgetSpecPtr = (StyledWidgetSpec *) element;
1175
1176    return widgetSpecPtr->elementPtr->specPtr->getBorderWidth(
1177	    stylePtr->clientData, recordPtr, widgetSpecPtr->optionsPtr, tkwin);
1178}
1179
1180/*
1181 *---------------------------------------------------------------------------
1182 *
1183 * Tk_DrawElement --
1184 *
1185 *	This function draw the given widget element in a given drawable area.
1186 *
1187 * Results:
1188 *	None
1189 *
1190 * Side effects:
1191 *	Cached data may be allocated or updated.
1192 *
1193 *---------------------------------------------------------------------------
1194 */
1195
1196void
1197Tk_DrawElement(
1198    Tk_Style style,		/* The widget style. */
1199    Tk_StyledElement element,	/* The styled element, previously returned by
1200				 * Tk_GetStyledElement. */
1201    char *recordPtr,		/* The widget record. */
1202    Tk_Window tkwin,		/* The widget window. */
1203    Drawable d,			/* Where to draw element. */
1204    int x, int y,		/* Top left corner of element. */
1205    int width, int height,	/* Size of element. */
1206    int state)			/* Drawing state flags. */
1207{
1208    Style *stylePtr = (Style *) style;
1209    StyledWidgetSpec *widgetSpecPtr = (StyledWidgetSpec *) element;
1210
1211    widgetSpecPtr->elementPtr->specPtr->draw(stylePtr->clientData,
1212	    recordPtr, widgetSpecPtr->optionsPtr, tkwin, d, x, y, width,
1213	    height, state);
1214}
1215
1216/*
1217 *---------------------------------------------------------------------------
1218 *
1219 * Tk_CreateStyle --
1220 *
1221 *	This function is called to create a new style as an instance of the
1222 *	given engine. Styles are stored in thread-local space.
1223 *
1224 * Results:
1225 *	The newly allocated style, or NULL if the style already exists.
1226 *
1227 * Side effects:
1228 *	Memory allocated. Data added to thread-local table.
1229 *
1230 *---------------------------------------------------------------------------
1231 */
1232
1233Tk_Style
1234Tk_CreateStyle(
1235    CONST char *name,		/* Name of the style to create. NULL or empty
1236				 * means the default system style. */
1237    Tk_StyleEngine engine,	/* The style engine. */
1238    ClientData clientData)	/* Private data passed as is to engine code. */
1239{
1240    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1241	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1242    Tcl_HashEntry *entryPtr;
1243    int newEntry;
1244    Style *stylePtr;
1245
1246    /*
1247     * Attempt to create a new entry in the style table.
1248     */
1249
1250    entryPtr = Tcl_CreateHashEntry(&tsdPtr->styleTable, (name?name:""),
1251	    &newEntry);
1252    if (!newEntry) {
1253	/*
1254	 * A style was already registered by that name.
1255	 */
1256
1257	return NULL;
1258    }
1259
1260    /*
1261     * Allocate and intitialize a new style.
1262     */
1263
1264    stylePtr = (Style *) ckalloc(sizeof(Style));
1265    InitStyle(stylePtr, Tcl_GetHashKey(&tsdPtr->styleTable, entryPtr),
1266	    (engine != NULL ? (StyleEngine *) engine :
1267		    tsdPtr->defaultEnginePtr),
1268	    clientData);
1269    Tcl_SetHashValue(entryPtr, (ClientData) stylePtr);
1270
1271    return (Tk_Style) stylePtr;
1272}
1273
1274/*
1275 *---------------------------------------------------------------------------
1276 *
1277 * Tk_NameOfStyle --
1278 *
1279 *	Given a style, return its registered name.
1280 *
1281 * Results:
1282 *	The return value is the name that was passed to Tk_CreateStyle() to
1283 *	create the style. The storage for the returned string is private (it
1284 *	points to the corresponding hash key) The caller should not modify
1285 *	this string.
1286 *
1287 * Side effects:
1288 *	None.
1289 *
1290 *---------------------------------------------------------------------------
1291 */
1292
1293CONST char *
1294Tk_NameOfStyle(
1295    Tk_Style style)		/* Style whose name is desired. */
1296{
1297    Style *stylePtr = (Style *) style;
1298
1299    return stylePtr->name;
1300}
1301
1302/*
1303 *---------------------------------------------------------------------------
1304 *
1305 * InitStyle --
1306 *
1307 *	Initialize a newly allocated style.
1308 *
1309 * Results:
1310 *	None.
1311 *
1312 * Side effects:
1313 *	None.
1314 *
1315 *---------------------------------------------------------------------------
1316 */
1317
1318static void
1319InitStyle(
1320    Style *stylePtr,		/* Points to an uninitialized style. */
1321    CONST char *name,		/* Name of the registered style. NULL or empty
1322				 * means the default system style. Usually
1323				 * points to the hash key. */
1324    StyleEngine *enginePtr,	/* The style engine. */
1325    ClientData clientData)	/* Private data passed as is to engine code. */
1326{
1327    stylePtr->name = name;
1328    stylePtr->enginePtr = enginePtr;
1329    stylePtr->clientData = clientData;
1330}
1331
1332/*
1333 *---------------------------------------------------------------------------
1334 *
1335 * Tk_GetStyle --
1336 *
1337 *	Retrieve a registered style by its name.
1338 *
1339 * Results:
1340 *	A pointer to the style engine, or NULL if none found. In the latter
1341 *	case and if the interp is not NULL, an error message is left in the
1342 *	interp's result.
1343 *
1344 * Side effects:
1345 *	None.
1346 *
1347 *---------------------------------------------------------------------------
1348 */
1349
1350Tk_Style
1351Tk_GetStyle(
1352    Tcl_Interp *interp,		/* Interp for error return. */
1353    CONST char *name)		/* Name of the style to retrieve. NULL or empty
1354				 * means the default system style. */
1355{
1356    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1357	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1358    Tcl_HashEntry *entryPtr;
1359    Style *stylePtr;
1360
1361    /*
1362     * Search for a corresponding entry in the style table.
1363     */
1364
1365    entryPtr = Tcl_FindHashEntry(&tsdPtr->styleTable, (name!=NULL?name:""));
1366    if (entryPtr == NULL) {
1367	if (interp != NULL) {
1368	    Tcl_AppendResult(interp, "style \"", name, "\" doesn't exist",
1369		    NULL);
1370	}
1371	return (Tk_Style) NULL;
1372    }
1373    stylePtr = (Style *) Tcl_GetHashValue(entryPtr);
1374
1375    return (Tk_Style) stylePtr;
1376}
1377
1378/*
1379 *---------------------------------------------------------------------------
1380 *
1381 * Tk_FreeStyle --
1382 *
1383 *	No-op. Present only for stubs compatibility.
1384 *
1385 *---------------------------------------------------------------------------
1386 */
1387
1388void
1389Tk_FreeStyle(
1390    Tk_Style style)
1391{
1392}
1393
1394/*
1395 *---------------------------------------------------------------------------
1396 *
1397 * Tk_AllocStyleFromObj --
1398 *
1399 *	Map the string name of a style to a corresponding Tk_Style. The style
1400 *	must have already been created by Tk_CreateStyle.
1401 *
1402 * Results:
1403 *	The return value is a token for the style that matches objPtr, or NULL
1404 *	if none found. If NULL is returned, an error message will be left in
1405 *	interp's result object.
1406 *
1407 *---------------------------------------------------------------------------
1408 */
1409
1410Tk_Style
1411Tk_AllocStyleFromObj(
1412    Tcl_Interp *interp,		/* Interp for error return. */
1413    Tcl_Obj *objPtr)		/* Object containing name of the style to
1414				 * retrieve. */
1415{
1416    Style *stylePtr;
1417
1418    if (objPtr->typePtr != &styleObjType) {
1419	SetStyleFromAny(interp, objPtr);
1420	stylePtr = (Style *) objPtr->internalRep.otherValuePtr;
1421    } else {
1422	stylePtr = (Style *) objPtr->internalRep.otherValuePtr;
1423    }
1424
1425    return (Tk_Style) stylePtr;
1426}
1427
1428/*
1429 *----------------------------------------------------------------------
1430 *
1431 * Tk_GetStyleFromObj --
1432 *
1433 *	Find the style that corresponds to a given object. The style must have
1434 *	already been created by Tk_CreateStyle.
1435 *
1436 * Results:
1437 *	The return value is a token for the style that matches objPtr, or NULL
1438 *	if none found.
1439 *
1440 * Side effects:
1441 *	If the object is not already a style ref, the conversion will free any
1442 *	old internal representation.
1443 *
1444 *----------------------------------------------------------------------
1445 */
1446
1447Tk_Style
1448Tk_GetStyleFromObj(
1449    Tcl_Obj *objPtr)		/* The object from which to get the style. */
1450{
1451    if (objPtr->typePtr != &styleObjType) {
1452	SetStyleFromAny(NULL, objPtr);
1453    }
1454
1455    return (Tk_Style) objPtr->internalRep.otherValuePtr;
1456}
1457
1458/*
1459 *---------------------------------------------------------------------------
1460 *
1461 * Tk_FreeStyleFromObj --
1462 *
1463 *	No-op. Present only for stubs compatibility.
1464 *
1465 *---------------------------------------------------------------------------
1466 */
1467void
1468Tk_FreeStyleFromObj(
1469    Tcl_Obj *objPtr)
1470{
1471}
1472
1473/*
1474 *----------------------------------------------------------------------
1475 *
1476 * SetStyleFromAny --
1477 *
1478 *	Convert the internal representation of a Tcl object to the style
1479 *	internal form.
1480 *
1481 * Results:
1482 *	Always returns TCL_OK. If an error occurs is returned (e.g. the style
1483 *	doesn't exist), an error message will be left in interp's result.
1484 *
1485 * Side effects:
1486 *	The object is left with its typePtr pointing to styleObjType.
1487 *
1488 *----------------------------------------------------------------------
1489 */
1490
1491static int
1492SetStyleFromAny(
1493    Tcl_Interp *interp,		/* Used for error reporting if not NULL. */
1494    Tcl_Obj *objPtr)		/* The object to convert. */
1495{
1496    const Tcl_ObjType *typePtr;
1497    const char *name;
1498
1499    /*
1500     * Free the old internalRep before setting the new one.
1501     */
1502
1503    name = Tcl_GetString(objPtr);
1504    typePtr = objPtr->typePtr;
1505    if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
1506	(*typePtr->freeIntRepProc)(objPtr);
1507    }
1508
1509    objPtr->typePtr = &styleObjType;
1510    objPtr->internalRep.otherValuePtr = (VOID *) Tk_GetStyle(interp, name);
1511
1512    return TCL_OK;
1513}
1514
1515/*
1516 *---------------------------------------------------------------------------
1517 *
1518 * FreeStyleObjProc --
1519 *
1520 *	This proc is called to release an object reference to a style. Called
1521 *	when the object's internal rep is released.
1522 *
1523 * Results:
1524 *	None.
1525 *
1526 *---------------------------------------------------------------------------
1527 */
1528
1529static void
1530FreeStyleObjProc(
1531    Tcl_Obj *objPtr)		/* The object we are releasing. */
1532{
1533    objPtr->internalRep.otherValuePtr = NULL;
1534    objPtr->typePtr = NULL;
1535}
1536
1537/*
1538 *---------------------------------------------------------------------------
1539 *
1540 * DupStyleObjProc --
1541 *
1542 *	When a cached style object is duplicated, this is called to update the
1543 *	internal reps.
1544 *
1545 *---------------------------------------------------------------------------
1546 */
1547
1548static void
1549DupStyleObjProc(
1550    Tcl_Obj *srcObjPtr,		/* The object we are copying from. */
1551    Tcl_Obj *dupObjPtr)		/* The object we are copying to. */
1552{
1553    dupObjPtr->typePtr = srcObjPtr->typePtr;
1554    dupObjPtr->internalRep.otherValuePtr=srcObjPtr->internalRep.otherValuePtr;
1555}
1556
1557/*
1558 * Local Variables:
1559 * mode: c
1560 * c-basic-offset: 4
1561 * fill-column: 78
1562 * End:
1563 */
1564