Tk_Uid Tk_GetOption( Tk_Window tkwin, /* Token for window that option is associated * with. */ CONST char *name, /* Name of option. */ CONST char *className) /* Class of option. NULL means there is no * class for this option: just check for * name. */ { Tk_Uid nameId, classId = NULL; char *masqName; register Element *elPtr, *bestPtr; register int count; StackLevel *levelPtr; int stackDepth[NUM_STACKS]; ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); /* * Note: no need to call OptionInit here: it will be done by the * SetupStacks call below (squeeze out those nanoseconds). */ if (tkwin != (Tk_Window) tsdPtr->cachedWindow) { SetupStacks((TkWindow *) tkwin, 1); } /* * Get a default "best" match. */ bestPtr = &tsdPtr->defaultMatch; /* * For megawidget support, we want to have some widget options masquerade * as options for other widgets. For example, a combobox has a button in * it; this button ought to pick up the *Button.background, etc., options. * But because the class of the widget is Combobox, our normal search * won't get that option. * * To work around this, the option name field syntax was extended to allow * for a "." in the name; if this character occurs in the name, then it * indicates that this name contains a new window class and an option * name, ie, "Button.foreground". If we see this form in the name field, * we query the option database directly (since the option stacks will not * have the information we need). */ masqName = strchr(name, (int)'.'); if (masqName != NULL) { /* * This option is masquerading with a different window class. Search * the stack to the depth it was before the current window's * information was pushed (the value for which is stored in the bases * field). */ levelPtr = &tsdPtr->levels[tsdPtr->curLevel]; nameId = Tk_GetUid(masqName+1); for (count = 0; count < NUM_STACKS; count++) { stackDepth[count] = levelPtr->bases[count]; } } else { /* * No option masquerading here. Just use the current level to get the * stack depths. */ nameId = Tk_GetUid(name); for (count = 0; count < NUM_STACKS; count++) { stackDepth[count] = tsdPtr->stacks[count]->numUsed; } } /* * Probe the stacks for matches. */ for (elPtr = tsdPtr->stacks[EXACT_LEAF_NAME]->els, count = stackDepth[EXACT_LEAF_NAME]; count > 0; elPtr++, count--) { if ((elPtr->nameUid == nameId) && (elPtr->priority > bestPtr->priority)) { bestPtr = elPtr; } } for (elPtr = tsdPtr->stacks[WILDCARD_LEAF_NAME]->els, count = stackDepth[WILDCARD_LEAF_NAME]; count > 0; elPtr++, count--) { if ((elPtr->nameUid == nameId) && (elPtr->priority > bestPtr->priority)) { bestPtr = elPtr; } } if (className != NULL) { classId = Tk_GetUid(className); for (elPtr = tsdPtr->stacks[EXACT_LEAF_CLASS]->els, count = stackDepth[EXACT_LEAF_CLASS]; count > 0; elPtr++, count--) { if ((elPtr->nameUid == classId) && (elPtr->priority > bestPtr->priority)) { bestPtr = elPtr; } } for (elPtr = tsdPtr->stacks[WILDCARD_LEAF_CLASS]->els, count = stackDepth[WILDCARD_LEAF_CLASS]; count > 0; elPtr++, count--) { if ((elPtr->nameUid == classId) && (elPtr->priority > bestPtr->priority)) { bestPtr = elPtr; } } } /* * If this option was masquerading with a different window class, probe * the option database now. Note that this will be inefficient if the * option database is densely populated, or if the widget has many * masquerading options. */ if (masqName != NULL) { char *masqClass; Tk_Uid nodeId, winClassId, winNameId; unsigned int classNameLength; register Element *nodePtr, *leafPtr; static int searchOrder[] = { EXACT_NODE_NAME, WILDCARD_NODE_NAME, EXACT_NODE_CLASS, WILDCARD_NODE_CLASS, -1 }; int *currentPtr, currentStack, leafCount; /* * Extract the masquerade class name from the name field. */ classNameLength = (unsigned int)(masqName - name); masqClass = (char *) ckalloc(classNameLength + 1); strncpy(masqClass, name, classNameLength); masqClass[classNameLength] = '\0'; winClassId = Tk_GetUid(masqClass); ckfree(masqClass); winNameId = ((TkWindow *)tkwin)->nameUid; levelPtr = &tsdPtr->levels[tsdPtr->curLevel]; for (currentPtr = searchOrder; *currentPtr != -1; currentPtr++) { currentStack = *currentPtr; nodePtr = tsdPtr->stacks[currentStack]->els; count = levelPtr->bases[currentStack]; /* * For wildcard stacks, check all entries; for non-wildcard * stacks, only check things that matched in the parent. */ if (!(currentStack & WILDCARD)) { nodePtr += levelPtr[-1].bases[currentStack]; count -= levelPtr[-1].bases[currentStack]; } if (currentStack && CLASS) { nodeId = winClassId; } else { nodeId = winNameId; } for ( ; count > 0; nodePtr++, count--) { if (nodePtr->nameUid == nodeId) { leafPtr = nodePtr->child.arrayPtr->els; leafCount = nodePtr->child.arrayPtr->numUsed; for ( ; leafCount > 0; leafPtr++, leafCount--) { if (leafPtr->flags & CLASS && className != NULL) { if (leafPtr->nameUid == classId && leafPtr->priority > bestPtr->priority) { bestPtr = leafPtr; } } else { if (leafPtr->nameUid == nameId && leafPtr->priority > bestPtr->priority) { bestPtr = leafPtr; } } } } } } } return bestPtr->child.valueUid; }
static void SetupStacks( TkWindow *winPtr, /* Window for which information is to be * cached. */ int leaf) /* Non-zero means this is the leaf window * being probed. Zero means this is an * ancestor of the desired leaf. */ { int level, i; const int *iPtr; register StackLevel *levelPtr; register ElArray *arrayPtr; ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); /* * The following array defines the order in which the current stacks are * searched to find matching entries to add to the stacks. Given the * current priority-based scheme, the order below is no longer relevant; * all that matters is that an element is on the list *somewhere*. The * ordering is a relic of the old days when priorities were determined * differently. */ static const int searchOrder[] = {WILDCARD_NODE_CLASS, WILDCARD_NODE_NAME, EXACT_NODE_CLASS, EXACT_NODE_NAME, -1}; if (winPtr->mainPtr->optionRootPtr == NULL) { OptionInit(winPtr->mainPtr); } /* * Step 1: make sure that options are cached for this window's parent. */ if (winPtr->parentPtr != NULL) { level = winPtr->parentPtr->optionLevel; if ((level == -1) || (tsdPtr->cachedWindow == NULL)) { SetupStacks(winPtr->parentPtr, 0); level = winPtr->parentPtr->optionLevel; } level++; } else { level = 1; } /* * Step 2: pop extra unneeded information off the stacks and mark those * windows as no longer having cached information. */ if (tsdPtr->curLevel >= level) { while (tsdPtr->curLevel >= level) { tsdPtr->levels[tsdPtr->curLevel].winPtr->optionLevel = -1; tsdPtr->curLevel--; } levelPtr = &tsdPtr->levels[level]; for (i = 0; i < NUM_STACKS; i++) { arrayPtr = tsdPtr->stacks[i]; arrayPtr->numUsed = levelPtr->bases[i]; arrayPtr->nextToUse = &arrayPtr->els[arrayPtr->numUsed]; } } tsdPtr->curLevel = winPtr->optionLevel = level; /* * Step 3: if the root database information isn't loaded or isn't valid, * initialize level 0 of the stack from the database root (this only * happens if winPtr is a main window). */ if ((tsdPtr->curLevel == 1) && ((tsdPtr->cachedWindow == NULL) || (tsdPtr->cachedWindow->mainPtr != winPtr->mainPtr))) { for (i = 0; i < NUM_STACKS; i++) { arrayPtr = tsdPtr->stacks[i]; arrayPtr->numUsed = 0; arrayPtr->nextToUse = arrayPtr->els; } ExtendStacks(winPtr->mainPtr->optionRootPtr, 0); } /* * Step 4: create a new stack level; grow the level array if we've run out * of levels. Clear the stacks for EXACT_LEAF_NAME and EXACT_LEAF_CLASS * (anything that was there is of no use any more). */ if (tsdPtr->curLevel >= tsdPtr->numLevels) { StackLevel *newLevels = (StackLevel *) ckalloc((unsigned) (tsdPtr->numLevels*2*sizeof(StackLevel))); memcpy(newLevels, tsdPtr->levels, tsdPtr->numLevels * sizeof(StackLevel)); ckfree((char *) tsdPtr->levels); tsdPtr->numLevels *= 2; tsdPtr->levels = newLevels; } levelPtr = &tsdPtr->levels[tsdPtr->curLevel]; levelPtr->winPtr = winPtr; arrayPtr = tsdPtr->stacks[EXACT_LEAF_NAME]; arrayPtr->numUsed = 0; arrayPtr->nextToUse = arrayPtr->els; arrayPtr = tsdPtr->stacks[EXACT_LEAF_CLASS]; arrayPtr->numUsed = 0; arrayPtr->nextToUse = arrayPtr->els; for (i = 0; i < NUM_STACKS; i++) { levelPtr->bases[i] = tsdPtr->stacks[i]->numUsed; } /* * Step 5: scan the current stack level looking for matches to this * window's name or class; where found, add new information to the stacks. */ for (iPtr = searchOrder; *iPtr != -1; iPtr++) { register Element *elPtr; int count; Tk_Uid id; i = *iPtr; if (i & CLASS) { id = winPtr->classUid; } else { id = winPtr->nameUid; } elPtr = tsdPtr->stacks[i]->els; count = levelPtr->bases[i]; /* * For wildcard stacks, check all entries; for non-wildcard stacks, * only check things that matched in the parent. */ if (!(i & WILDCARD)) { elPtr += levelPtr[-1].bases[i]; count -= levelPtr[-1].bases[i]; } for ( ; count > 0; elPtr++, count--) { if (elPtr->nameUid != id) { continue; } ExtendStacks(elPtr->child.arrayPtr, leaf); } } tsdPtr->cachedWindow = winPtr; }