274 lines
6.8 KiB
C
274 lines
6.8 KiB
C
/*
|
|
* Tk widget state utilities.
|
|
*
|
|
* Copyright (c) 2003 Joe English. Freely redistributable.
|
|
*
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include <tk.h>
|
|
#include "ttkTheme.h"
|
|
|
|
/*
|
|
* Table of state names. Must be kept in sync with TTK_STATE_*
|
|
* #defines in ttkTheme.h.
|
|
*/
|
|
static const char *const stateNames[] =
|
|
{
|
|
"active", /* Mouse cursor is over widget or element */
|
|
"disabled", /* Widget is disabled */
|
|
"focus", /* Widget has keyboard focus */
|
|
"pressed", /* Pressed or "armed" */
|
|
"selected", /* "on", "true", "current", etc. */
|
|
"background", /* Top-level window lost focus (Mac,Win "inactive") */
|
|
"alternate", /* Widget-specific alternate display style */
|
|
"invalid", /* Bad value */
|
|
"readonly", /* Editing/modification disabled */
|
|
"hover", /* Mouse cursor is over widget */
|
|
"reserved1", /* Reserved for future extension */
|
|
"reserved2", /* Reserved for future extension */
|
|
"reserved3", /* Reserved for future extension */
|
|
"user3", /* User-definable state */
|
|
"user2", /* User-definable state */
|
|
"user1", /* User-definable state */
|
|
NULL
|
|
};
|
|
|
|
/*------------------------------------------------------------------------
|
|
* +++ StateSpec object type:
|
|
*
|
|
* The string representation consists of a list of state names,
|
|
* each optionally prefixed by an exclamation point (!).
|
|
*
|
|
* The internal representation uses the upper half of the longValue
|
|
* to store the on bits and the lower half to store the off bits.
|
|
* If we ever get more than 16 states, this will need to be reconsidered...
|
|
*/
|
|
|
|
static int StateSpecSetFromAny(Tcl_Interp *interp, Tcl_Obj *obj);
|
|
/* static void StateSpecFreeIntRep(Tcl_Obj *); */
|
|
#define StateSpecFreeIntRep 0 /* not needed */
|
|
static void StateSpecDupIntRep(Tcl_Obj *, Tcl_Obj *);
|
|
static void StateSpecUpdateString(Tcl_Obj *);
|
|
|
|
static
|
|
struct Tcl_ObjType StateSpecObjType =
|
|
{
|
|
"StateSpec",
|
|
StateSpecFreeIntRep,
|
|
StateSpecDupIntRep,
|
|
StateSpecUpdateString,
|
|
StateSpecSetFromAny
|
|
};
|
|
|
|
static void StateSpecDupIntRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr)
|
|
{
|
|
copyPtr->internalRep.longValue = srcPtr->internalRep.longValue;
|
|
copyPtr->typePtr = &StateSpecObjType;
|
|
}
|
|
|
|
static int StateSpecSetFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr)
|
|
{
|
|
int status;
|
|
int objc;
|
|
Tcl_Obj **objv;
|
|
int i;
|
|
unsigned int onbits = 0, offbits = 0;
|
|
|
|
status = Tcl_ListObjGetElements(interp, objPtr, &objc, &objv);
|
|
if (status != TCL_OK)
|
|
return status;
|
|
|
|
for (i = 0; i < objc; ++i) {
|
|
const char *stateName = Tcl_GetString(objv[i]);
|
|
int on, j;
|
|
|
|
if (*stateName == '!') {
|
|
++stateName;
|
|
on = 0;
|
|
} else {
|
|
on = 1;
|
|
}
|
|
|
|
for (j = 0; stateNames[j] != 0; ++j) {
|
|
if (strcmp(stateName, stateNames[j]) == 0)
|
|
break;
|
|
}
|
|
|
|
if (stateNames[j] == 0) {
|
|
if (interp) {
|
|
Tcl_ResetResult(interp);
|
|
Tcl_AppendResult(interp, "Invalid state name ", stateName,NULL);
|
|
}
|
|
return TCL_ERROR;
|
|
}
|
|
|
|
if (on) {
|
|
onbits |= (1<<j);
|
|
} else {
|
|
offbits |= (1<<j);
|
|
}
|
|
}
|
|
|
|
/* Invalidate old intrep:
|
|
*/
|
|
if (objPtr->typePtr && objPtr->typePtr->freeIntRepProc) {
|
|
objPtr->typePtr->freeIntRepProc(objPtr);
|
|
}
|
|
|
|
objPtr->typePtr = &StateSpecObjType;
|
|
objPtr->internalRep.longValue = (onbits << 16) | offbits;
|
|
|
|
return TCL_OK;
|
|
}
|
|
|
|
static void StateSpecUpdateString(Tcl_Obj *objPtr)
|
|
{
|
|
unsigned int onbits = (objPtr->internalRep.longValue & 0xFFFF0000) >> 16;
|
|
unsigned int offbits = objPtr->internalRep.longValue & 0x0000FFFF;
|
|
unsigned int mask = onbits | offbits;
|
|
Tcl_DString result;
|
|
int i, len;
|
|
|
|
Tcl_DStringInit(&result);
|
|
|
|
for (i=0; stateNames[i] != NULL; ++i) {
|
|
if (mask & (1<<i)) {
|
|
if (offbits & (1<<i))
|
|
Tcl_DStringAppend(&result, "!", 1);
|
|
Tcl_DStringAppend(&result, stateNames[i], -1);
|
|
Tcl_DStringAppend(&result, " ", 1);
|
|
}
|
|
}
|
|
|
|
len = Tcl_DStringLength(&result);
|
|
if (len) {
|
|
/* 'len' includes extra trailing ' ' */
|
|
objPtr->bytes = Tcl_Alloc((unsigned)len);
|
|
objPtr->length = len-1;
|
|
strncpy(objPtr->bytes, Tcl_DStringValue(&result), (size_t)len-1);
|
|
objPtr->bytes[len-1] = '\0';
|
|
} else {
|
|
/* empty string */
|
|
objPtr->length = 0;
|
|
objPtr->bytes = Tcl_Alloc(1);
|
|
*objPtr->bytes = '\0';
|
|
}
|
|
|
|
Tcl_DStringFree(&result);
|
|
}
|
|
|
|
Tcl_Obj *Ttk_NewStateSpecObj(unsigned int onbits, unsigned int offbits)
|
|
{
|
|
Tcl_Obj *objPtr = Tcl_NewObj();
|
|
|
|
Tcl_InvalidateStringRep(objPtr);
|
|
objPtr->typePtr = &StateSpecObjType;
|
|
objPtr->internalRep.longValue = (onbits << 16) | offbits;
|
|
|
|
return objPtr;
|
|
}
|
|
|
|
int Ttk_GetStateSpecFromObj(
|
|
Tcl_Interp *interp,
|
|
Tcl_Obj *objPtr,
|
|
Ttk_StateSpec *spec)
|
|
{
|
|
if (objPtr->typePtr != &StateSpecObjType) {
|
|
int status = StateSpecSetFromAny(interp, objPtr);
|
|
if (status != TCL_OK)
|
|
return status;
|
|
}
|
|
|
|
spec->onbits = (objPtr->internalRep.longValue & 0xFFFF0000) >> 16;
|
|
spec->offbits = objPtr->internalRep.longValue & 0x0000FFFF;
|
|
return TCL_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
* Tk_StateMapLookup --
|
|
*
|
|
* A state map is a paired list of StateSpec / value pairs.
|
|
* Returns the value corresponding to the first matching state
|
|
* specification, or NULL if not found or an error occurs.
|
|
*/
|
|
Tcl_Obj *Ttk_StateMapLookup(
|
|
Tcl_Interp *interp, /* Where to leave error messages; may be NULL */
|
|
Ttk_StateMap map, /* State map */
|
|
Ttk_State state) /* State to look up */
|
|
{
|
|
Tcl_Obj **specs;
|
|
int nSpecs;
|
|
int j, status;
|
|
|
|
status = Tcl_ListObjGetElements(interp, map, &nSpecs, &specs);
|
|
if (status != TCL_OK)
|
|
return NULL;
|
|
|
|
for (j = 0; j < nSpecs; j += 2) {
|
|
Ttk_StateSpec spec;
|
|
status = Ttk_GetStateSpecFromObj(interp, specs[j], &spec);
|
|
if (status != TCL_OK)
|
|
return NULL;
|
|
if (Ttk_StateMatches(state, &spec))
|
|
return specs[j+1];
|
|
}
|
|
if (interp) {
|
|
Tcl_ResetResult(interp);
|
|
Tcl_AppendResult(interp, "No match in state map", NULL);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Ttk_GetStateMapFromObj --
|
|
* Returns a Ttk_StateMap from a Tcl_Obj*.
|
|
* Since a Ttk_StateMap is just a specially-formatted Tcl_Obj,
|
|
* this basically just checks for errors.
|
|
*/
|
|
Ttk_StateMap Ttk_GetStateMapFromObj(
|
|
Tcl_Interp *interp, /* Where to leave error messages; may be NULL */
|
|
Tcl_Obj *mapObj) /* State map */
|
|
{
|
|
Tcl_Obj **specs;
|
|
int nSpecs;
|
|
int j, status;
|
|
|
|
status = Tcl_ListObjGetElements(interp, mapObj, &nSpecs, &specs);
|
|
if (status != TCL_OK)
|
|
return NULL;
|
|
|
|
if (nSpecs % 2 != 0) {
|
|
if (interp)
|
|
Tcl_SetResult(interp,
|
|
"State map must have an even number of elements",
|
|
TCL_STATIC);
|
|
return 0;
|
|
}
|
|
|
|
for (j = 0; j < nSpecs; j += 2) {
|
|
Ttk_StateSpec spec;
|
|
if (Ttk_GetStateSpecFromObj(interp, specs[j], &spec) != TCL_OK)
|
|
return NULL;
|
|
}
|
|
|
|
return mapObj;
|
|
}
|
|
|
|
/*
|
|
* Ttk_StateTableLooup --
|
|
* Look up an index from a statically allocated state table.
|
|
*/
|
|
int Ttk_StateTableLookup(Ttk_StateTable *map, unsigned int state)
|
|
{
|
|
while ((state & map->onBits) != map->onBits
|
|
|| (~state & map->offBits) != map->offBits)
|
|
{
|
|
++map;
|
|
}
|
|
return map->index;
|
|
}
|
|
|
|
/*EOF*/
|