/* * tkStyle.c -- * * This file implements the widget styles and themes support. * * Copyright (c) 1990-1993 The Regents of the University of California. * Copyright (c) 1994-1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tkStyle.c,v 1.3 2003/01/21 20:24:45 hunt Exp $ */ #include "tkInt.h" /* * The following structure is used to cache widget option specs matching an * element's required options defined by Tk_ElementOptionSpecs. It also holds * information behind Tk_StyledElement opaque tokens. */ typedef struct StyledWidgetSpec { struct StyledElement *elementPtr; /* Pointer to the element holding this * structure. */ Tk_OptionTable optionTable; /* Option table for the widget class * using the element. */ CONST Tk_OptionSpec **optionsPtr; /* Table of option spec pointers, * matching the option list provided * during element registration. * Malloc'd. */ } StyledWidgetSpec; /* * Elements are declared using static templates. But static * information must be completed by dynamic information only * accessible at runtime. For each registered element, an instance of * the following structure is stored in each style engine and used to * cache information about the widget types (identified by their * optionTable) that use the given element. */ typedef struct StyledElement { struct Tk_ElementSpec *specPtr; /* Filled with template provided during * registration. NULL means no implementation * is available for the current engine. */ int nbWidgetSpecs; /* Size of the array below. Number of distinct * widget classes (actually, distinct option * tables) that used the element so far. */ StyledWidgetSpec *widgetSpecs; /* See above for the structure definition. * Table grows dynamically as new widgets * use the element. Malloc'd. */ } StyledElement; /* * The following structure holds information behind Tk_StyleEngine opaque * tokens. */ typedef struct StyleEngine { CONST char *name; /* Name of engine. Points to a hash key. */ StyledElement *elements; /* Table of widget element descriptors. Each * element is indexed by a unique system-wide * ID. Table grows dynamically as new elements * are registered. Malloc'd*/ struct StyleEngine *parentPtr; /* Parent engine. Engines may be layered to form * a fallback chain, terminated by the default * system engine. */ } StyleEngine; /* * Styles are instances of style engines. The following structure holds * information behind Tk_Style opaque tokens. */ typedef struct Style { int refCount; /* Number of active uses of this style. * If this count is 0, then this Style * structure is no longer valid. */ Tcl_HashEntry *hashPtr; /* Entry in style table for this structure, * used when deleting it. */ CONST char *name; /* Name of style. Points to a hash key. */ StyleEngine *enginePtr; /* Style engine of which the style is an * instance. */ ClientData clientData; /* Data provided during registration. */ } Style; /* * Each registered element uses an instance of the following structure. */ typedef struct Element { CONST char *name; /* Name of element. Points to a hash key. */ int id; /* Id of element. */ int genericId; /* Id of generic element. */ int created; /* Boolean, whether the element was created * explicitly (was registered) or implicitly * (by a derived element). */ } Element; /* * Thread-local data. */ typedef struct ThreadSpecificData { int nbInit; /* Number of calls to the init proc. */ Tcl_HashTable engineTable; /* Map a name to a style engine. Keys are * strings, values are Tk_StyleEngine * pointers. */ StyleEngine *defaultEnginePtr; /* Default, core-defined style engine. Global * fallback for all engines. */ Tcl_HashTable styleTable; /* Map a name to a style. Keys are strings, * values are Tk_Style pointers.*/ int nbElements; /* Size of the below tables. */ Tcl_HashTable elementTable; /* Map a name to an element Id. Keys are * strings, values are integer element IDs. */ Element *elements; /* Array of Elements. */ } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; /* * Forward declarations for procedures defined later in this file: */ /* TODO: sort alpha. */ static int CreateElement _ANSI_ARGS_((CONST char *name, int create)); static void DupStyleObjProc _ANSI_ARGS_((Tcl_Obj *srcObjPtr, Tcl_Obj *dupObjPtr)); static void FreeElement _ANSI_ARGS_((Element *elementPtr)); static void FreeStyle _ANSI_ARGS_((Style *stylePtr)); static void FreeStyledElement _ANSI_ARGS_(( StyledElement *elementPtr)); static void FreeStyleEngine _ANSI_ARGS_(( StyleEngine *enginePtr)); static void FreeStyleObjProc _ANSI_ARGS_((Tcl_Obj *objPtr)); static void FreeWidgetSpec _ANSI_ARGS_(( StyledWidgetSpec *widgetSpecPtr)); static StyledElement * GetStyledElement _ANSI_ARGS_(( StyleEngine *enginePtr, int elementId)); static StyledWidgetSpec * GetWidgetSpec _ANSI_ARGS_((StyledElement *elementPtr, Tk_OptionTable optionTable)); static void InitElement _ANSI_ARGS_((Element *elementPtr, CONST char *name, int id, int genericId, int created)); static void InitStyle _ANSI_ARGS_((Style *stylePtr, Tcl_HashEntry *hashPtr, CONST char *name, StyleEngine *enginePtr, ClientData clientData)); static void InitStyledElement _ANSI_ARGS_(( StyledElement *elementPtr)); static void InitStyleEngine _ANSI_ARGS_((StyleEngine *enginePtr, CONST char *name, StyleEngine *parentPtr)); static void InitWidgetSpec _ANSI_ARGS_(( StyledWidgetSpec *widgetSpecPtr, StyledElement *elementPtr, Tk_OptionTable optionTable)); static int SetStyleFromAny _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *objPtr)); /* * The following structure defines the implementation of the "style" Tcl * object, used for drawing. The internalRep.otherValuePtr field of * each style object points to the Style structure for the stylefont, or * NULL. */ static Tcl_ObjType styleObjType = { "style", /* name */ FreeStyleObjProc, /* freeIntRepProc */ DupStyleObjProc, /* dupIntRepProc */ NULL, /* updateStringProc */ SetStyleFromAny /* setFromAnyProc */ }; /* *--------------------------------------------------------------------------- * * TkStylePkgInit -- * * This procedure is called when an application is created. It * initializes all the structures that are used by the style * package on a per application basis. * * Results: * Stores data in thread-local storage. * * Side effects: * Memory allocated. * *--------------------------------------------------------------------------- */ void TkStylePkgInit(mainPtr) TkMainInfo *mainPtr; /* The application being created. */ { ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); if (tsdPtr->nbInit != 0) return; /* * Initialize tables. */ Tcl_InitHashTable(&tsdPtr->engineTable, TCL_STRING_KEYS); Tcl_InitHashTable(&tsdPtr->styleTable, TCL_STRING_KEYS); Tcl_InitHashTable(&tsdPtr->elementTable, TCL_STRING_KEYS); tsdPtr->nbElements = 0; tsdPtr->elements = NULL; /* * Create the default system engine. */ tsdPtr->defaultEnginePtr = (StyleEngine *) Tk_RegisterStyleEngine(NULL, NULL); /* * Create the default system style. */ Tk_CreateStyle(NULL, (Tk_StyleEngine) tsdPtr->defaultEnginePtr, (ClientData) 0); tsdPtr->nbInit++; } /* *--------------------------------------------------------------------------- * * TkStylePkgFree -- * * This procedure is called when an application is deleted. It * deletes all the structures that were used by the style package * for this application. * * Results: * None. * * Side effects: * Memory freed. * *--------------------------------------------------------------------------- */ void TkStylePkgFree(mainPtr) TkMainInfo *mainPtr; /* The application being deleted. */ { ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); Tcl_HashSearch search; Tcl_HashEntry *entryPtr; StyleEngine *enginePtr; int i; tsdPtr->nbInit--; if (tsdPtr->nbInit != 0) return; /* * Free styles. */ entryPtr = Tcl_FirstHashEntry(&tsdPtr->styleTable, &search); while (entryPtr != NULL) { ckfree((char *) Tcl_GetHashValue(entryPtr)); entryPtr = Tcl_NextHashEntry(&search); } Tcl_DeleteHashTable(&tsdPtr->styleTable); /* * Free engines. */ entryPtr = Tcl_FirstHashEntry(&tsdPtr->engineTable, &search); while (entryPtr != NULL) { enginePtr = (StyleEngine *) Tcl_GetHashValue(entryPtr); FreeStyleEngine(enginePtr); ckfree((char *) enginePtr); entryPtr = Tcl_NextHashEntry(&search); } Tcl_DeleteHashTable(&tsdPtr->engineTable); /* * Free elements. */ for (i = 0; i < tsdPtr->nbElements; i++) { FreeElement(tsdPtr->elements+i); } Tcl_DeleteHashTable(&tsdPtr->elementTable); ckfree((char *) tsdPtr->elements); } /* *--------------------------------------------------------------------------- * * Tk_RegisterStyleEngine -- * * This procedure is called to register a new style engine. Style engines * are stored in thread-local space. * * Results: * The newly allocated engine. * * Side effects: * Memory allocated. Data added to thread-local table. * *--------------------------------------------------------------------------- */ Tk_StyleEngine Tk_RegisterStyleEngine(name, parent) CONST char *name; /* Name of the engine to create. NULL or empty * means the default system engine. */ Tk_StyleEngine parent; /* The engine's parent. NULL means the default * system engine. */ { ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); Tcl_HashEntry *entryPtr; int newEntry; StyleEngine *enginePtr; /* * Attempt to create a new entry in the engine table. */ entryPtr = Tcl_CreateHashEntry(&tsdPtr->engineTable, (name?name:""), &newEntry); if (!newEntry) { /* * An engine was already registered by that name. */ return NULL; } /* * Allocate and intitialize a new engine. */ enginePtr = (StyleEngine *) ckalloc(sizeof(StyleEngine)); InitStyleEngine(enginePtr, Tcl_GetHashKey(&tsdPtr->engineTable, entryPtr), (StyleEngine *) parent); Tcl_SetHashValue(entryPtr, (ClientData) enginePtr); return (Tk_StyleEngine) enginePtr; } /* *--------------------------------------------------------------------------- * * InitStyleEngine -- * * Initialize a newly allocated style engine. * * Results: * None. * * Side effects: * Memory allocated. * *--------------------------------------------------------------------------- */ static void InitStyleEngine(enginePtr, name, parentPtr) StyleEngine *enginePtr; /* Points to an uninitialized engine. */ CONST char *name; /* Name of the registered engine. NULL or empty * means the default system engine. Usually * points to the hash key. */ StyleEngine *parentPtr; /* The engine's parent. NULL means the default * system engine. */ { ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); int elementId; if (name == NULL || *name == '\0') { /* * This is the default style engine. */ enginePtr->parentPtr = NULL; } else if (parentPtr == NULL) { /* * The default style engine is the parent. */ enginePtr->parentPtr = tsdPtr->defaultEnginePtr; } else { enginePtr->parentPtr = parentPtr; } /* * Allocate and initialize elements array. */ if (tsdPtr->nbElements > 0) { enginePtr->elements = (StyledElement *) ckalloc( sizeof(StyledElement) * tsdPtr->nbElements); for (elementId = 0; elementId < tsdPtr->nbElements; elementId++) { InitStyledElement(enginePtr->elements+elementId); } } else { enginePtr->elements = NULL; } } /* *--------------------------------------------------------------------------- * * FreeStyleEngine -- * * Free an engine and its associated data. * * Results: * None * * Side effects: * Memory freed. * *--------------------------------------------------------------------------- */ static void FreeStyleEngine(enginePtr) StyleEngine *enginePtr; /* The style engine to free. */ { ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); int elementId; /* * Free allocated elements. */ for (elementId = 0; elementId < tsdPtr->nbElements; elementId++) { FreeStyledElement(enginePtr->elements+elementId); } ckfree((char *) enginePtr->elements); } /* *--------------------------------------------------------------------------- * * Tk_GetStyleEngine -- * * Retrieve a registered style engine by its name. * * Results: * A pointer to the style engine, or NULL if none found. * * Side effects: * None. * *--------------------------------------------------------------------------- */ Tk_StyleEngine Tk_GetStyleEngine(name) CONST char *name; /* Name of the engine to retrieve. NULL or * empty means the default system engine. */ { ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); Tcl_HashEntry *entryPtr; if (name == NULL) { return (Tk_StyleEngine) tsdPtr->defaultEnginePtr; } entryPtr = Tcl_FindHashEntry(&tsdPtr->engineTable, (name?name:"")); if (!entryPtr) { return NULL; } return (Tk_StyleEngine) Tcl_GetHashValue(entryPtr); } /* *--------------------------------------------------------------------------- * * InitElement -- * * Initialize a newly allocated element. * * Results: * None. * * Side effects: * None. * *--------------------------------------------------------------------------- */ static void InitElement(elementPtr, name, id, genericId, created) Element *elementPtr; /* Points to an uninitialized element.*/ CONST char *name; /* Name of the registered element. Usually * points to the hash key. */ int id; /* Unique element ID. */ int genericId; /* ID of generic element. -1 means none. */ int created; /* Boolean, whether the element was created * explicitly (was registered) or implicitly * (by a derived element). */ { elementPtr->name = name; elementPtr->id = id; elementPtr->genericId = genericId; elementPtr->created = (created?1:0); } /* *--------------------------------------------------------------------------- * * FreeElement -- * * Free an element and its associated data. * * Results: * None. * * Side effects: * Memory freed. * *--------------------------------------------------------------------------- */ static void FreeElement(elementPtr) Element *elementPtr; /* The element to free. */ { /* Nothing to do. */ } /* *--------------------------------------------------------------------------- * * InitStyledElement -- * * Initialize a newly allocated styled element. * * Results: * None. * * Side effects: * None. * *--------------------------------------------------------------------------- */ static void InitStyledElement(elementPtr) StyledElement *elementPtr; /* Points to an uninitialized element.*/ { memset(elementPtr, 0, sizeof(StyledElement)); } /* *--------------------------------------------------------------------------- * * FreeStyledElement -- * * Free a styled element and its associated data. * * Results: * None. * * Side effects: * Memory freed. * *--------------------------------------------------------------------------- */ static void FreeStyledElement(elementPtr) StyledElement *elementPtr; /* The styled element to free. */ { int i; /* * Free allocated widget specs. */ for (i = 0; i < elementPtr->nbWidgetSpecs; i++) { FreeWidgetSpec(elementPtr->widgetSpecs+i); } ckfree((char *) elementPtr->widgetSpecs); } /* *--------------------------------------------------------------------------- * * CreateElement -- * * Find an existing or create a new element. * * Results: * The unique ID for the created or found element. * * Side effects: * Memory allocated. * *--------------------------------------------------------------------------- */ static int CreateElement(name, create) CONST char *name; /* Name of the element. */ int create; /* Boolean, whether the element is being created * explicitly (being registered) or implicitly (by a * derived element). */ { ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); Tcl_HashEntry *entryPtr, *engineEntryPtr; Tcl_HashSearch search; int newEntry; int elementId, genericId = -1; char *dot; StyleEngine *enginePtr; /* * Find or create the element. */ entryPtr = Tcl_CreateHashEntry(&tsdPtr->elementTable, name, &newEntry); if (!newEntry) { elementId = (int) Tcl_GetHashValue(entryPtr); if (create) { tsdPtr->elements[elementId].created = 1; } return elementId; } /* * The element didn't exist. If it's a derived element, find or * create its generic element ID. */ dot = strchr(name, '.'); if (dot) { genericId = CreateElement(dot+1, 0); } elementId = tsdPtr->nbElements++; Tcl_SetHashValue(entryPtr, (ClientData) elementId); /* * Reallocate element table. */ tsdPtr->elements = (Element *) ckrealloc((char *) tsdPtr->elements, sizeof(Element) * tsdPtr->nbElements); InitElement(tsdPtr->elements+elementId, Tcl_GetHashKey(&tsdPtr->elementTable, entryPtr), elementId, genericId, create); /* * Reallocate style engines' element table. */ engineEntryPtr = Tcl_FirstHashEntry(&tsdPtr->engineTable, &search); while (engineEntryPtr != NULL) { enginePtr = (StyleEngine *) Tcl_GetHashValue(engineEntryPtr); enginePtr->elements = (StyledElement *) ckrealloc( (char *) enginePtr->elements, sizeof(StyledElement) * tsdPtr->nbElements); InitStyledElement(enginePtr->elements+elementId); engineEntryPtr = Tcl_NextHashEntry(&search); } return elementId; } /* *--------------------------------------------------------------------------- * * Tk_GetElementId -- * * Find an existing element. * * Results: * The unique ID for the found element, or -1 if not found. * * Side effects: * Generic elements may be created. * *--------------------------------------------------------------------------- */ int Tk_GetElementId(name) CONST char *name; /* Name of the element. */ { ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); Tcl_HashEntry *entryPtr; int genericId = -1; char *dot; /* * Find the element Id. */ entryPtr = Tcl_FindHashEntry(&tsdPtr->elementTable, name); if (entryPtr) { return (int) Tcl_GetHashValue(entryPtr); } /* * Element not found. If the given name was derived, then first search for * the generic element. If found, create the new derived element. */ dot = strchr(name, '.'); if (!dot) { return -1; } genericId = Tk_GetElementId(dot+1); if (genericId == -1) { return -1; } if (!tsdPtr->elements[genericId].created) { /* * The generic element was created implicitly and thus has no real * existence. */ return -1; } else { /* * The generic element was created explicitly. Create the derived * element. */ return CreateElement(name, 1); } } /* *--------------------------------------------------------------------------- * * Tk_RegisterStyledElement -- * * Register an implementation of a new or existing element for the * given style engine. * * Results: * The unique ID for the created or found element. * * Side effects: * Elements may be created. Memory allocated. * *--------------------------------------------------------------------------- */ int Tk_RegisterStyledElement(engine, templatePtr) Tk_StyleEngine engine; /* Style engine providing the * implementation. */ Tk_ElementSpec *templatePtr; /* Static template information about * the element. */ { int elementId; StyledElement *elementPtr; Tk_ElementSpec *specPtr; int nbOptions; register Tk_ElementOptionSpec *srcOptions, *dstOptions; if (templatePtr->version != TK_STYLE_VERSION_1) { /* * Version mismatch. Do nothing. */ return -1; } if (engine == NULL) { engine = Tk_GetStyleEngine(NULL); } /* * Register the element, allocating storage in the various engines if * necessary. */ elementId = CreateElement(templatePtr->name, 1); /* * Initialize the styled element. */ elementPtr = ((StyleEngine *) engine)->elements+elementId; specPtr = (Tk_ElementSpec *) ckalloc(sizeof(Tk_ElementSpec)); specPtr->version = templatePtr->version; specPtr->name = ckalloc(strlen(templatePtr->name)+1); strcpy(specPtr->name, templatePtr->name); nbOptions = 0; for (nbOptions = 0, srcOptions = templatePtr->options; srcOptions->name != NULL; nbOptions++, srcOptions++); specPtr->options = (Tk_ElementOptionSpec *) ckalloc( sizeof(Tk_ElementOptionSpec) * (nbOptions+1)); for (srcOptions = templatePtr->options, dstOptions = specPtr->options; /* End condition within loop */; srcOptions++, dstOptions++) { if (srcOptions->name == NULL) { dstOptions->name = NULL; break; } dstOptions->name = ckalloc(strlen(srcOptions->name)+1); strcpy(dstOptions->name, srcOptions->name); dstOptions->type = srcOptions->type; } specPtr->getSize = templatePtr->getSize; specPtr->getBox = templatePtr->getBox; specPtr->getBorderWidth = templatePtr->getBorderWidth; specPtr->draw = templatePtr->draw; elementPtr->specPtr = specPtr; elementPtr->nbWidgetSpecs = 0; elementPtr->widgetSpecs = NULL; return elementId; } /* *--------------------------------------------------------------------------- * * GetStyledElement -- * * Get a registered implementation of an existing element for the * given style engine. * * Results: * The styled element descriptor, or NULL if not found. * * Side effects: * None. * *--------------------------------------------------------------------------- */ static StyledElement * GetStyledElement(enginePtr, elementId) StyleEngine *enginePtr; /* Style engine providing the implementation. * NULL means the default system engine. */ int elementId; /* Unique element ID */{ StyledElement *elementPtr; ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); StyleEngine *enginePtr2; if (enginePtr == NULL) { enginePtr = tsdPtr->defaultEnginePtr; } while (elementId >= 0 && elementId < tsdPtr->nbElements) { /* * Look for an implemented element through the engine chain. */ enginePtr2 = enginePtr; do { elementPtr = enginePtr2->elements+elementId; if (elementPtr->specPtr != NULL) { return elementPtr; } enginePtr2 = enginePtr2->parentPtr; } while (enginePtr2 != NULL); /* * None found, try with the generic element. */ elementId = tsdPtr->elements[elementId].genericId; } /* * No matching element found. */ return NULL; } /* *--------------------------------------------------------------------------- * * InitWidgetSpec -- * * Initialize a newly allocated widget spec. * * Results: * None. * * Side effects: * Memory allocated. * *--------------------------------------------------------------------------- */ static void InitWidgetSpec(widgetSpecPtr, elementPtr, optionTable) StyledWidgetSpec *widgetSpecPtr; /* Points to an uninitialized widget * spec. */ StyledElement *elementPtr; /* Styled element descriptor. */ Tk_OptionTable optionTable; /* The widget's option table. */ { int i, nbOptions; Tk_ElementOptionSpec *elementOptionPtr; CONST Tk_OptionSpec *widgetOptionPtr; widgetSpecPtr->elementPtr = elementPtr; widgetSpecPtr->optionTable = optionTable; /* * Count the number of options. */ for (nbOptions = 0, elementOptionPtr = elementPtr->specPtr->options; elementOptionPtr->name != NULL; nbOptions++, elementOptionPtr++) { } /* * Build the widget option list. */ widgetSpecPtr->optionsPtr = (CONST Tk_OptionSpec **) ckalloc( sizeof(Tk_OptionSpec *) * nbOptions); for (i = 0, elementOptionPtr = elementPtr->specPtr->options; i < nbOptions; i++, elementOptionPtr++) { widgetOptionPtr = TkGetOptionSpec(elementOptionPtr->name, optionTable); /* * Check that the widget option type is compatible with one of the * element's required types. */ if ( elementOptionPtr->type == TK_OPTION_END || elementOptionPtr->type == widgetOptionPtr->type) { widgetSpecPtr->optionsPtr[i] = widgetOptionPtr; } else { widgetSpecPtr->optionsPtr[i] = NULL; } } } /* *--------------------------------------------------------------------------- * * FreeWidgetSpec -- * * Free a widget spec and its associated data. * * Results: * None * * Side effects: * Memory freed. * *--------------------------------------------------------------------------- */ static void FreeWidgetSpec(widgetSpecPtr) StyledWidgetSpec *widgetSpecPtr; /* The widget spec to free. */ { ckfree((char *) widgetSpecPtr->optionsPtr); } /* *--------------------------------------------------------------------------- * * GetWidgetSpec -- * * Return a new or existing widget spec for the given element and * widget type (identified by its option table). * * Results: * A pointer to the matching widget spec. * * Side effects: * Memory may be allocated. * *--------------------------------------------------------------------------- */ static StyledWidgetSpec * GetWidgetSpec(elementPtr, optionTable) StyledElement *elementPtr; /* Styled element descriptor. */ Tk_OptionTable optionTable; /* The widget's option table. */ { StyledWidgetSpec *widgetSpecPtr; int i; /* * Try to find an existing widget spec. */ for (i = 0; i < elementPtr->nbWidgetSpecs; i++) { widgetSpecPtr = elementPtr->widgetSpecs+i; if (widgetSpecPtr->optionTable == optionTable) { return widgetSpecPtr; } } /* * Create and initialize a new widget spec. */ i = elementPtr->nbWidgetSpecs++; elementPtr->widgetSpecs = (StyledWidgetSpec *) ckrealloc( (char *) elementPtr->widgetSpecs, sizeof(StyledWidgetSpec) * elementPtr->nbWidgetSpecs); widgetSpecPtr = elementPtr->widgetSpecs+i; InitWidgetSpec(widgetSpecPtr, elementPtr, optionTable); return widgetSpecPtr; } /* *--------------------------------------------------------------------------- * * Tk_GetStyledElement -- * * This procedure returns a styled instance of the given element. * * Results: * None. * * Side effects: * Cached data may be allocated or updated. * *--------------------------------------------------------------------------- */ Tk_StyledElement Tk_GetStyledElement(style, elementId, optionTable) Tk_Style style; /* The widget style. */ int elementId; /* Unique element ID. */ Tk_OptionTable optionTable; /* Option table for the widget. */ { Style *stylePtr = (Style *) style; StyledElement *elementPtr; /* * Get an element implementation and call corresponding hook. */ elementPtr = GetStyledElement((stylePtr?stylePtr->enginePtr:NULL), elementId); if (!elementPtr) { return NULL; } return (Tk_StyledElement) GetWidgetSpec(elementPtr, optionTable); } /* *--------------------------------------------------------------------------- * * Tk_GetElementSize -- * * This procedure computes the size of the given widget element according * to its style. * * Results: * None. * * Side effects: * Cached data may be allocated or updated. * *--------------------------------------------------------------------------- */ void Tk_GetElementSize(style, element, recordPtr, tkwin, width, height, inner, widthPtr, heightPtr) Tk_Style style; /* The widget style. */ Tk_StyledElement element; /* The styled element, previously * returned by Tk_GetStyledElement. */ char *recordPtr; /* The widget record. */ Tk_Window tkwin; /* The widget window. */ int width, height; /* Requested size. */ int inner; /* Boolean. If TRUE, compute the outer * size according to the requested * minimum inner size. If FALSE, compute * the inner size according to the * requested maximum outer size. */ int *widthPtr, *heightPtr; /* Returned size. */ { Style *stylePtr = (Style *) style; StyledWidgetSpec *widgetSpecPtr = (StyledWidgetSpec *) element; widgetSpecPtr->elementPtr->specPtr->getSize(stylePtr->clientData, recordPtr, widgetSpecPtr->optionsPtr, tkwin, width, height, inner, widthPtr, heightPtr); } /* *--------------------------------------------------------------------------- * * Tk_GetElementBox -- * * This procedure computes the bounding or inscribed box coordinates * of the given widget element according to its style and within the * given limits. * * Results: * None. * * Side effects: * Cached data may be allocated or updated. * *--------------------------------------------------------------------------- */ void Tk_GetElementBox(style, element, recordPtr, tkwin, x, y, width, height, inner, xPtr, yPtr, widthPtr, heightPtr) Tk_Style style; /* The widget style. */ Tk_StyledElement element; /* The styled element, previously * returned by Tk_GetStyledElement. */ char *recordPtr; /* The widget record. */ Tk_Window tkwin; /* The widget window. */ int x, y; /* Top left corner of available area. */ int width, height; /* Size of available area. */ int inner; /* Boolean. If TRUE, compute the * bounding box according to the * requested inscribed box size. If * FALSE, compute the inscribed box * according to the requested bounding * box. */ int *xPtr, *yPtr; /* Returned top left corner. */ int *widthPtr, *heightPtr; /* Returned size. */ { Style *stylePtr = (Style *) style; StyledWidgetSpec *widgetSpecPtr = (StyledWidgetSpec *) element; widgetSpecPtr->elementPtr->specPtr->getBox(stylePtr->clientData, recordPtr, widgetSpecPtr->optionsPtr, tkwin, x, y, width, height, inner, xPtr, yPtr, widthPtr, heightPtr); } /* *--------------------------------------------------------------------------- * * Tk_GetElementBorderWidth -- * * This procedure computes the border widthof the given widget element * according to its style and within the given limits. * * Results: * Border width in pixels. This value is uniform for all four sides. * * Side effects: * Cached data may be allocated or updated. * *--------------------------------------------------------------------------- */ int Tk_GetElementBorderWidth(style, element, recordPtr, tkwin) Tk_Style style; /* The widget style. */ Tk_StyledElement element; /* The styled element, previously * returned by Tk_GetStyledElement. */ char *recordPtr; /* The widget record. */ Tk_Window tkwin; /* The widget window. */ { Style *stylePtr = (Style *) style; StyledWidgetSpec *widgetSpecPtr = (StyledWidgetSpec *) element; return widgetSpecPtr->elementPtr->specPtr->getBorderWidth( stylePtr->clientData, recordPtr, widgetSpecPtr->optionsPtr, tkwin); } /* *--------------------------------------------------------------------------- * * Tk_DrawElement -- * * This procedure draw the given widget element in a given drawable area. * * Results: * None * * Side effects: * Cached data may be allocated or updated. * *--------------------------------------------------------------------------- */ void Tk_DrawElement(style, element, recordPtr, tkwin, d, x, y, width, height, state) Tk_Style style; /* The widget style. */ Tk_StyledElement element; /* The styled element, previously * returned by Tk_GetStyledElement. */ char *recordPtr; /* The widget record. */ Tk_Window tkwin; /* The widget window. */ Drawable d; /* Where to draw element. */ int x, y; /* Top left corner of element. */ int width, height; /* Size of element. */ int state; /* Drawing state flags. */ { Style *stylePtr = (Style *) style; StyledWidgetSpec *widgetSpecPtr = (StyledWidgetSpec *) element; widgetSpecPtr->elementPtr->specPtr->draw(stylePtr->clientData, recordPtr, widgetSpecPtr->optionsPtr, tkwin, d, x, y, width, height, state); } /* *--------------------------------------------------------------------------- * * Tk_CreateStyle -- * * This procedure is called to create a new style as an instance of the * given engine. Styles are stored in thread-local space. * * Results: * The newly allocated style. * * Side effects: * Memory allocated. Data added to thread-local table. The style's * refCount is incremented. * *--------------------------------------------------------------------------- */ Tk_Style Tk_CreateStyle(name, engine, clientData) CONST char *name; /* Name of the style to create. NULL or empty * means the default system style. */ Tk_StyleEngine engine; /* The style engine. */ ClientData clientData; /* Private data passed as is to engine code. */ { ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); Tcl_HashEntry *entryPtr; int newEntry; Style *stylePtr; /* * Attempt to create a new entry in the style table. */ entryPtr = Tcl_CreateHashEntry(&tsdPtr->styleTable, (name?name:""), &newEntry); if (!newEntry) { /* * A style was already registered by that name. */ return NULL; } /* * Allocate and intitialize a new style. */ stylePtr = (Style *) ckalloc(sizeof(Style)); InitStyle(stylePtr, entryPtr, Tcl_GetHashKey(&tsdPtr->styleTable, entryPtr), (engine?(StyleEngine *) engine:tsdPtr->defaultEnginePtr), clientData); Tcl_SetHashValue(entryPtr, (ClientData) stylePtr); stylePtr->refCount++; return (Tk_Style) stylePtr; } /* *--------------------------------------------------------------------------- * * Tk_NameOfStyle -- * * Given a style, return its registered name. * * Results: * The return value is the name that was passed to Tk_CreateStyle() to * create the style. The storage for the returned string is private * (it points to the corresponding hash key) The caller should not modify * this string. * * Side effects: * None. * *--------------------------------------------------------------------------- */ CONST char * Tk_NameOfStyle(style) Tk_Style style; /* Style whose name is desired. */ { Style *stylePtr = (Style *) style; return stylePtr->name; } /* *--------------------------------------------------------------------------- * * InitStyle -- * * Initialize a newly allocated style. * * Results: * None. * * Side effects: * None. * *--------------------------------------------------------------------------- */ static void InitStyle(stylePtr, hashPtr, name, enginePtr, clientData) Style *stylePtr; /* Points to an uninitialized style. */ Tcl_HashEntry *hashPtr; /* Hash entry for the registered style. */ CONST char *name; /* Name of the registered style. NULL or empty * means the default system style. Usually * points to the hash key. */ StyleEngine *enginePtr; /* The style engine. */ ClientData clientData; /* Private data passed as is to engine code. */ { stylePtr->refCount = 0; stylePtr->hashPtr = hashPtr; stylePtr->name = name; stylePtr->enginePtr = enginePtr; stylePtr->clientData = clientData; } /* *--------------------------------------------------------------------------- * * FreeStyle -- * * Free a style and its associated data. * * Results: * None * * Side effects: * None. * *--------------------------------------------------------------------------- */ static void FreeStyle(stylePtr) Style *stylePtr; /* The style to free. */ { /* Nothing to do. */ } /* *--------------------------------------------------------------------------- * * Tk_GetStyle -- * * Retrieve a registered style by its name. * * Results: * A pointer to the style engine, or NULL if none found. In the latter * case and if the interp is not NULL, an error message is left in the * interp's result. * * Side effects: * None. * *--------------------------------------------------------------------------- */ Tk_Style Tk_GetStyle(interp, name) Tcl_Interp *interp; /* Interp for error return. */ CONST char *name; /* Name of the style to retrieve. NULL or empty * means the default system style. */ { ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); Tcl_HashEntry *entryPtr; Style *stylePtr; /* * Search for a corresponding entry in the style table. */ entryPtr = Tcl_FindHashEntry(&tsdPtr->styleTable, (name?name:"")); if (entryPtr == NULL) { if (interp != NULL) { Tcl_AppendResult(interp, "style \"", name, "\" doesn't exist", NULL); } return (Tk_Style) NULL; } stylePtr = (Style *) Tcl_GetHashValue(entryPtr); stylePtr->refCount++; return (Tk_Style) stylePtr; } /* *--------------------------------------------------------------------------- * * Tk_FreeStyle -- * * Free a style previously created by Tk_CreateStyle. * * Results: * None * * Side effects: * The style's refCount is decremented. If it reaches zero, the style * is freed. * *--------------------------------------------------------------------------- */ void Tk_FreeStyle(style) Tk_Style style; /* The style to free. */ { Style *stylePtr = (Style *) style; if (stylePtr == NULL) { return; } stylePtr->refCount--; if (stylePtr->refCount > 0) { return; } /* * Keep the default style alive. */ if (*stylePtr->name == '\0') { stylePtr->refCount = 1; return; } Tcl_DeleteHashEntry(stylePtr->hashPtr); FreeStyle(stylePtr); ckfree((char *) stylePtr); } /* *--------------------------------------------------------------------------- * * Tk_AllocStyleFromObj -- * * Map the string name of a style to a corresponding Tk_Style. The style * must have already been created by Tk_CreateStyle. * * Results: * The return value is a token for the style that matches objPtr, or * NULL if none found. If NULL is returned, an error message will be * left in interp's result object. * * Side effects: * The style's reference count is incremented. For each call to this * procedure, there should eventually be a call to Tk_FreeStyle() or * Tk_FreeStyleFromObj() so that the database is cleaned up when styles * aren't in use anymore. * *--------------------------------------------------------------------------- */ Tk_Style Tk_AllocStyleFromObj(interp, objPtr) Tcl_Interp *interp; /* Interp for error return. */ Tcl_Obj *objPtr; /* Object containing name of the style to * retrieve. */ { Style *stylePtr; if (objPtr->typePtr != &styleObjType) { SetStyleFromAny(interp, objPtr); stylePtr = (Style *) objPtr->internalRep.otherValuePtr; } else { stylePtr = (Style *) objPtr->internalRep.otherValuePtr; stylePtr->refCount++; } return (Tk_Style) stylePtr; } /* *---------------------------------------------------------------------- * * Tk_GetStyleFromObj -- * * Find the style that corresponds to a given object. The style must * have already been created by Tk_CreateStyle. * * Results: * The return value is a token for the style that matches objPtr, or * NULL if none found. * * Side effects: * If the object is not already a style ref, the conversion will free * any old internal representation. * *---------------------------------------------------------------------- */ Tk_Style Tk_GetStyleFromObj(objPtr) Tcl_Obj *objPtr; /* The object from which to get the style. */ { if (objPtr->typePtr != &styleObjType) { SetStyleFromAny((Tcl_Interp *) NULL, objPtr); } return (Tk_Style) objPtr->internalRep.otherValuePtr; } /* *--------------------------------------------------------------------------- * * Tk_FreeStyleFromObj -- * * Called to release a style inside a Tcl_Obj *. * * Results: * None. * * Side effects: * If the object is a style ref, the conversion will free its * internal representation. * *--------------------------------------------------------------------------- */ void Tk_FreeStyleFromObj(objPtr) Tcl_Obj *objPtr; /* The Tcl_Obj * to be freed. */ { if (objPtr->typePtr == &styleObjType) { FreeStyleObjProc(objPtr); } } /* *---------------------------------------------------------------------- * * SetStyleFromAny -- * * Convert the internal representation of a Tcl object to the * style internal form. * * Results: * Always returns TCL_OK. If an error occurs is returned (e.g. the * style doesn't exist), an error message will be left in interp's * result. * * Side effects: * The object is left with its typePtr pointing to styleObjType. * The reference count is incremented (in Tk_GetStyle()). * *---------------------------------------------------------------------- */ static int SetStyleFromAny(interp, objPtr) Tcl_Interp *interp; /* Used for error reporting if not NULL. */ Tcl_Obj *objPtr; /* The object to convert. */ { Tcl_ObjType *typePtr; char *name; /* * Free the old internalRep before setting the new one. */ name = Tcl_GetString(objPtr); typePtr = objPtr->typePtr; if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) { (*typePtr->freeIntRepProc)(objPtr); } objPtr->typePtr = &styleObjType; objPtr->internalRep.otherValuePtr = (VOID *) Tk_GetStyle(interp, name); return TCL_OK; } /* *--------------------------------------------------------------------------- * * FreeStyleObjProc -- * * This proc is called to release an object reference to a style. * Called when the object's internal rep is released. * * Results: * None. * * Side effects: * The reference count is decremented (in Tk_FreeStyle()). * *--------------------------------------------------------------------------- */ static void FreeStyleObjProc(objPtr) Tcl_Obj *objPtr; /* The object we are releasing. */ { Style *stylePtr = (Style *) objPtr->internalRep.otherValuePtr; if (stylePtr != NULL) { Tk_FreeStyle((Tk_Style) stylePtr); objPtr->internalRep.otherValuePtr = NULL; } } /* *--------------------------------------------------------------------------- * * DupStyleObjProc -- * * When a cached style object is duplicated, this is called to * update the internal reps. * * Results: * None. * * Side effects: * The style's refCount is incremented and the internal rep of the copy * is set to point to it. * *--------------------------------------------------------------------------- */ static void DupStyleObjProc(srcObjPtr, dupObjPtr) Tcl_Obj *srcObjPtr; /* The object we are copying from. */ Tcl_Obj *dupObjPtr; /* The object we are copying to. */ { Style *stylePtr = (Style *) srcObjPtr->internalRep.otherValuePtr; dupObjPtr->typePtr = srcObjPtr->typePtr; dupObjPtr->internalRep.otherValuePtr = (VOID *) stylePtr; if (stylePtr != NULL) { stylePtr->refCount++; } }