26
26
* structures exists for each node or leaf in the option tree. It is
27
27
* actually stored as part of the parent node, and describes a particular
28
28
* child of the parent.
30
* The structure of the option db tree is a little confusing. There are
31
* four different kinds of nodes in the tree:
32
* interior class nodes
37
* All interior nodes refer to _window_ classes and names; all leaf nodes
38
* refer to _option_ classes and names. When looking for a particular option,
39
* therefore, you must compare interior node values to corresponding window
40
* values, and compare leaf node values to corresponding option values.
42
* The tree is actually stored in a collection of arrays; there is one each
43
* combination of WILDCARD/EXACT and CLASS/NAME and NODE/LEAF. The NODE arrays
44
* contain the interior nodes of the tree; each element has a pointer to an
45
* array of elements which are the leaves of the tree. The LEAF arrays, rather
46
* than holding the leaves of the tree, hold a cached subset of the option
47
* database, consisting of the values of all defined options for a single
48
* window, and some additional information about each ancestor of the window
49
* (since some options may be inherited from a parent), all the way back to the
52
* Each time a call is made to Tk_GetOption, Tk will attempt to use the cached
53
* information to satisfy the lookup. If the call is for a window other than
54
* that for which options are currently cached, the portion of the cache that
55
* contains information for common ancestors of the two windows is retained and
56
* the remainder is discarded and rebuilt with new information for the new
31
60
typedef struct Element {
163
185
* fields when popping out of a level. */
167
* Information about all of the stack levels that are currently
168
* active. This array grows dynamically to become as large as needed.
188
typedef struct ThreadSpecificData {
189
int initialized; /* 0 means the ThreadSpecific Data structure
190
* for the current thread needs to be
192
ElArray *stacks[NUM_STACKS];
193
TkWindow *cachedWindow;
194
/* Lowest-level window currently
195
* loaded in stacks at present.
196
* NULL means stacks have never
197
* been used, or have been
198
* invalidated because of a change
199
* to the database. */
201
* Information about all of the stack levels that are currently
202
* active. This array grows dynamically to become as large as needed.
171
static StackLevel *levels = NULL;
172
/* Array describing current stack. */
173
static int numLevels = 0; /* Total space allocated. */
174
static int curLevel = -1; /* Highest level currently in use. Note:
205
StackLevel *levels; /* Array describing current stack. */
206
int numLevels; /* Total space allocated. */
207
int curLevel; /* Highest level currently in use. Note:
175
208
* curLevel is never 0! (I don't remember
176
209
* why anymore...) */
179
* The variable below is a serial number for all options entered into
180
* the database so far. It increments on each addition to the option
181
* database. It is used in computing option priorities, so that the
182
* most recent entry wins when choosing between options at the same
186
static int serial = 0;
189
* Special "no match" Element to use as default for searches.
192
static Element defaultMatch;
211
* The variable below is a serial number for all options entered into
212
* the database so far. It increments on each addition to the option
213
* database. It is used in computing option priorities, so that the
214
* most recent entry wins when choosing between options at the same
219
Element defaultMatch; /* Special "no match" Element to use as
220
* default for searches.*/
221
} ThreadSpecificData;
222
static Tcl_ThreadDataKey dataKey;
195
225
* Forward declarations for procedures defined in this file:
388
422
Tk_GetOption(tkwin, name, className)
389
423
Tk_Window tkwin; /* Token for window that option is
390
424
* associated with. */
391
char *name; /* Name of option. */
392
char *className; /* Class of option. NULL means there
425
CONST char *name; /* Name of option. */
426
CONST char *className; /* Class of option. NULL means there
393
427
* is no class for this option: just
394
428
* check for name. */
396
Tk_Uid nameId, classId;
430
Tk_Uid nameId, classId = NULL;
397
432
register Element *elPtr, *bestPtr;
398
433
register int count;
434
StackLevel *levelPtr;
435
int stackDepth[NUM_STACKS];
436
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
437
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
401
440
* Note: no need to call OptionInit here: it will be done by
402
441
* the SetupStacks call below (squeeze out those nanoseconds).
405
if (tkwin != (Tk_Window) cachedWindow) {
444
if (tkwin != (Tk_Window) tsdPtr->cachedWindow) {
406
445
SetupStacks((TkWindow *) tkwin, 1);
409
nameId = Tk_GetUid(name);
410
bestPtr = &defaultMatch;
411
for (elPtr = stacks[EXACT_LEAF_NAME]->els,
412
count = stacks[EXACT_LEAF_NAME]->numUsed; count > 0;
414
if ((elPtr->nameUid == nameId)
415
&& (elPtr->priority > bestPtr->priority)) {
419
for (elPtr = stacks[WILDCARD_LEAF_NAME]->els,
420
count = stacks[WILDCARD_LEAF_NAME]->numUsed; count > 0;
422
if ((elPtr->nameUid == nameId)
423
&& (elPtr->priority > bestPtr->priority)) {
449
* Get a default "best" match.
452
bestPtr = &tsdPtr->defaultMatch;
455
* For megawidget support, we want to have some widget options masquerade
456
* as options for other widgets. For example, a combobox has a button in
457
* it; this button ought to pick up the *Button.background, etc., options.
458
* But because the class of the widget is Combobox, our normal search
459
* won't get that option.
461
* To work around this, the option name field syntax was extended to allow
462
* for a "." in the name; if this character occurs in the name, then it
463
* indicates that this name contains a new window class and an option name,
464
* ie, "Button.foreground". If we see this form in the name field, we
465
* query the option database directly (since the option stacks will not
466
* have the information we need).
469
masqName = strchr(name, (int)'.');
470
if (masqName != NULL) {
472
* This option is masquerading with a different window class.
473
* Search the stack to the depth it was before the current window's
474
* information was pushed (the value for which is stored in the bases
477
levelPtr = &tsdPtr->levels[tsdPtr->curLevel];
478
nameId = Tk_GetUid(masqName+1);
479
for (count = 0; count < NUM_STACKS; count++) {
480
stackDepth[count] = levelPtr->bases[count];
484
* No option masquerading here. Just use the current level to get the
487
nameId = Tk_GetUid(name);
488
for (count = 0; count < NUM_STACKS; count++) {
489
stackDepth[count] = tsdPtr->stacks[count]->numUsed;
494
* Probe the stacks for matches.
497
for (elPtr = tsdPtr->stacks[EXACT_LEAF_NAME]->els,
498
count = stackDepth[EXACT_LEAF_NAME]; count > 0;
500
if ((elPtr->nameUid == nameId)
501
&& (elPtr->priority > bestPtr->priority)) {
505
for (elPtr = tsdPtr->stacks[WILDCARD_LEAF_NAME]->els,
506
count = stackDepth[WILDCARD_LEAF_NAME]; count > 0;
508
if ((elPtr->nameUid == nameId)
509
&& (elPtr->priority > bestPtr->priority)) {
427
514
if (className != NULL) {
428
515
classId = Tk_GetUid(className);
429
for (elPtr = stacks[EXACT_LEAF_CLASS]->els,
430
count = stacks[EXACT_LEAF_CLASS]->numUsed; count > 0;
432
if ((elPtr->nameUid == classId)
433
&& (elPtr->priority > bestPtr->priority)) {
437
for (elPtr = stacks[WILDCARD_LEAF_CLASS]->els,
438
count = stacks[WILDCARD_LEAF_CLASS]->numUsed; count > 0;
440
if ((elPtr->nameUid == classId)
441
&& (elPtr->priority > bestPtr->priority)) {
516
for (elPtr = tsdPtr->stacks[EXACT_LEAF_CLASS]->els,
517
count = stackDepth[EXACT_LEAF_CLASS]; count > 0;
519
if ((elPtr->nameUid == classId)
520
&& (elPtr->priority > bestPtr->priority)) {
524
for (elPtr = tsdPtr->stacks[WILDCARD_LEAF_CLASS]->els,
525
count = stackDepth[WILDCARD_LEAF_CLASS]; count > 0;
527
if ((elPtr->nameUid == classId)
528
&& (elPtr->priority > bestPtr->priority)) {
535
* If this option was masquerading with a different window class,
536
* probe the option database now. Note that this will be inefficient
537
* if the option database is densely populated, or if the widget has many
538
* masquerading options.
541
if (masqName != NULL) {
543
Tk_Uid nodeId, winClassId, winNameId;
544
unsigned int classNameLength;
545
register Element *nodePtr, *leafPtr;
546
static int searchOrder[] = { EXACT_NODE_NAME,
551
int *currentPtr, currentStack, leafCount;
554
* Extract the masquerade class name from the name field.
557
classNameLength = (unsigned int)(masqName - name);
558
masqClass = (char *)ckalloc(classNameLength + 1);
559
strncpy(masqClass, name, classNameLength);
560
masqClass[classNameLength] = '\0';
562
winClassId = Tk_GetUid(masqClass);
564
winNameId = ((TkWindow *)tkwin)->nameUid;
566
levelPtr = &tsdPtr->levels[tsdPtr->curLevel];
568
for (currentPtr = searchOrder; *currentPtr != -1; currentPtr++) {
569
currentStack = *currentPtr;
570
nodePtr = tsdPtr->stacks[currentStack]->els;
571
count = levelPtr->bases[currentStack];
574
* For wildcard stacks, check all entries; for non-wildcard
575
* stacks, only check things that matched in the parent.
578
if (!(currentStack & WILDCARD)) {
579
nodePtr += levelPtr[-1].bases[currentStack];
580
count -= levelPtr[-1].bases[currentStack];
583
if (currentStack && CLASS) {
589
for ( ; count > 0; nodePtr++, count--) {
590
if (nodePtr->nameUid == nodeId) {
591
leafPtr = nodePtr->child.arrayPtr->els;
592
leafCount = nodePtr->child.arrayPtr->numUsed;
593
for ( ; leafCount > 0; leafPtr++, leafCount--) {
594
if (leafPtr->flags & CLASS && className != NULL) {
595
if (leafPtr->nameUid == classId &&
596
leafPtr->priority > bestPtr->priority) {
600
if (leafPtr->nameUid == nameId &&
601
leafPtr->priority > bestPtr->priority) {
446
611
return bestPtr->child.valueUid;
450
615
*--------------------------------------------------------------
454
619
* This procedure is invoked to process the "option" Tcl command.
455
620
* See the user documentation for details on what it does.
467
Tk_OptionCmd(clientData, interp, argc, argv)
632
Tk_OptionObjCmd(clientData, interp, objc, objv)
468
633
ClientData clientData; /* Main window associated with
469
634
* interpreter. */
470
635
Tcl_Interp *interp; /* Current interpreter. */
471
int argc; /* Number of arguments. */
472
char **argv; /* Argument strings. */
636
int objc; /* Number of Tcl_Obj arguments. */
637
Tcl_Obj *CONST objv[]; /* Tcl_Obj arguments. */
474
639
Tk_Window tkwin = (Tk_Window) clientData;
479
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
480
" cmd arg ?arg ...?\"", (char *) NULL);
484
length = strlen(argv[1]);
485
if ((c == 'a') && (strncmp(argv[1], "add", length) == 0)) {
488
if ((argc != 4) && (argc != 5)) {
489
Tcl_AppendResult(interp, "wrong # args: should be \"",
490
argv[0], " add pattern value ?priority?\"", (char *) NULL);
494
priority = TK_INTERACTIVE_PRIO;
496
priority = ParsePriority(interp, argv[4]);
501
Tk_AddOption(tkwin, argv[2], argv[3], priority);
503
} else if ((c == 'c') && (strncmp(argv[1], "clear", length) == 0)) {
507
Tcl_AppendResult(interp, "wrong # args: should be \"",
508
argv[0], " clear\"", (char *) NULL);
511
mainPtr = ((TkWindow *) tkwin)->mainPtr;
512
if (mainPtr->optionRootPtr != NULL) {
513
ClearOptionTree(mainPtr->optionRootPtr);
514
mainPtr->optionRootPtr = NULL;
518
} else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
523
Tcl_AppendResult(interp, "wrong # args: should be \"",
524
argv[0], " get window name class\"", (char *) NULL);
527
window = Tk_NameToWindow(interp, argv[2], tkwin);
528
if (window == NULL) {
531
value = Tk_GetOption(window, argv[3], argv[4]);
533
interp->result = value;
536
} else if ((c == 'r') && (strncmp(argv[1], "readfile", length) == 0)) {
539
if ((argc != 3) && (argc != 4)) {
540
Tcl_AppendResult(interp, "wrong # args: should be \"",
541
argv[0], " readfile fileName ?priority?\"",
546
priority = ParsePriority(interp, argv[3]);
551
priority = TK_INTERACTIVE_PRIO;
553
return ReadOptionFile(interp, tkwin, argv[2], priority);
555
Tcl_AppendResult(interp, "bad option \"", argv[1],
556
"\": must be add, clear, get, or readfile", (char *) NULL);
641
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
642
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
644
static CONST char *optionCmds[] = {
645
"add", "clear", "get", "readfile", NULL
649
OPTION_ADD, OPTION_CLEAR, OPTION_GET, OPTION_READFILE
653
Tcl_WrongNumArgs(interp, 1, objv, "cmd arg ?arg ...?");
657
result = Tcl_GetIndexFromObj(interp, objv[1], optionCmds, "option", 0,
659
if (result != TCL_OK) {
664
switch ((enum optionVals) index) {
667
if ((objc != 4) && (objc != 5)) {
668
Tcl_WrongNumArgs(interp, 2, objv, "pattern value ?priority?");
673
priority = TK_INTERACTIVE_PRIO;
675
priority = ParsePriority(interp, Tcl_GetString(objv[4]));
680
Tk_AddOption(tkwin, Tcl_GetString(objv[2]),
681
Tcl_GetString(objv[3]), priority);
689
Tcl_WrongNumArgs(interp, 2, objv, "");
692
mainPtr = ((TkWindow *) tkwin)->mainPtr;
693
if (mainPtr->optionRootPtr != NULL) {
694
ClearOptionTree(mainPtr->optionRootPtr);
695
mainPtr->optionRootPtr = NULL;
697
tsdPtr->cachedWindow = NULL;
706
Tcl_WrongNumArgs(interp, 2, objv, "window name class");
709
window = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), tkwin);
710
if (window == NULL) {
713
value = Tk_GetOption(window, Tcl_GetString(objv[3]),
714
Tcl_GetString(objv[4]));
716
Tcl_SetResult(interp, (char *)value, TCL_STATIC);
721
case OPTION_READFILE: {
724
if ((objc != 3) && (objc != 4)) {
725
Tcl_WrongNumArgs(interp, 2, objv, "fileName ?priority?");
730
priority = ParsePriority(interp, Tcl_GetString(objv[3]));
735
priority = TK_INTERACTIVE_PRIO;
737
result = ReadOptionFile(interp, tkwin, Tcl_GetString(objv[2]),
562
746
*--------------------------------------------------------------
1141
if (curLevel >= numLevels) {
1340
if (tsdPtr->curLevel >= tsdPtr->numLevels) {
1142
1341
StackLevel *newLevels;
1144
1343
newLevels = (StackLevel *) ckalloc((unsigned)
1145
(numLevels*2*sizeof(StackLevel)));
1146
memcpy((VOID *) newLevels, (VOID *) levels,
1147
(numLevels*sizeof(StackLevel)));
1148
ckfree((char *) levels);
1344
(tsdPtr->numLevels*2*sizeof(StackLevel)));
1345
memcpy((VOID *) newLevels, (VOID *) tsdPtr->levels,
1346
(tsdPtr->numLevels*sizeof(StackLevel)));
1347
ckfree((char *) tsdPtr->levels);
1348
tsdPtr->numLevels *= 2;
1349
tsdPtr->levels = newLevels;
1152
levelPtr = &levels[curLevel];
1351
levelPtr = &tsdPtr->levels[tsdPtr->curLevel];
1153
1352
levelPtr->winPtr = winPtr;
1154
arrayPtr = stacks[EXACT_LEAF_NAME];
1155
arrayPtr->numUsed = 0;
1156
arrayPtr->nextToUse = arrayPtr->els;
1157
arrayPtr = stacks[EXACT_LEAF_CLASS];
1158
arrayPtr->numUsed = 0;
1159
arrayPtr->nextToUse = arrayPtr->els;
1160
levelPtr->bases[EXACT_LEAF_NAME] = stacks[EXACT_LEAF_NAME]->numUsed;
1161
levelPtr->bases[EXACT_LEAF_CLASS] = stacks[EXACT_LEAF_CLASS]->numUsed;
1162
levelPtr->bases[EXACT_NODE_NAME] = stacks[EXACT_NODE_NAME]->numUsed;
1163
levelPtr->bases[EXACT_NODE_CLASS] = stacks[EXACT_NODE_CLASS]->numUsed;
1164
levelPtr->bases[WILDCARD_LEAF_NAME] = stacks[WILDCARD_LEAF_NAME]->numUsed;
1165
levelPtr->bases[WILDCARD_LEAF_CLASS] = stacks[WILDCARD_LEAF_CLASS]->numUsed;
1166
levelPtr->bases[WILDCARD_NODE_NAME] = stacks[WILDCARD_NODE_NAME]->numUsed;
1167
levelPtr->bases[WILDCARD_NODE_CLASS] = stacks[WILDCARD_NODE_CLASS]->numUsed;
1353
arrayPtr = tsdPtr->stacks[EXACT_LEAF_NAME];
1354
arrayPtr->numUsed = 0;
1355
arrayPtr->nextToUse = arrayPtr->els;
1356
arrayPtr = tsdPtr->stacks[EXACT_LEAF_CLASS];
1357
arrayPtr->numUsed = 0;
1358
arrayPtr->nextToUse = arrayPtr->els;
1359
for (i = 0; i < NUM_STACKS; i++) {
1360
levelPtr->bases[i] = tsdPtr->stacks[i]->numUsed;
1171
1363
* Step 5: scan the current stack level looking for matches to this
1172
1364
* window's name or class; where found, add new information to the
1233
1425
register int count;
1234
1426
register Element *elPtr;
1427
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1428
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1236
1430
for (elPtr = arrayPtr->els, count = arrayPtr->numUsed;
1237
1431
count > 0; elPtr++, count--) {
1238
1432
if (!(elPtr->flags & (NODE|WILDCARD)) && !leaf) {
1241
stacks[elPtr->flags] = ExtendArray(stacks[elPtr->flags], elPtr);
1435
tsdPtr->stacks[elPtr->flags] = ExtendArray(
1436
tsdPtr->stacks[elPtr->flags], elPtr);
1441
*--------------------------------------------------------------
1443
* OptionThreadExitProc --
1445
* Free data structures for option handling.
1451
* Option-related data structures get freed.
1453
*--------------------------------------------------------------
1457
OptionThreadExitProc(clientData)
1458
ClientData clientData; /* not used */
1460
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1461
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1463
if (tsdPtr->initialized) {
1465
for (i = 0; i < NUM_STACKS; i++) {
1466
ckfree((char *) tsdPtr->stacks[i]);
1468
ckfree((char *) tsdPtr->levels);
1469
tsdPtr->initialized = 0;
1246
1474
*--------------------------------------------------------------