📄 tkoption.c
字号:
/* * tkOption.c -- * * This module contains procedures to manage the option * database, which allows various strings to be associated * with windows either by name or by class or both. * * Copyright (c) 1990-1994 The Regents of the University of California. * Copyright (c) 1994-1996 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * SCCS: @(#) tkOption.c 1.57 96/10/17 15:16:45 */#include "tkPort.h"#include "tkInt.h"/* * The option database is stored as one tree for each main window. * Each name or class field in an option is associated with a node or * leaf of the tree. For example, the options "x.y.z" and "x.y*a" * each correspond to three nodes in the tree; they share the nodes * "x" and "x.y", but have different leaf nodes. One of the following * structures exists for each node or leaf in the option tree. It is * actually stored as part of the parent node, and describes a particular * child of the parent. */typedef struct Element { Tk_Uid nameUid; /* Name or class from one element of * an option spec. */ union { struct ElArray *arrayPtr; /* If this is an intermediate node, * a pointer to a structure describing * the remaining elements of all * options whose prefixes are the * same up through this element. */ Tk_Uid valueUid; /* For leaf nodes, this is the string * value of the option. */ } child; int priority; /* Used to select among matching * options. Includes both the * priority level and a serial #. * Greater value means higher * priority. Irrelevant except in * leaf nodes. */ int flags; /* OR-ed combination of bits. See * below for values. */} Element;/* * Flags in Element structures: * * CLASS - Non-zero means this element refers to a class, * Zero means this element refers to a name. * NODE - Zero means this is a leaf element (the child * field is a value, not a pointer to another node). * One means this is a node element. * WILDCARD - Non-zero means this there was a star in the * original specification just before this element. * Zero means there was a dot. */#define TYPE_MASK 0x7#define CLASS 0x1#define NODE 0x2#define WILDCARD 0x4#define EXACT_LEAF_NAME 0x0#define EXACT_LEAF_CLASS 0x1#define EXACT_NODE_NAME 0x2#define EXACT_NODE_CLASS 0x3#define WILDCARD_LEAF_NAME 0x4#define WILDCARD_LEAF_CLASS 0x5#define WILDCARD_NODE_NAME 0x6#define WILDCARD_NODE_CLASS 0x7/* * The following structure is used to manage a dynamic array of * Elements. These structures are used for two purposes: to store * the contents of a node in the option tree, and for the option * stacks described below. */typedef struct ElArray { int arraySize; /* Number of elements actually * allocated in the "els" array. */ int numUsed; /* Number of elements currently in * use out of els. */ Element *nextToUse; /* Pointer to &els[numUsed]. */ Element els[1]; /* Array of structures describing * children of this node. The * array will actually contain enough * elements for all of the children * (and even a few extras, perhaps). * This must be the last field in * the structure. */} ElArray;#define EL_ARRAY_SIZE(numEls) ((unsigned) (sizeof(ElArray) \ + ((numEls)-1)*sizeof(Element)))#define INITIAL_SIZE 5/* * In addition to the option tree, which is a relatively static structure, * there are eight additional structures called "stacks", which are used * to speed up queries into the option database. The stack structures * are designed for the situation where an individual widget makes repeated * requests for its particular options. The requests differ only in * their last name/class, so during the first request we extract all * the options pertaining to the particular widget and save them in a * stack-like cache; subsequent requests for the same widget can search * the cache relatively quickly. In fact, the cache is a hierarchical * one, storing a list of relevant options for this widget and all of * its ancestors up to the application root; hence the name "stack". * * Each of the eight stacks consists of an array of Elements, ordered in * terms of levels in the window hierarchy. All the elements relevant * for the top-level widget appear first in the array, followed by all * those from the next-level widget on the path to the current widget, * etc. down to those for the current widget. * * Cached information is divided into eight stacks according to the * CLASS, NODE, and WILDCARD flags. Leaf and non-leaf information is * kept separate to speed up individual probes (non-leaf information is * only relevant when building the stacks, but isn't relevant when * making probes; similarly, only non-leaf information is relevant * when the stacks are being extended to the next widget down in the * widget hierarchy). Wildcard elements are handled separately from * "exact" elements because once they appear at a particular level in * the stack they remain active for all deeper levels; exact elements * are only relevant at a particular level. For example, when searching * for options relevant in a particular window, the entire wildcard * stacks get checked, but only the portions of the exact stacks that * pertain to the window's parent. Lastly, name and class stacks are * kept separate because different search keys are used when searching * them; keeping them separate speeds up the searches. */#define NUM_STACKS 8static ElArray *stacks[NUM_STACKS];static TkWindow *cachedWindow = NULL; /* Lowest-level window currently * loaded in stacks at present. * NULL means stacks have never * been used, or have been * invalidated because of a change * to the database. *//* * One of the following structures is used to keep track of each * level in the stacks. */typedef struct StackLevel { TkWindow *winPtr; /* Window corresponding to this stack * level. */ int bases[NUM_STACKS]; /* For each stack, index of first * element on stack corresponding to * this level (used to restore "numUsed" * fields when popping out of a level. */} StackLevel;/* * Information about all of the stack levels that are currently * active. This array grows dynamically to become as large as needed. */static StackLevel *levels = NULL; /* Array describing current stack. */static int numLevels = 0; /* Total space allocated. */static int curLevel = -1; /* Highest level currently in use. Note: * curLevel is never 0! (I don't remember * why anymore...) *//* * The variable below is a serial number for all options entered into * the database so far. It increments on each addition to the option * database. It is used in computing option priorities, so that the * most recent entry wins when choosing between options at the same * priority level. */static int serial = 0;/* * Special "no match" Element to use as default for searches. */static Element defaultMatch;/* * Forward declarations for procedures defined in this file: */static int AddFromString _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin, char *string, int priority));static void ClearOptionTree _ANSI_ARGS_((ElArray *arrayPtr));static ElArray * ExtendArray _ANSI_ARGS_((ElArray *arrayPtr, Element *elPtr));static void ExtendStacks _ANSI_ARGS_((ElArray *arrayPtr, int leaf));static int GetDefaultOptions _ANSI_ARGS_((Tcl_Interp *interp, TkWindow *winPtr)); static ElArray * NewArray _ANSI_ARGS_((int numEls)); static void OptionInit _ANSI_ARGS_((TkMainInfo *mainPtr));static int ParsePriority _ANSI_ARGS_((Tcl_Interp *interp, char *string));static int ReadOptionFile _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin, char *fileName, int priority));static void SetupStacks _ANSI_ARGS_((TkWindow *winPtr, int leaf));/* *-------------------------------------------------------------- * * Tk_AddOption -- * * Add a new option to the option database. * * Results: * None. * * Side effects: * Information is added to the option database. * *-------------------------------------------------------------- */voidTk_AddOption(tkwin, name, value, priority) Tk_Window tkwin; /* Window token; option will be associated * with main window for this window. */ char *name; /* Multi-element name of option. */ char *value; /* String value for option. */ int priority; /* Overall priority level to use for * this option, such as TK_USER_DEFAULT_PRIO * or TK_INTERACTIVE_PRIO. Must be between * 0 and TK_MAX_PRIO. */{ TkWindow *winPtr = ((TkWindow *) tkwin)->mainPtr->winPtr; register ElArray **arrayPtrPtr; register Element *elPtr; Element newEl; register char *p; char *field; int count, firstField, length;#define TMP_SIZE 100 char tmp[TMP_SIZE+1]; if (winPtr->mainPtr->optionRootPtr == NULL) { OptionInit(winPtr->mainPtr); } cachedWindow = NULL; /* Invalidate the cache. */ /* * Compute the priority for the new element, including both the * overall level and the serial number (to disambiguate with the * level). */ if (priority < 0) { priority = 0; } else if (priority > TK_MAX_PRIO) { priority = TK_MAX_PRIO; } newEl.priority = (priority << 24) + serial; serial++; /* * Parse the option one field at a time. */ arrayPtrPtr = &(((TkWindow *) tkwin)->mainPtr->optionRootPtr); p = name; for (firstField = 1; ; firstField = 0) { /* * Scan the next field from the name and convert it to a Tk_Uid. * Must copy the field before calling Tk_Uid, so that a terminating * NULL may be added without modifying the source string. */ if (*p == '*') { newEl.flags = WILDCARD; p++; } else { newEl.flags = 0; } field = p; while ((*p != 0) && (*p != '.') && (*p != '*')) { p++; } length = p - field; if (length > TMP_SIZE) { length = TMP_SIZE; } strncpy(tmp, field, (size_t) length); tmp[length] = 0; newEl.nameUid = Tk_GetUid(tmp); if (isupper(UCHAR(*field))) { newEl.flags |= CLASS; } if (*p != 0) { /* * New element will be a node. If this option can't possibly * apply to this main window, then just skip it. Otherwise, * add it to the parent, if it isn't already there, and descend * into it. */ newEl.flags |= NODE; if (firstField && !(newEl.flags & WILDCARD) && (newEl.nameUid != winPtr->nameUid) && (newEl.nameUid != winPtr->classUid)) { return; } for (elPtr = (*arrayPtrPtr)->els, count = (*arrayPtrPtr)->numUsed; ; elPtr++, count--) { if (count == 0) { newEl.child.arrayPtr = NewArray(5); *arrayPtrPtr = ExtendArray(*arrayPtrPtr, &newEl); arrayPtrPtr = &((*arrayPtrPtr)->nextToUse[-1].child.arrayPtr); break; } if ((elPtr->nameUid == newEl.nameUid) && (elPtr->flags == newEl.flags)) { arrayPtrPtr = &(elPtr->child.arrayPtr); break; } } if (*p == '.') { p++; } } else { /* * New element is a leaf. Add it to the parent, if it isn't * already there. If it exists already, keep whichever value * has highest priority. */ newEl.child.valueUid = Tk_GetUid(value); for (elPtr = (*arrayPtrPtr)->els, count = (*arrayPtrPtr)->numUsed; ; elPtr++, count--) { if (count == 0) { *arrayPtrPtr = ExtendArray(*arrayPtrPtr, &newEl); return; } if ((elPtr->nameUid == newEl.nameUid) && (elPtr->flags == newEl.flags)) { if (elPtr->priority < newEl.priority) { elPtr->priority = newEl.priority; elPtr->child.valueUid = newEl.child.valueUid; } return; } } } }}/* *-------------------------------------------------------------- * * Tk_GetOption -- * * Retrieve an option from the option database. * * Results: * The return value is the value specified in the option * database for the given name and class on the given * window. If there is nothing specified in the database * for that option, then NULL is returned. * * Side effects: * The internal caches used to speed up option mapping * may be modified, if this tkwin is different from the * last tkwin used for option retrieval. * *-------------------------------------------------------------- */Tk_UidTk_GetOption(tkwin, name, className) Tk_Window tkwin; /* Token for window that option is * associated with. */ char *name; /* Name of option. */ char *className; /* Class of option. NULL means there * is no class for this option: just * check for name. */{ Tk_Uid nameId, classId; register Element *elPtr, *bestPtr; register int count; /* * Note: no need to call OptionInit here: it will be done by * the SetupStacks call below (squeeze out those nanoseconds). */ if (tkwin != (Tk_Window) cachedWindow) { SetupStacks((TkWindow *) tkwin, 1); } nameId = Tk_GetUid(name); bestPtr = &defaultMatch; for (elPtr = stacks[EXACT_LEAF_NAME]->els, count = stacks[EXACT_LEAF_NAME]->numUsed; count > 0; elPtr++, count--) { if ((elPtr->nameUid == nameId) && (elPtr->priority > bestPtr->priority)) { bestPtr = elPtr; } } for (elPtr = stacks[WILDCARD_LEAF_NAME]->els, count = stacks[WILDCARD_LEAF_NAME]->numUsed; count > 0; elPtr++, count--) { if ((elPtr->nameUid == nameId) && (elPtr->priority > bestPtr->priority)) { bestPtr = elPtr; } } if (className != NULL) { classId = Tk_GetUid(className); for (elPtr = stacks[EXACT_LEAF_CLASS]->els, count = stacks[EXACT_LEAF_CLASS]->numUsed; count > 0; elPtr++, count--) { if ((elPtr->nameUid == classId) && (elPtr->priority > bestPtr->priority)) { bestPtr = elPtr; } } for (elPtr = stacks[WILDCARD_LEAF_CLASS]->els, count = stacks[WILDCARD_LEAF_CLASS]->numUsed; count > 0; elPtr++, count--) { if ((elPtr->nameUid == classId) && (elPtr->priority > bestPtr->priority)) { bestPtr = elPtr; } } } return bestPtr->child.valueUid;}/* *-------------------------------------------------------------- * * Tk_OptionCmd -- * * This procedure is invoked to process the "option" Tcl command. * See the user documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *-------------------------------------------------------------- */int
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -