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