1/*
2 * $Id$
3 *
4 * Tk widget state utilities.
5 *
6 * Copyright (c) 2003 Joe English.  Freely redistributable.
7 *
8 */
9
10#include <string.h>
11
12#include <tk.h>
13#include "ttkTheme.h"
14
15/*
16 * Table of state names.  Must be kept in sync with TTK_STATE_*
17 * #defines in ttkTheme.h.
18 */
19static const char *const stateNames[] =
20{
21    "active",		/* Mouse cursor is over widget or element */
22    "disabled",		/* Widget is disabled */
23    "focus",		/* Widget has keyboard focus */
24    "pressed",		/* Pressed or "armed" */
25    "selected",		/* "on", "true", "current", etc. */
26    "background",	/* Top-level window lost focus (Mac,Win "inactive") */
27    "alternate",	/* Widget-specific alternate display style */
28    "invalid",		/* Bad value */
29    "readonly",		/* Editing/modification disabled */
30    "hover",		/* Mouse cursor is over widget */
31    "reserved1",	/* Reserved for future extension */
32    "reserved2",	/* Reserved for future extension */
33    "reserved3",	/* Reserved for future extension */
34    "user3",		/* User-definable state */
35    "user2",		/* User-definable state */
36    "user1",		/* User-definable state */
37    NULL
38};
39
40/*------------------------------------------------------------------------
41 * +++ StateSpec object type:
42 *
43 * The string representation consists of a list of state names,
44 * each optionally prefixed by an exclamation point (!).
45 *
46 * The internal representation uses the upper half of the longValue
47 * to store the on bits and the lower half to store the off bits.
48 * If we ever get more than 16 states, this will need to be reconsidered...
49 */
50
51static int  StateSpecSetFromAny(Tcl_Interp *interp, Tcl_Obj *obj);
52/* static void StateSpecFreeIntRep(Tcl_Obj *); */
53#define StateSpecFreeIntRep 0		/* not needed */
54static void StateSpecDupIntRep(Tcl_Obj *, Tcl_Obj *);
55static void StateSpecUpdateString(Tcl_Obj *);
56
57static
58struct Tcl_ObjType StateSpecObjType =
59{
60    "StateSpec",
61    StateSpecFreeIntRep,
62    StateSpecDupIntRep,
63    StateSpecUpdateString,
64    StateSpecSetFromAny
65};
66
67static void StateSpecDupIntRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr)
68{
69    copyPtr->internalRep.longValue = srcPtr->internalRep.longValue;
70    copyPtr->typePtr = &StateSpecObjType;
71}
72
73static int StateSpecSetFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr)
74{
75    int status;
76    int objc;
77    Tcl_Obj **objv;
78    int i;
79    unsigned int onbits = 0, offbits = 0;
80
81    status = Tcl_ListObjGetElements(interp, objPtr, &objc, &objv);
82    if (status != TCL_OK)
83	return status;
84
85    for (i = 0; i < objc; ++i) {
86	const char *stateName = Tcl_GetString(objv[i]);
87	int on, j;
88
89	if (*stateName == '!') {
90	    ++stateName;
91	    on = 0;
92	} else {
93	    on = 1;
94	}
95
96	for (j = 0; stateNames[j] != 0; ++j) {
97	    if (strcmp(stateName, stateNames[j]) == 0)
98		break;
99	}
100
101    	if (stateNames[j] == 0) {
102	    if (interp) {
103		Tcl_ResetResult(interp);
104		Tcl_AppendResult(interp, "Invalid state name ", stateName,NULL);
105	    }
106	    return TCL_ERROR;
107	}
108
109	if (on) {
110	    onbits |= (1<<j);
111	} else {
112	    offbits |= (1<<j);
113	}
114    }
115
116    /* Invalidate old intrep:
117     */
118    if (objPtr->typePtr && objPtr->typePtr->freeIntRepProc) {
119	objPtr->typePtr->freeIntRepProc(objPtr);
120    }
121
122    objPtr->typePtr = &StateSpecObjType;
123    objPtr->internalRep.longValue = (onbits << 16) | offbits;
124
125    return TCL_OK;
126}
127
128static void StateSpecUpdateString(Tcl_Obj *objPtr)
129{
130    unsigned int onbits = (objPtr->internalRep.longValue & 0xFFFF0000) >> 16;
131    unsigned int offbits = objPtr->internalRep.longValue & 0x0000FFFF;
132    unsigned int mask = onbits | offbits;
133    Tcl_DString result;
134    int i, len;
135
136    Tcl_DStringInit(&result);
137
138    for (i=0; stateNames[i] != NULL; ++i) {
139	if (mask & (1<<i)) {
140	    if (offbits & (1<<i))
141		Tcl_DStringAppend(&result, "!", 1);
142	    Tcl_DStringAppend(&result, stateNames[i], -1);
143	    Tcl_DStringAppend(&result, " ", 1);
144	}
145    }
146
147    len = Tcl_DStringLength(&result);
148    if (len) {
149	/* 'len' includes extra trailing ' ' */
150	objPtr->bytes = Tcl_Alloc((unsigned)len);
151	objPtr->length = len-1;
152	strncpy(objPtr->bytes, Tcl_DStringValue(&result), (size_t)len-1);
153	objPtr->bytes[len-1] = '\0';
154    } else {
155	/* empty string */
156	objPtr->length = 0;
157	objPtr->bytes = Tcl_Alloc(1);
158	*objPtr->bytes = '\0';
159    }
160
161    Tcl_DStringFree(&result);
162}
163
164Tcl_Obj *Ttk_NewStateSpecObj(unsigned int onbits, unsigned int offbits)
165{
166    Tcl_Obj *objPtr = Tcl_NewObj();
167
168    Tcl_InvalidateStringRep(objPtr);
169    objPtr->typePtr = &StateSpecObjType;
170    objPtr->internalRep.longValue = (onbits << 16) | offbits;
171
172    return objPtr;
173}
174
175int Ttk_GetStateSpecFromObj(
176    Tcl_Interp *interp,
177    Tcl_Obj *objPtr,
178    Ttk_StateSpec *spec)
179{
180    if (objPtr->typePtr != &StateSpecObjType) {
181	int status = StateSpecSetFromAny(interp, objPtr);
182	if (status != TCL_OK)
183	    return status;
184    }
185
186    spec->onbits = (objPtr->internalRep.longValue & 0xFFFF0000) >> 16;
187    spec->offbits = objPtr->internalRep.longValue & 0x0000FFFF;
188    return TCL_OK;
189}
190
191
192/*
193 * Tk_StateMapLookup --
194 *
195 * 	A state map is a paired list of StateSpec / value pairs.
196 *	Returns the value corresponding to the first matching state
197 *	specification, or NULL if not found or an error occurs.
198 */
199Tcl_Obj *Ttk_StateMapLookup(
200    Tcl_Interp *interp,		/* Where to leave error messages; may be NULL */
201    Ttk_StateMap map,		/* State map */
202    Ttk_State state)    	/* State to look up */
203{
204    Tcl_Obj **specs;
205    int nSpecs;
206    int j, status;
207
208    status = Tcl_ListObjGetElements(interp, map, &nSpecs, &specs);
209    if (status != TCL_OK)
210	return NULL;
211
212    for (j = 0; j < nSpecs; j += 2) {
213	Ttk_StateSpec spec;
214	status = Ttk_GetStateSpecFromObj(interp, specs[j], &spec);
215	if (status != TCL_OK)
216	    return NULL;
217	if (Ttk_StateMatches(state, &spec))
218	    return specs[j+1];
219    }
220    if (interp) {
221	Tcl_ResetResult(interp);
222	Tcl_AppendResult(interp, "No match in state map", NULL);
223    }
224    return NULL;
225}
226
227/* Ttk_GetStateMapFromObj --
228 * 	Returns a Ttk_StateMap from a Tcl_Obj*.
229 * 	Since a Ttk_StateMap is just a specially-formatted Tcl_Obj,
230 * 	this basically just checks for errors.
231 */
232Ttk_StateMap Ttk_GetStateMapFromObj(
233    Tcl_Interp *interp,		/* Where to leave error messages; may be NULL */
234    Tcl_Obj *mapObj)		/* State map */
235{
236    Tcl_Obj **specs;
237    int nSpecs;
238    int j, status;
239
240    status = Tcl_ListObjGetElements(interp, mapObj, &nSpecs, &specs);
241    if (status != TCL_OK)
242	return NULL;
243
244    if (nSpecs % 2 != 0) {
245	if (interp)
246	    Tcl_SetResult(interp,
247		    "State map must have an even number of elements",
248		    TCL_STATIC);
249	return 0;
250    }
251
252    for (j = 0; j < nSpecs; j += 2) {
253	Ttk_StateSpec spec;
254	if (Ttk_GetStateSpecFromObj(interp, specs[j], &spec) != TCL_OK)
255	    return NULL;
256    }
257
258    return mapObj;
259}
260
261/*
262 * Ttk_StateTableLooup --
263 * 	Look up an index from a statically allocated state table.
264 */
265int Ttk_StateTableLookup(Ttk_StateTable *map, unsigned int state)
266{
267    while ((state & map->onBits) != map->onBits
268	    || (~state & map->offBits) != map->offBits)
269    {
270	++map;
271    }
272    return map->index;
273}
274
275/*EOF*/
276