Ejemplo n.º 1
0
Archivo: tkOption.c Proyecto: aosm/tcl
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;
}
Ejemplo n.º 2
0
Archivo: tkOption.c Proyecto: das/tcltk
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;
}