~ubuntu-branches/ubuntu/dapper/tk8.0/dapper-updates

« back to all changes in this revision

Viewing changes to generic/tkOption.c

  • Committer: Bazaar Package Importer
  • Author(s): Mike Markley
  • Date: 2001-07-24 21:57:40 UTC
  • Revision ID: james.westby@ubuntu.com-20010724215740-r70t25rtmbqjil2h
Tags: upstream-8.0.5
ImportĀ upstreamĀ versionĀ 8.0.5

Show diffs side-by-side

added added

removed removed

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