]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * tkOption.c -- | |
3 | * | |
4 | * This module contains procedures to manage the option | |
5 | * database, which allows various strings to be associated | |
6 | * with windows either by name or by class or both. | |
7 | * | |
8 | * Copyright 1990 Regents of the University of California. | |
9 | * Permission to use, copy, modify, and distribute this | |
10 | * software and its documentation for any purpose and without | |
11 | * fee is hereby granted, provided that the above copyright | |
12 | * notice appear in all copies. The University of California | |
13 | * makes no representations about the suitability of this | |
14 | * software for any purpose. It is provided "as is" without | |
15 | * express or implied warranty. | |
16 | */ | |
17 | ||
18 | #ifndef lint | |
19 | static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkOption.c,v 1.25 92/03/16 08:46:14 ouster Exp $ SPRITE (Berkeley)"; | |
20 | #endif | |
21 | ||
22 | #include "tkconfig.h" | |
23 | #include "tkint.h" | |
24 | ||
25 | /* | |
26 | * The option database is stored as one tree for each main window. | |
27 | * Each name or class field in an option is associated with a node or | |
28 | * leaf of the tree. For example, the options "x.y.z" and "x.y*a" | |
29 | * each correspond to three nodes in the tree; they share the nodes | |
30 | * "x" and "x.y", but have different leaf nodes. One of the following | |
31 | * structures exists for each node or leaf in the option tree. It is | |
32 | * actually stored as part of the parent node, and describes a particular | |
33 | * child of the parent. | |
34 | */ | |
35 | ||
36 | typedef struct Element { | |
37 | Tk_Uid nameUid; /* Name or class from one element of | |
38 | * an option spec. */ | |
39 | union { | |
40 | struct ElArray *arrayPtr; /* If this is an intermediate node, | |
41 | * a pointer to a structure describing | |
42 | * the remaining elements of all | |
43 | * options whose prefixes are the | |
44 | * same up through this element. */ | |
45 | Tk_Uid valueUid; /* For leaf nodes, this is the string | |
46 | * value of the option. */ | |
47 | } child; | |
48 | int priority; /* Used to select among matching | |
49 | * options. Includes both the | |
50 | * priority level and a serial #. | |
51 | * Greater value means higher | |
52 | * priority. Irrelevant except in | |
53 | * leaf nodes. */ | |
54 | int flags; /* OR-ed combination of bits. See | |
55 | * below for values. */ | |
56 | } Element; | |
57 | ||
58 | /* | |
59 | * Flags in NodeElement structures: | |
60 | * | |
61 | * CLASS - Non-zero means this element refers to a class, | |
62 | * Zero means this element refers to a name. | |
63 | * NODE - Zero means this is a leaf element (the child | |
64 | * field is a value, not a pointer to another node). | |
65 | * One means this is a node element. | |
66 | * WILDCARD - Non-zero means this there was a star in the | |
67 | * original specification just before this element. | |
68 | * Zero means there was a dot. | |
69 | */ | |
70 | ||
71 | #define TYPE_MASK 0x7 | |
72 | ||
73 | #define CLASS 0x1 | |
74 | #define NODE 0x2 | |
75 | #define WILDCARD 0x4 | |
76 | ||
77 | #define EXACT_LEAF_NAME 0x0 | |
78 | #define EXACT_LEAF_CLASS 0x1 | |
79 | #define EXACT_NODE_NAME 0x2 | |
80 | #define EXACT_NODE_CLASS 0x3 | |
81 | #define WILDCARD_LEAF_NAME 0x4 | |
82 | #define WILDCARD_LEAF_CLASS 0x5 | |
83 | #define WILDCARD_NODE_NAME 0x6 | |
84 | #define WILDCARD_NODE_CLASS 0x7 | |
85 | ||
86 | /* | |
87 | * The following structure is used to manage a dynamic array of | |
88 | * Elements. These structures are used for two purposes: to store | |
89 | * the contents of a node in the option tree, and for the option | |
90 | * stacks described below. | |
91 | */ | |
92 | ||
93 | typedef struct ElArray { | |
94 | int arraySize; /* Number of elements actually | |
95 | * allocated in the "els" array. */ | |
96 | int numUsed; /* Number of elements currently in | |
97 | * use out of els. */ | |
98 | Element *nextToUse; /* Pointer to &els[numUsed]. */ | |
99 | Element els[1]; /* Array of structures describing | |
100 | * children of this node. The | |
101 | * array will actually contain enough | |
102 | * elements for all of the children | |
103 | * (and even a few extras, perhaps). | |
104 | * This must be the last field in | |
105 | * the structure. */ | |
106 | } ElArray; | |
107 | ||
108 | #define EL_ARRAY_SIZE(numEls) ((unsigned) (sizeof(ElArray) \ | |
109 | + ((numEls)-1)*sizeof(Element))) | |
110 | #define INITIAL_SIZE 5 | |
111 | ||
112 | /* | |
113 | * In addition to the option tree, which is a relatively static structure, | |
114 | * there are eight additional structures called "stacks", which are used | |
115 | * to speed up queries into the option database. The stack structures | |
116 | * are designed for the situation where an individual widget makes repeated | |
117 | * requests for its particular options. The requests differ only in | |
118 | * their last name/class, so during the first request we extract all | |
119 | * the options pertaining to the particular widget and save them in a | |
120 | * stack-like cache; subsequent requests for the same widget can search | |
121 | * the cache relatively quickly. In fact, the cache is a hierarchical | |
122 | * one, storing a list of relevant options for this widget and all of | |
123 | * its ancestors up to the application root; hence the name "stack". | |
124 | * | |
125 | * Each of the eight stacks consists of an array of Elements, ordered in | |
126 | * terms of levels in the window hierarchy. All the elements relevant | |
127 | * for the top-level widget appear first in the array, followed by all | |
128 | * those from the next-level widget on the path to the current widget, | |
129 | * etc. down to those for the current widget. | |
130 | * | |
131 | * Cached information is divided into eight stacks according to the | |
132 | * CLASS, NODE, and WILDCARD flags. Leaf and non-leaf information is | |
133 | * kept separate to speed up individual probes (non-leaf information is | |
134 | * only relevant when building the stacks, but isn't relevant when | |
135 | * making probes; similarly, only non-leaf information is relevant | |
136 | * when the stacks are being extended to the next widget down in the | |
137 | * widget hierarchy). Wildcard elements are handled separately from | |
138 | * "exact" elements because once they appear at a particular level in | |
139 | * the stack they remain active for all deeper levels; exact elements | |
140 | * are only relevant at a particular level. For example, when searching | |
141 | * for options relevant in a particular window, the entire wildcard | |
142 | * stacks get checked, but only the portions of the exact stacks that | |
143 | * pertain to the window's parent. Lastly, name and class stacks are | |
144 | * kept separate because different search keys are used when searching | |
145 | * them; keeping them separate speeds up the searches. | |
146 | */ | |
147 | ||
148 | #define NUM_STACKS 8 | |
149 | static ElArray *stacks[NUM_STACKS]; | |
150 | static TkWindow *cachedWindow = NULL; /* Lowest-level window currently | |
151 | * loaded in stacks at present. | |
152 | * NULL means stacks have never | |
153 | * been used, or have been | |
154 | * invalidated because of a change | |
155 | * to the database. */ | |
156 | ||
157 | /* | |
158 | * One of the following structures is used to keep track of each | |
159 | * level in the stacks. | |
160 | */ | |
161 | ||
162 | typedef struct StackLevel { | |
163 | TkWindow *winPtr; /* Window corresponding to this stack | |
164 | * level. */ | |
165 | int bases[NUM_STACKS]; /* For each stack, index of first | |
166 | * element on stack corresponding to | |
167 | * this level (used to restore "numUsed" | |
168 | * fields when popping out of a level. */ | |
169 | } StackLevel; | |
170 | ||
171 | /* | |
172 | * Information about all of the stack levels that are currently | |
173 | * active. This array grows dynamically to become as large as needed. | |
174 | */ | |
175 | ||
176 | static StackLevel *levels = NULL; | |
177 | /* Array describing current stack. */ | |
178 | static int numLevels = 0; /* Total space allocated. */ | |
179 | static int curLevel = 0; /* Highest level currently in use. */ | |
180 | ||
181 | /* | |
182 | * The variable below is a serial number for all options entered into | |
183 | * the database so far. It increments on each addition to the option | |
184 | * database. It is used in computing option priorities, so that the | |
185 | * most recent entry wins when choosing between options at the same | |
186 | * priority level. | |
187 | */ | |
188 | ||
189 | static int serial = 0; | |
190 | ||
191 | /* | |
192 | * Special "no match" Element to use as default for searches. | |
193 | */ | |
194 | ||
195 | static Element defaultMatch; | |
196 | ||
197 | /* | |
198 | * Forward declarations for procedures defined in this file: | |
199 | */ | |
200 | ||
201 | static int AddFromString _ANSI_ARGS_((Tcl_Interp *interp, | |
202 | Tk_Window tkwin, char *string, int priority)); | |
203 | static void ClearOptionTree _ANSI_ARGS_((ElArray *arrayPtr)); | |
204 | static ElArray * ExtendArray _ANSI_ARGS_((ElArray *arrayPtr, | |
205 | Element *elPtr)); | |
206 | static void ExtendStacks _ANSI_ARGS_((ElArray *arrayPtr, | |
207 | int leaf)); | |
208 | static int GetDefaultOptions _ANSI_ARGS_((Tcl_Interp *interp, | |
209 | TkWindow *winPtr)); | |
210 | static ElArray * NewArray _ANSI_ARGS_((int numEls)); | |
211 | static void OptionInit _ANSI_ARGS_((TkMainInfo *mainPtr)); | |
212 | static int ParsePriority _ANSI_ARGS_((Tcl_Interp *interp, | |
213 | char *string)); | |
214 | static int ReadOptionFile _ANSI_ARGS_((Tcl_Interp *interp, | |
215 | Tk_Window tkwin, char *fileName, int priority)); | |
216 | static void SetupStacks _ANSI_ARGS_((TkWindow *winPtr, int leaf)); | |
217 | \f | |
218 | /* | |
219 | *-------------------------------------------------------------- | |
220 | * | |
221 | * Tk_AddOption -- | |
222 | * | |
223 | * Add a new option to the option database. | |
224 | * | |
225 | * Results: | |
226 | * None. | |
227 | * | |
228 | * Side effects: | |
229 | * Information is added to the option database. | |
230 | * | |
231 | *-------------------------------------------------------------- | |
232 | */ | |
233 | ||
234 | void | |
235 | Tk_AddOption(tkwin, name, value, priority) | |
236 | Tk_Window tkwin; /* Window token; option will be associated | |
237 | * with main window for this window. */ | |
238 | char *name; /* Multi-element name of option. */ | |
239 | char *value; /* String value for option. */ | |
240 | int priority; /* Overall priority level to use for | |
241 | * this option, such as TK_USER_DEFAULT_PRIO | |
242 | * or TK_INTERACTIVE_PRIO. Must be between | |
243 | * 0 and TK_MAX_PRIO. */ | |
244 | { | |
245 | TkWindow *winPtr = ((TkWindow *) tkwin)->mainPtr->winPtr; | |
246 | register ElArray **arrayPtrPtr; | |
247 | register Element *elPtr; | |
248 | Element newEl; | |
249 | register char *p; | |
250 | char *field; | |
251 | int count, firstField, length; | |
252 | #define TMP_SIZE 100 | |
253 | char tmp[TMP_SIZE+1]; | |
254 | ||
255 | if (winPtr->mainPtr->optionRootPtr == NULL) { | |
256 | OptionInit(winPtr->mainPtr); | |
257 | } | |
258 | cachedWindow = NULL; /* Invalidate the cache. */ | |
259 | ||
260 | /* | |
261 | * Compute the priority for the new element, including both the | |
262 | * overall level and the serial number (to disambiguate with the | |
263 | * level). | |
264 | */ | |
265 | ||
266 | if (priority < 0) { | |
267 | priority = 0; | |
268 | } else if (priority > TK_MAX_PRIO) { | |
269 | priority = TK_MAX_PRIO; | |
270 | } | |
271 | newEl.priority = (priority << 24) + serial; | |
272 | serial++; | |
273 | ||
274 | /* | |
275 | * Parse the option one field at a time. | |
276 | */ | |
277 | ||
278 | arrayPtrPtr = &(((TkWindow *) tkwin)->mainPtr->optionRootPtr); | |
279 | p = name; | |
280 | for (firstField = 1; ; firstField = 0) { | |
281 | ||
282 | /* | |
283 | * Scan the next field from the name and convert it to a Tk_Uid. | |
284 | * Must copy the field before calling Tk_Uid, so that a terminating | |
285 | * NULL may be added without modifying the source string. | |
286 | */ | |
287 | ||
288 | if (*p == '*') { | |
289 | newEl.flags = WILDCARD; | |
290 | p++; | |
291 | } else { | |
292 | newEl.flags = 0; | |
293 | } | |
294 | field = p; | |
295 | while ((*p != 0) && (*p != '.') && (*p != '*')) { | |
296 | p++; | |
297 | } | |
298 | length = p - field; | |
299 | if (length > TMP_SIZE) { | |
300 | length = TMP_SIZE; | |
301 | } | |
302 | strncpy(tmp, field, length); | |
303 | tmp[length] = 0; | |
304 | newEl.nameUid = Tk_GetUid(tmp); | |
305 | if (isupper(*field)) { | |
306 | newEl.flags |= CLASS; | |
307 | } | |
308 | ||
309 | if (*p != 0) { | |
310 | ||
311 | /* | |
312 | * New element will be a node. If this option can't possibly | |
313 | * apply to this main window, then just skip it. Otherwise, | |
314 | * add it to the parent, if it isn't already there, and descend | |
315 | * into it. | |
316 | */ | |
317 | ||
318 | newEl.flags |= NODE; | |
319 | if (firstField && !(newEl.flags & WILDCARD) | |
320 | && (newEl.nameUid != winPtr->nameUid) | |
321 | && (newEl.nameUid != winPtr->classUid)) { | |
322 | return; | |
323 | } | |
324 | for (elPtr = (*arrayPtrPtr)->els, count = (*arrayPtrPtr)->numUsed; | |
325 | ; elPtr++, count--) { | |
326 | if (count == 0) { | |
327 | newEl.child.arrayPtr = NewArray(5); | |
328 | *arrayPtrPtr = ExtendArray(*arrayPtrPtr, &newEl); | |
329 | arrayPtrPtr = &((*arrayPtrPtr)->nextToUse[-1].child.arrayPtr); | |
330 | break; | |
331 | } | |
332 | if ((elPtr->nameUid == newEl.nameUid) | |
333 | && (elPtr->flags == newEl.flags)) { | |
334 | arrayPtrPtr = &(elPtr->child.arrayPtr); | |
335 | break; | |
336 | } | |
337 | } | |
338 | if (*p == '.') { | |
339 | p++; | |
340 | } | |
341 | } else { | |
342 | ||
343 | /* | |
344 | * New element is a leaf. Add it to the parent, if it isn't | |
345 | * already there. If it exists already, keep whichever value | |
346 | * has highest priority. | |
347 | */ | |
348 | ||
349 | newEl.child.valueUid = Tk_GetUid(value); | |
350 | for (elPtr = (*arrayPtrPtr)->els, count = (*arrayPtrPtr)->numUsed; | |
351 | ; elPtr++, count--) { | |
352 | if (count == 0) { | |
353 | *arrayPtrPtr = ExtendArray(*arrayPtrPtr, &newEl); | |
354 | return; | |
355 | } | |
356 | if ((elPtr->nameUid == newEl.nameUid) | |
357 | && (elPtr->flags == newEl.flags)) { | |
358 | if (elPtr->priority < newEl.priority) { | |
359 | elPtr->priority = newEl.priority; | |
360 | elPtr->child.valueUid = newEl.child.valueUid; | |
361 | } | |
362 | return; | |
363 | } | |
364 | } | |
365 | } | |
366 | } | |
367 | } | |
368 | \f | |
369 | /* | |
370 | *-------------------------------------------------------------- | |
371 | * | |
372 | * Tk_GetOption -- | |
373 | * | |
374 | * Retrieve an option from the option database. | |
375 | * | |
376 | * Results: | |
377 | * The return value is the value specified in the option | |
378 | * database for the given name and class on the given | |
379 | * window. If there is nothing specified in the database | |
380 | * for that option, then NULL is returned. | |
381 | * | |
382 | * Side effects: | |
383 | * The internal caches used to speed up option mapping | |
384 | * may be modified, if this tkwin is different from the | |
385 | * last tkwin used for option retrieval. | |
386 | * | |
387 | *-------------------------------------------------------------- | |
388 | */ | |
389 | ||
390 | Tk_Uid | |
391 | Tk_GetOption(tkwin, name, className) | |
392 | Tk_Window tkwin; /* Token for window that option is | |
393 | * associated with. */ | |
394 | char *name; /* Name of option. */ | |
395 | char *className; /* Class of option. NULL means there | |
396 | * is no class for this option: just | |
397 | * check for name. */ | |
398 | { | |
399 | Tk_Uid nameId, classId; | |
400 | register Element *elPtr, *bestPtr; | |
401 | register int count; | |
402 | ||
403 | /* | |
404 | * Note: no need to call OptionInit here: it will be done by | |
405 | * the SetupStacks call below (squeeze out those nanoseconds). | |
406 | */ | |
407 | ||
408 | if (tkwin != (Tk_Window) cachedWindow) { | |
409 | SetupStacks((TkWindow *) tkwin, 1); | |
410 | } | |
411 | ||
412 | nameId = Tk_GetUid(name); | |
413 | bestPtr = &defaultMatch; | |
414 | for (elPtr = stacks[EXACT_LEAF_NAME]->els, | |
415 | count = stacks[EXACT_LEAF_NAME]->numUsed; count > 0; | |
416 | elPtr++, count--) { | |
417 | if ((elPtr->nameUid == nameId) | |
418 | && (elPtr->priority > bestPtr->priority)) { | |
419 | bestPtr = elPtr; | |
420 | } | |
421 | } | |
422 | for (elPtr = stacks[WILDCARD_LEAF_NAME]->els, | |
423 | count = stacks[WILDCARD_LEAF_NAME]->numUsed; count > 0; | |
424 | elPtr++, count--) { | |
425 | if ((elPtr->nameUid == nameId) | |
426 | && (elPtr->priority > bestPtr->priority)) { | |
427 | bestPtr = elPtr; | |
428 | } | |
429 | } | |
430 | if (className != NULL) { | |
431 | classId = Tk_GetUid(className); | |
432 | for (elPtr = stacks[EXACT_LEAF_CLASS]->els, | |
433 | count = stacks[EXACT_LEAF_CLASS]->numUsed; count > 0; | |
434 | elPtr++, count--) { | |
435 | if ((elPtr->nameUid == classId) | |
436 | && (elPtr->priority > bestPtr->priority)) { | |
437 | bestPtr = elPtr; | |
438 | } | |
439 | } | |
440 | for (elPtr = stacks[WILDCARD_LEAF_CLASS]->els, | |
441 | count = stacks[WILDCARD_LEAF_CLASS]->numUsed; count > 0; | |
442 | elPtr++, count--) { | |
443 | if ((elPtr->nameUid == classId) | |
444 | && (elPtr->priority > bestPtr->priority)) { | |
445 | bestPtr = elPtr; | |
446 | } | |
447 | } | |
448 | } | |
449 | return bestPtr->child.valueUid; | |
450 | } | |
451 | \f | |
452 | /* | |
453 | *-------------------------------------------------------------- | |
454 | * | |
455 | * Tk_OptionCmd -- | |
456 | * | |
457 | * This procedure is invoked to process the "option" Tcl command. | |
458 | * See the user documentation for details on what it does. | |
459 | * | |
460 | * Results: | |
461 | * A standard Tcl result. | |
462 | * | |
463 | * Side effects: | |
464 | * See the user documentation. | |
465 | * | |
466 | *-------------------------------------------------------------- | |
467 | */ | |
468 | ||
469 | int | |
470 | Tk_OptionCmd(clientData, interp, argc, argv) | |
471 | ClientData clientData; /* Main window associated with | |
472 | * interpreter. */ | |
473 | Tcl_Interp *interp; /* Current interpreter. */ | |
474 | int argc; /* Number of arguments. */ | |
475 | char **argv; /* Argument strings. */ | |
476 | { | |
477 | Tk_Window tkwin = (Tk_Window) clientData; | |
478 | int length; | |
479 | char c; | |
480 | ||
481 | if (argc < 2) { | |
482 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], | |
483 | " cmd arg ?arg ...?\"", (char *) NULL); | |
484 | return TCL_ERROR; | |
485 | } | |
486 | c = argv[1][0]; | |
487 | length = strlen(argv[1]); | |
488 | if ((c == 'a') && (strncmp(argv[1], "add", length) == 0)) { | |
489 | int priority; | |
490 | ||
491 | if ((argc != 4) && (argc != 5)) { | |
492 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
493 | argv[0], " add pattern value ?priority?\"", (char *) NULL); | |
494 | return TCL_ERROR; | |
495 | } | |
496 | if (argc == 4) { | |
497 | priority = TK_INTERACTIVE_PRIO; | |
498 | } else { | |
499 | priority = ParsePriority(interp, argv[4]); | |
500 | if (priority < 0) { | |
501 | return TCL_ERROR; | |
502 | } | |
503 | } | |
504 | Tk_AddOption(tkwin, argv[2], argv[3], priority); | |
505 | return TCL_OK; | |
506 | } else if ((c == 'c') && (strncmp(argv[1], "clear", length) == 0)) { | |
507 | TkMainInfo *mainPtr; | |
508 | ||
509 | if (argc != 2) { | |
510 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
511 | argv[0], " clear\"", (char *) NULL); | |
512 | return TCL_ERROR; | |
513 | } | |
514 | mainPtr = ((TkWindow *) tkwin)->mainPtr; | |
515 | if (mainPtr->optionRootPtr != NULL) { | |
516 | ClearOptionTree(mainPtr->optionRootPtr); | |
517 | mainPtr->optionRootPtr = NULL; | |
518 | } | |
519 | cachedWindow = NULL; | |
520 | return TCL_OK; | |
521 | } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) { | |
522 | Tk_Window window; | |
523 | Tk_Uid value; | |
524 | ||
525 | if (argc != 5) { | |
526 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
527 | argv[0], " get window name class\"", (char *) NULL); | |
528 | return TCL_ERROR; | |
529 | } | |
530 | window = Tk_NameToWindow(interp, argv[2], tkwin); | |
531 | if (window == NULL) { | |
532 | return TCL_ERROR; | |
533 | } | |
534 | value = Tk_GetOption(window, argv[3], argv[4]); | |
535 | if (value != NULL) { | |
536 | interp->result = value; | |
537 | } | |
538 | return TCL_OK; | |
539 | } else if ((c == 'r') && (strncmp(argv[1], "readfile", length) == 0)) { | |
540 | int priority; | |
541 | ||
542 | if ((argc != 3) && (argc != 4)) { | |
543 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
544 | argv[0], " readfile fileName ?priority?\"", | |
545 | (char *) NULL); | |
546 | return TCL_ERROR; | |
547 | } | |
548 | if (argc == 4) { | |
549 | priority = ParsePriority(interp, argv[3]); | |
550 | if (priority < 0) { | |
551 | return TCL_ERROR; | |
552 | } | |
553 | } else { | |
554 | priority = TK_INTERACTIVE_PRIO; | |
555 | } | |
556 | return ReadOptionFile(interp, tkwin, argv[2], priority); | |
557 | } else { | |
558 | Tcl_AppendResult(interp, "bad option \"", argv[1], | |
559 | "\": must be add, clear, get, or readfile", (char *) NULL); | |
560 | return TCL_ERROR; | |
561 | } | |
562 | } | |
563 | \f | |
564 | /* | |
565 | *-------------------------------------------------------------- | |
566 | * | |
567 | * TkOptionDeadWindow -- | |
568 | * | |
569 | * This procedure is called whenever a window is deleted. | |
570 | * It cleans up any option-related stuff associated with | |
571 | * the window. | |
572 | * | |
573 | * Results: | |
574 | * None. | |
575 | * | |
576 | * Side effects: | |
577 | * Option-related resources are freed. See code below | |
578 | * for details. | |
579 | * | |
580 | *-------------------------------------------------------------- | |
581 | */ | |
582 | ||
583 | void | |
584 | TkOptionDeadWindow(winPtr) | |
585 | register TkWindow *winPtr; /* Window to be cleaned up. */ | |
586 | { | |
587 | /* | |
588 | * If this window is in the option stacks, then clear the stacks. | |
589 | */ | |
590 | ||
591 | if (winPtr->optionLevel != -1) { | |
592 | int i; | |
593 | ||
594 | for (i = 1; i <= curLevel; i++) { | |
595 | levels[curLevel].winPtr->optionLevel = -1; | |
596 | } | |
597 | curLevel = 0; | |
598 | cachedWindow = NULL; | |
599 | } | |
600 | ||
601 | /* | |
602 | * If this window was a main window, then delete its option | |
603 | * database. | |
604 | */ | |
605 | ||
606 | if ((winPtr->mainPtr->winPtr == winPtr) | |
607 | && (winPtr->mainPtr->optionRootPtr != NULL)) { | |
608 | ClearOptionTree(winPtr->mainPtr->optionRootPtr); | |
609 | winPtr->mainPtr->optionRootPtr = NULL; | |
610 | } | |
611 | } | |
612 | \f | |
613 | /* | |
614 | *---------------------------------------------------------------------- | |
615 | * | |
616 | * ParsePriority -- | |
617 | * | |
618 | * Parse a string priority value. | |
619 | * | |
620 | * Results: | |
621 | * The return value is the integer priority level corresponding | |
622 | * to string, or -1 if string doesn't point to a valid priority level. | |
623 | * In this case, an error message is left in interp->result. | |
624 | * | |
625 | * Side effects: | |
626 | * None. | |
627 | * | |
628 | *---------------------------------------------------------------------- | |
629 | */ | |
630 | ||
631 | static int | |
632 | ParsePriority(interp, string) | |
633 | Tcl_Interp *interp; /* Interpreter to use for error reporting. */ | |
634 | char *string; /* Describes a priority level, either | |
635 | * symbolically or numerically. */ | |
636 | { | |
637 | char c; | |
638 | int length, priority; | |
639 | ||
640 | c = string[0]; | |
641 | length = strlen(string); | |
642 | if ((c == 'w') | |
643 | && (strncmp(string, "widgetDefault", length) == 0)) { | |
644 | return TK_WIDGET_DEFAULT_PRIO; | |
645 | } else if ((c == 's') | |
646 | && (strncmp(string, "startupFile", length) == 0)) { | |
647 | return TK_STARTUP_FILE_PRIO; | |
648 | } else if ((c == 'u') | |
649 | && (strncmp(string, "userDefault", length) == 0)) { | |
650 | return TK_USER_DEFAULT_PRIO; | |
651 | } else if ((c == 'i') | |
652 | && (strncmp(string, "interactive", length) == 0)) { | |
653 | return TK_INTERACTIVE_PRIO; | |
654 | } else { | |
655 | char *end; | |
656 | ||
657 | priority = strtoul(string, &end, 0); | |
658 | if ((end == string) || (*end != 0) || (priority < 0) | |
659 | || (priority > 100)) { | |
660 | Tcl_AppendResult(interp, "bad priority level \"", string, | |
661 | "\": must be widgetDefault, startupFile, userDefault, ", | |
662 | "interactive, or a number between 0 and 100", | |
663 | (char *) NULL); | |
664 | return -1; | |
665 | } | |
666 | } | |
667 | return priority; | |
668 | } | |
669 | \f | |
670 | /* | |
671 | *---------------------------------------------------------------------- | |
672 | * | |
673 | * AddFromString -- | |
674 | * | |
675 | * Given a string containing lines in the standard format for | |
676 | * X resources (see other documentation for details on what this | |
677 | * is), parse the resource specifications and enter them as options | |
678 | * for tkwin's main window. | |
679 | * | |
680 | * Results: | |
681 | * The return value is a standard Tcl return code. In the case of | |
682 | * an error in parsing string, TCL_ERROR will be returned and an | |
683 | * error message will be left in interp->result. The memory at | |
684 | * string is totally trashed by this procedure. If you care about | |
685 | * its contents, make a copy before calling here. | |
686 | * | |
687 | * Side effects: | |
688 | * None. | |
689 | * | |
690 | *---------------------------------------------------------------------- | |
691 | */ | |
692 | ||
693 | static int | |
694 | AddFromString(interp, tkwin, string, priority) | |
695 | Tcl_Interp *interp; /* Interpreter to use for reporting results. */ | |
696 | Tk_Window tkwin; /* Token for window: options are entered | |
697 | * for this window's main window. */ | |
698 | char *string; /* String containing option specifiers. */ | |
699 | int priority; /* Priority level to use for options in | |
700 | * this string, such as TK_USER_DEFAULT_PRIO | |
701 | * or TK_INTERACTIVE_PRIO. Must be between | |
702 | * 0 and TK_MAX_PRIO. */ | |
703 | { | |
704 | register char *src, *dst; | |
705 | char *name, *value; | |
706 | int lineNum; | |
707 | ||
708 | src = string; | |
709 | lineNum = 1; | |
710 | while (1) { | |
711 | ||
712 | /* | |
713 | * Skip leading white space and empty lines and comment lines, and | |
714 | * check for the end of the spec. | |
715 | */ | |
716 | ||
717 | while ((*src == ' ') || (*src == '\t')) { | |
718 | src++; | |
719 | } | |
720 | if ((*src == '#') || (*src == '!')) { | |
721 | do { | |
722 | src++; | |
723 | if ((src[0] == '\\') && (src[1] == '\n')) { | |
724 | src += 2; | |
725 | lineNum++; | |
726 | } | |
727 | } while ((*src != '\n') && (*src != 0)); | |
728 | } | |
729 | if (*src == '\n') { | |
730 | src++; | |
731 | lineNum++; | |
732 | continue; | |
733 | } | |
734 | if (*src == '\0') { | |
735 | break; | |
736 | } | |
737 | ||
738 | /* | |
739 | * Parse off the option name, collapsing out backslash-newline | |
740 | * sequences of course. | |
741 | */ | |
742 | ||
743 | dst = name = src; | |
744 | while (*src != ':') { | |
745 | if ((*src == '\0') || (*src == '\n')) { | |
746 | sprintf(interp->result, "missing colon on line %d", | |
747 | lineNum); | |
748 | return TCL_ERROR; | |
749 | } | |
750 | if ((src[0] == '\\') && (src[1] == '\n')) { | |
751 | src += 2; | |
752 | lineNum++; | |
753 | } else { | |
754 | *dst = *src; | |
755 | dst++; | |
756 | src++; | |
757 | } | |
758 | } | |
759 | ||
760 | /* | |
761 | * Eliminate trailing white space on the name, and null-terminate | |
762 | * it. | |
763 | */ | |
764 | ||
765 | while ((dst != name) && ((dst[-1] == ' ') || (dst[-1] == '\t'))) { | |
766 | dst--; | |
767 | } | |
768 | *dst = '\0'; | |
769 | ||
770 | /* | |
771 | * Skip white space between the name and the value. | |
772 | */ | |
773 | ||
774 | src++; | |
775 | while ((*src == ' ') || (*src == '\t')) { | |
776 | src++; | |
777 | } | |
778 | if (*src == '\0') { | |
779 | sprintf(interp->result, "missing value on line %d", lineNum); | |
780 | return TCL_ERROR; | |
781 | } | |
782 | ||
783 | /* | |
784 | * Parse off the value, squeezing out backslash-newline sequences | |
785 | * along the way. | |
786 | */ | |
787 | ||
788 | dst = value = src; | |
789 | while (*src != '\n') { | |
790 | if (*src == '\0') { | |
791 | sprintf(interp->result, "missing newline on line %d", | |
792 | lineNum); | |
793 | return TCL_ERROR; | |
794 | } | |
795 | if ((src[0] == '\\') && (src[1] == '\n')) { | |
796 | src += 2; | |
797 | lineNum++; | |
798 | } else { | |
799 | *dst = *src; | |
800 | dst++; | |
801 | src++; | |
802 | } | |
803 | } | |
804 | *dst = 0; | |
805 | ||
806 | /* | |
807 | * Enter the option into the database. | |
808 | */ | |
809 | ||
810 | Tk_AddOption(tkwin, name, value, priority); | |
811 | src++; | |
812 | lineNum++; | |
813 | } | |
814 | return TCL_OK; | |
815 | } | |
816 | \f | |
817 | /* | |
818 | *---------------------------------------------------------------------- | |
819 | * | |
820 | * ReadOptionFile -- | |
821 | * | |
822 | * Read a file of options ("resources" in the old X terminology) | |
823 | * and load them into the option database. | |
824 | * | |
825 | * Results: | |
826 | * The return value is a standard Tcl return code. In the case of | |
827 | * an error in parsing string, TCL_ERROR will be returned and an | |
828 | * error message will be left in interp->result. | |
829 | * | |
830 | * Side effects: | |
831 | * None. | |
832 | * | |
833 | *---------------------------------------------------------------------- | |
834 | */ | |
835 | ||
836 | static int | |
837 | ReadOptionFile(interp, tkwin, fileName, priority) | |
838 | Tcl_Interp *interp; /* Interpreter to use for reporting results. */ | |
839 | Tk_Window tkwin; /* Token for window: options are entered | |
840 | * for this window's main window. */ | |
841 | char *fileName; /* Name of file containing options. */ | |
842 | int priority; /* Priority level to use for options in | |
843 | * this file, such as TK_USER_DEFAULT_PRIO | |
844 | * or TK_INTERACTIVE_PRIO. Must be between | |
845 | * 0 and TK_MAX_PRIO. */ | |
846 | { | |
847 | char *realName, *buffer; | |
848 | int fileId, result; | |
849 | struct stat statBuf; | |
850 | ||
851 | realName = Tcl_TildeSubst(interp, fileName); | |
852 | if (fileName == NULL) { | |
853 | return TCL_ERROR; | |
854 | } | |
855 | #ifdef MSDOS | |
856 | fileId = open(realName, O_RDONLY | O_BINARY, 0); | |
857 | #else | |
858 | fileId = open(realName, O_RDONLY, 0); | |
859 | #endif | |
860 | if (fileId < 0) { | |
861 | Tcl_AppendResult(interp, "couldn't read file \"", fileName, "\"", | |
862 | (char *) NULL); | |
863 | return TCL_ERROR; | |
864 | } | |
865 | if (fstat(fileId, &statBuf) == -1) { | |
866 | Tcl_AppendResult(interp, "couldn't stat file \"", fileName, "\"", | |
867 | (char *) NULL); | |
868 | close(fileId); | |
869 | return TCL_ERROR; | |
870 | } | |
871 | buffer = (char *) ckalloc((unsigned) statBuf.st_size+1); | |
872 | #ifdef MSDOS | |
873 | if (read(fileId, buffer, (int) statBuf.st_size) < 0) { | |
874 | #else | |
875 | if (read(fileId, buffer, (int) statBuf.st_size) != statBuf.st_size) { | |
876 | #endif | |
877 | Tcl_AppendResult(interp, "error reading file \"", fileName, "\"", | |
878 | (char *) NULL); | |
879 | close(fileId); | |
880 | return TCL_ERROR; | |
881 | } | |
882 | close(fileId); | |
883 | buffer[statBuf.st_size] = 0; | |
884 | result = AddFromString(interp, tkwin, buffer, priority); | |
885 | ckfree(buffer); | |
886 | return result; | |
887 | } | |
888 | \f | |
889 | /* | |
890 | *-------------------------------------------------------------- | |
891 | * | |
892 | * NewArray -- | |
893 | * | |
894 | * Create a new ElArray structure of a given size. | |
895 | * | |
896 | * Results: | |
897 | * The return value is a pointer to a properly initialized | |
898 | * element array with "numEls" space. The array is marked | |
899 | * as having no active elements. | |
900 | * | |
901 | * Side effects: | |
902 | * Memory is allocated. | |
903 | * | |
904 | *-------------------------------------------------------------- | |
905 | */ | |
906 | ||
907 | static ElArray * | |
908 | NewArray(numEls) | |
909 | int numEls; /* How many elements of space to allocate. */ | |
910 | { | |
911 | register ElArray *arrayPtr; | |
912 | ||
913 | arrayPtr = (ElArray *) ckalloc(EL_ARRAY_SIZE(numEls)); | |
914 | arrayPtr->arraySize = numEls; | |
915 | arrayPtr->numUsed = 0; | |
916 | arrayPtr->nextToUse = arrayPtr->els; | |
917 | return arrayPtr; | |
918 | } | |
919 | \f | |
920 | /* | |
921 | *-------------------------------------------------------------- | |
922 | * | |
923 | * ExtendArray -- | |
924 | * | |
925 | * Add a new element to an array, extending the array if | |
926 | * necessary. | |
927 | * | |
928 | * Results: | |
929 | * The return value is a pointer to the new array, which | |
930 | * will be different from arrayPtr if the array got expanded. | |
931 | * | |
932 | * Side effects: | |
933 | * Memory may be allocated or freed. | |
934 | * | |
935 | *-------------------------------------------------------------- | |
936 | */ | |
937 | ||
938 | static ElArray * | |
939 | ExtendArray(arrayPtr, elPtr) | |
940 | register ElArray *arrayPtr; /* Array to be extended. */ | |
941 | register Element *elPtr; /* Element to be copied into array. */ | |
942 | { | |
943 | /* | |
944 | * If the current array has filled up, make it bigger. | |
945 | */ | |
946 | ||
947 | if (arrayPtr->numUsed >= arrayPtr->arraySize) { | |
948 | register ElArray *newPtr; | |
949 | ||
950 | newPtr = (ElArray *) ckalloc(EL_ARRAY_SIZE(2*arrayPtr->arraySize)); | |
951 | newPtr->arraySize = 2*arrayPtr->arraySize; | |
952 | newPtr->numUsed = arrayPtr->numUsed; | |
953 | newPtr->nextToUse = &newPtr->els[newPtr->numUsed]; | |
954 | memcpy((VOID *) newPtr->els, (VOID *) arrayPtr->els, | |
955 | (arrayPtr->arraySize*sizeof(Element))); | |
956 | ckfree((char *) arrayPtr); | |
957 | arrayPtr = newPtr; | |
958 | } | |
959 | ||
960 | *arrayPtr->nextToUse = *elPtr; | |
961 | arrayPtr->nextToUse++; | |
962 | arrayPtr->numUsed++; | |
963 | return arrayPtr; | |
964 | } | |
965 | \f | |
966 | /* | |
967 | *-------------------------------------------------------------- | |
968 | * | |
969 | * SetupStacks -- | |
970 | * | |
971 | * Arrange the stacks so that they cache all the option | |
972 | * information for a particular window. | |
973 | * | |
974 | * Results: | |
975 | * None. | |
976 | * | |
977 | * Side effects: | |
978 | * The stacks are modified to hold information for tkwin | |
979 | * and all its ancestors in the window hierarchy. | |
980 | * | |
981 | *-------------------------------------------------------------- | |
982 | */ | |
983 | ||
984 | static void | |
985 | SetupStacks(winPtr, leaf) | |
986 | TkWindow *winPtr; /* Window for which information is to | |
987 | * be cached. */ | |
988 | int leaf; /* Non-zero means this is the leaf | |
989 | * window being probed. Zero means this | |
990 | * is an ancestor of the desired leaf. */ | |
991 | { | |
992 | int level, i, *iPtr; | |
993 | register StackLevel *levelPtr; | |
994 | register ElArray *arrayPtr; | |
995 | ||
996 | /* | |
997 | * The following array defines the order in which the current | |
998 | * stacks are searched to find matching entries to add to the | |
999 | * stacks. Given the current priority-based scheme, the order | |
1000 | * below is no longer relevant; all that matters is that an | |
1001 | * element is on the list *somewhere*. The ordering is a relic | |
1002 | * of the old days when priorities were determined differently. | |
1003 | */ | |
1004 | ||
1005 | static int searchOrder[] = {WILDCARD_NODE_CLASS, WILDCARD_NODE_NAME, | |
1006 | EXACT_NODE_CLASS, EXACT_NODE_NAME, -1}; | |
1007 | ||
1008 | if (winPtr->mainPtr->optionRootPtr == NULL) { | |
1009 | OptionInit(winPtr->mainPtr); | |
1010 | } | |
1011 | ||
1012 | /* | |
1013 | * Step 1: make sure that options are cached for this window's | |
1014 | * parent. | |
1015 | */ | |
1016 | ||
1017 | if (winPtr->parentPtr != NULL) { | |
1018 | level = winPtr->parentPtr->optionLevel; | |
1019 | if ((level == -1) || (cachedWindow == NULL)) { | |
1020 | SetupStacks(winPtr->parentPtr, 0); | |
1021 | level = winPtr->parentPtr->optionLevel; | |
1022 | } | |
1023 | level++; | |
1024 | } else { | |
1025 | level = 1; | |
1026 | } | |
1027 | ||
1028 | /* | |
1029 | * Step 2: pop extra unneeded information off the stacks and | |
1030 | * mark those windows as no longer having cached information. | |
1031 | */ | |
1032 | ||
1033 | if (curLevel >= level) { | |
1034 | while (curLevel >= level) { | |
1035 | levels[curLevel].winPtr->optionLevel = -1; | |
1036 | curLevel--; | |
1037 | } | |
1038 | levelPtr = &levels[level]; | |
1039 | for (i = 0; i < NUM_STACKS; i++) { | |
1040 | arrayPtr = stacks[i]; | |
1041 | arrayPtr->numUsed = levelPtr->bases[i]; | |
1042 | arrayPtr->nextToUse = &arrayPtr->els[arrayPtr->numUsed]; | |
1043 | } | |
1044 | } | |
1045 | curLevel = winPtr->optionLevel = level; | |
1046 | ||
1047 | /* | |
1048 | * Step 3: if the root database information isn't loaded or | |
1049 | * isn't valid, initialize level 0 of the stack from the | |
1050 | * database root (this only happens if winPtr is a main window). | |
1051 | */ | |
1052 | ||
1053 | if ((curLevel == 1) | |
1054 | && ((cachedWindow == NULL) | |
1055 | || (cachedWindow->mainPtr != winPtr->mainPtr))) { | |
1056 | for (i = 0; i < NUM_STACKS; i++) { | |
1057 | arrayPtr = stacks[i]; | |
1058 | arrayPtr->numUsed = 0; | |
1059 | arrayPtr->nextToUse = arrayPtr->els; | |
1060 | } | |
1061 | ExtendStacks(winPtr->mainPtr->optionRootPtr, 0); | |
1062 | } | |
1063 | ||
1064 | /* | |
1065 | * Step 4: create a new stack level; grow the level array if | |
1066 | * we've run out of levels. Clear the stacks for EXACT_LEAF_NAME | |
1067 | * and EXACT_LEAF_CLASS (anything that was there is of no use | |
1068 | * any more). | |
1069 | */ | |
1070 | ||
1071 | if (curLevel >= numLevels) { | |
1072 | StackLevel *newLevels; | |
1073 | ||
1074 | newLevels = (StackLevel *) ckalloc((unsigned) | |
1075 | (numLevels*2*sizeof(StackLevel))); | |
1076 | memcpy((VOID *) newLevels, (VOID *) levels, | |
1077 | (numLevels*sizeof(StackLevel))); | |
1078 | ckfree((char *) levels); | |
1079 | numLevels *= 2; | |
1080 | levels = newLevels; | |
1081 | } | |
1082 | levelPtr = &levels[curLevel]; | |
1083 | levelPtr->winPtr = winPtr; | |
1084 | arrayPtr = stacks[EXACT_LEAF_NAME]; | |
1085 | arrayPtr->numUsed = 0; | |
1086 | arrayPtr->nextToUse = arrayPtr->els; | |
1087 | arrayPtr = stacks[EXACT_LEAF_CLASS]; | |
1088 | arrayPtr->numUsed = 0; | |
1089 | arrayPtr->nextToUse = arrayPtr->els; | |
1090 | levelPtr->bases[EXACT_LEAF_NAME] = stacks[EXACT_LEAF_NAME]->numUsed; | |
1091 | levelPtr->bases[EXACT_LEAF_CLASS] = stacks[EXACT_LEAF_CLASS]->numUsed; | |
1092 | levelPtr->bases[EXACT_NODE_NAME] = stacks[EXACT_NODE_NAME]->numUsed; | |
1093 | levelPtr->bases[EXACT_NODE_CLASS] = stacks[EXACT_NODE_CLASS]->numUsed; | |
1094 | levelPtr->bases[WILDCARD_LEAF_NAME] = stacks[WILDCARD_LEAF_NAME]->numUsed; | |
1095 | levelPtr->bases[WILDCARD_LEAF_CLASS] = stacks[WILDCARD_LEAF_CLASS]->numUsed; | |
1096 | levelPtr->bases[WILDCARD_NODE_NAME] = stacks[WILDCARD_NODE_NAME]->numUsed; | |
1097 | levelPtr->bases[WILDCARD_NODE_CLASS] = stacks[WILDCARD_NODE_CLASS]->numUsed; | |
1098 | ||
1099 | ||
1100 | /* | |
1101 | * Step 5: scan the current stack level looking for matches to this | |
1102 | * window's name or class; where found, add new information to the | |
1103 | * stacks. | |
1104 | */ | |
1105 | ||
1106 | for (iPtr = searchOrder; *iPtr != -1; iPtr++) { | |
1107 | register Element *elPtr; | |
1108 | int count; | |
1109 | Tk_Uid id; | |
1110 | ||
1111 | i = *iPtr; | |
1112 | if (i & CLASS) { | |
1113 | id = winPtr->classUid; | |
1114 | } else { | |
1115 | id = winPtr->nameUid; | |
1116 | } | |
1117 | elPtr = stacks[i]->els; | |
1118 | count = levelPtr->bases[i]; | |
1119 | ||
1120 | /* | |
1121 | * For wildcard stacks, check all entries; for non-wildcard | |
1122 | * stacks, only check things that matched in the parent. | |
1123 | */ | |
1124 | ||
1125 | if (!(i & WILDCARD)) { | |
1126 | elPtr += levelPtr[-1].bases[i]; | |
1127 | count -= levelPtr[-1].bases[i]; | |
1128 | } | |
1129 | for ( ; count > 0; elPtr++, count--) { | |
1130 | if (elPtr->nameUid != id) { | |
1131 | continue; | |
1132 | } | |
1133 | ExtendStacks(elPtr->child.arrayPtr, leaf); | |
1134 | } | |
1135 | } | |
1136 | cachedWindow = winPtr; | |
1137 | } | |
1138 | \f | |
1139 | /* | |
1140 | *-------------------------------------------------------------- | |
1141 | * | |
1142 | * ExtendStacks -- | |
1143 | * | |
1144 | * Given an element array, copy all the elements from the | |
1145 | * array onto the system stacks (except for irrelevant leaf | |
1146 | * elements). | |
1147 | * | |
1148 | * Results: | |
1149 | * None. | |
1150 | * | |
1151 | * Side effects: | |
1152 | * The option stacks are extended. | |
1153 | * | |
1154 | *-------------------------------------------------------------- | |
1155 | */ | |
1156 | ||
1157 | static void | |
1158 | ExtendStacks(arrayPtr, leaf) | |
1159 | ElArray *arrayPtr; /* Array of elements to copy onto stacks. */ | |
1160 | int leaf; /* If zero, then don't copy exact leaf | |
1161 | * elements. */ | |
1162 | { | |
1163 | register int count; | |
1164 | register Element *elPtr; | |
1165 | ||
1166 | for (elPtr = arrayPtr->els, count = arrayPtr->numUsed; | |
1167 | count > 0; elPtr++, count--) { | |
1168 | if (!(elPtr->flags & (NODE|WILDCARD)) && !leaf) { | |
1169 | continue; | |
1170 | } | |
1171 | stacks[elPtr->flags] = ExtendArray(stacks[elPtr->flags], elPtr); | |
1172 | } | |
1173 | } | |
1174 | \f | |
1175 | /* | |
1176 | *-------------------------------------------------------------- | |
1177 | * | |
1178 | * OptionInit -- | |
1179 | * | |
1180 | * Initialize data structures for option handling. | |
1181 | * | |
1182 | * Results: | |
1183 | * None. | |
1184 | * | |
1185 | * Side effects: | |
1186 | * Option-related data structures get initialized. | |
1187 | * | |
1188 | *-------------------------------------------------------------- | |
1189 | */ | |
1190 | ||
1191 | static void | |
1192 | OptionInit(mainPtr) | |
1193 | register TkMainInfo *mainPtr; /* Top-level information about | |
1194 | * window that isn't initialized | |
1195 | * yet. */ | |
1196 | { | |
1197 | int i; | |
1198 | Tcl_Interp *interp; | |
1199 | ||
1200 | /* | |
1201 | * First, once-only initialization. | |
1202 | */ | |
1203 | ||
1204 | if (numLevels == 0) { | |
1205 | ||
1206 | numLevels = 5; | |
1207 | levels = (StackLevel *) ckalloc((unsigned) (5*sizeof(StackLevel))); | |
1208 | for (i = 0; i < NUM_STACKS; i++) { | |
1209 | stacks[i] = NewArray(10); | |
1210 | levels[0].bases[i] = 0; | |
1211 | } | |
1212 | ||
1213 | defaultMatch.nameUid = NULL; | |
1214 | defaultMatch.child.valueUid = NULL; | |
1215 | defaultMatch.priority = -1; | |
1216 | defaultMatch.flags = 0; | |
1217 | } | |
1218 | ||
1219 | /* | |
1220 | * Then, per-main-window initialization. Create and delete dummy | |
1221 | * interpreter for message logging. | |
1222 | */ | |
1223 | ||
1224 | mainPtr->optionRootPtr = NewArray(20); | |
1225 | interp = Tcl_CreateInterp(); | |
1226 | (void) GetDefaultOptions(interp, mainPtr->winPtr); | |
1227 | Tcl_DeleteInterp(interp); | |
1228 | } | |
1229 | \f | |
1230 | /* | |
1231 | *-------------------------------------------------------------- | |
1232 | * | |
1233 | * ClearOptionTree -- | |
1234 | * | |
1235 | * This procedure is called to erase everything in a | |
1236 | * hierarchical option database. | |
1237 | * | |
1238 | * Results: | |
1239 | * None. | |
1240 | * | |
1241 | * Side effects: | |
1242 | * All the options associated with arrayPtr are deleted, | |
1243 | * along with all option subtrees. The space pointed to | |
1244 | * by arrayPtr is freed. | |
1245 | * | |
1246 | *-------------------------------------------------------------- | |
1247 | */ | |
1248 | ||
1249 | static void | |
1250 | ClearOptionTree(arrayPtr) | |
1251 | ElArray *arrayPtr; /* Array of options; delete everything | |
1252 | * referred to recursively by this. */ | |
1253 | { | |
1254 | register Element *elPtr; | |
1255 | int count; | |
1256 | ||
1257 | for (count = arrayPtr->numUsed, elPtr = arrayPtr->els; count > 0; | |
1258 | count--, elPtr++) { | |
1259 | if (elPtr->flags & NODE) { | |
1260 | ClearOptionTree(elPtr->child.arrayPtr); | |
1261 | } | |
1262 | } | |
1263 | ckfree((char *) arrayPtr); | |
1264 | } | |
1265 | \f | |
1266 | /* | |
1267 | *-------------------------------------------------------------- | |
1268 | * | |
1269 | * GetDefaultOptions -- | |
1270 | * | |
1271 | * This procedure is invoked to load the default set of options | |
1272 | * for a window. | |
1273 | * | |
1274 | * Results: | |
1275 | * None. | |
1276 | * | |
1277 | * Side effects: | |
1278 | * Options are added to those for winPtr's main window. If | |
1279 | * there exists a RESOURCE_MANAGER proprety for winPtr's | |
1280 | * display, that is used. Otherwise, the .Xdefaults file in | |
1281 | * the user's home directory is used. | |
1282 | * | |
1283 | *-------------------------------------------------------------- | |
1284 | */ | |
1285 | ||
1286 | static int | |
1287 | GetDefaultOptions(interp, winPtr) | |
1288 | Tcl_Interp *interp; /* Interpreter to use for error reporting. */ | |
1289 | TkWindow *winPtr; /* Fetch option defaults for main window | |
1290 | * associated with this. */ | |
1291 | { | |
1292 | char *regProp, *home, *fileName; | |
1293 | int result, actualFormat; | |
1294 | unsigned long numItems, bytesAfter; | |
1295 | Atom actualType; | |
1296 | ||
1297 | /* | |
1298 | * Try the RESOURCE_MANAGER property on the root window first. | |
1299 | */ | |
1300 | ||
1301 | regProp = NULL; | |
1302 | result = XGetWindowProperty(winPtr->display, | |
1303 | Tk_DefaultRootWindow(winPtr->display), | |
1304 | XA_RESOURCE_MANAGER, 0, 100000, | |
1305 | False, XA_STRING, &actualType, &actualFormat, | |
1306 | &numItems, &bytesAfter, (unsigned char **) ®Prop); | |
1307 | ||
1308 | if ((result == Success) && (actualType == XA_STRING) | |
1309 | && (actualFormat == 8)) { | |
1310 | result = AddFromString(interp, (Tk_Window) winPtr, regProp, | |
1311 | TK_USER_DEFAULT_PRIO); | |
1312 | XFree(regProp); | |
1313 | return result; | |
1314 | } | |
1315 | ||
1316 | /* | |
1317 | * No luck there. Try a .Xdefaults file in the user's home | |
1318 | * directory. | |
1319 | */ | |
1320 | ||
1321 | if (regProp != NULL) { | |
1322 | XFree(regProp); | |
1323 | } | |
1324 | home = getenv("HOME"); | |
1325 | if (home == NULL) { | |
1326 | sprintf(interp->result, | |
1327 | "no RESOURCE_MANAGER property and no HOME envariable"); | |
1328 | return TCL_ERROR; | |
1329 | } | |
1330 | fileName = (char *) ckalloc((unsigned) (strlen(home) + 20)); | |
1331 | sprintf(fileName, "%s/.Xdefaults", home); | |
1332 | result = ReadOptionFile(interp, (Tk_Window) winPtr, fileName, | |
1333 | TK_USER_DEFAULT_PRIO); | |
1334 | ckfree(fileName); | |
1335 | return result; | |
1336 | } |