OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [tags/] [start/] [insight/] [tk/] [generic/] [tkPack.c] - Blame information for rev 578

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
2
 * tkPack.c --
3
 *
4
 *      This file contains code to implement the "packer"
5
 *      geometry manager for Tk.
6
 *
7
 * Copyright (c) 1990-1994 The Regents of the University of California.
8
 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
9
 *
10
 * See the file "license.terms" for information on usage and redistribution
11
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12
 *
13
 * RCS: @(#) $Id: tkPack.c,v 1.1.1.1 2002-01-16 10:25:52 markom Exp $
14
 */
15
 
16
#include "tkPort.h"
17
#include "tkInt.h"
18
 
19
typedef enum {TOP, BOTTOM, LEFT, RIGHT} Side;
20
 
21
/* For each window that the packer cares about (either because
22
 * the window is managed by the packer or because the window
23
 * has slaves that are managed by the packer), there is a
24
 * structure of the following type:
25
 */
26
 
27
typedef struct Packer {
28
    Tk_Window tkwin;            /* Tk token for window.  NULL means that
29
                                 * the window has been deleted, but the
30
                                 * packet hasn't had a chance to clean up
31
                                 * yet because the structure is still in
32
                                 * use. */
33
    struct Packer *masterPtr;   /* Master window within which this window
34
                                 * is packed (NULL means this window
35
                                 * isn't managed by the packer). */
36
    struct Packer *nextPtr;     /* Next window packed within same
37
                                 * parent.  List is priority-ordered:
38
                                 * first on list gets packed first. */
39
    struct Packer *slavePtr;    /* First in list of slaves packed
40
                                 * inside this window (NULL means
41
                                 * no packed slaves). */
42
    Side side;                  /* Side of parent against which
43
                                 * this window is packed. */
44
    Tk_Anchor anchor;           /* If frame allocated for window is larger
45
                                 * than window needs, this indicates how
46
                                 * where to position window in frame. */
47
    int padX, padY;             /* Total additional pixels to leave around the
48
                                 * window (half of this space is left on each
49
                                 * side).  This is space *outside* the window:
50
                                 * we'll allocate extra space in frame but
51
                                 * won't enlarge window). */
52
    int iPadX, iPadY;           /* Total extra pixels to allocate inside the
53
                                 * window (half this amount will appear on
54
                                 * each side). */
55
    int doubleBw;               /* Twice the window's last known border
56
                                 * width.  If this changes, the window
57
                                 * must be repacked within its parent. */
58
    int *abortPtr;              /* If non-NULL, it means that there is a nested
59
                                 * call to ArrangePacking already working on
60
                                 * this window.  *abortPtr may be set to 1 to
61
                                 * abort that nested call.  This happens, for
62
                                 * example, if tkwin or any of its slaves
63
                                 * is deleted. */
64
    int flags;                  /* Miscellaneous flags;  see below
65
                                 * for definitions. */
66
} Packer;
67
 
68
/*
69
 * Flag values for Packer structures:
70
 *
71
 * REQUESTED_REPACK:            1 means a Tcl_DoWhenIdle request
72
 *                              has already been made to repack
73
 *                              all the slaves of this window.
74
 * FILLX:                       1 means if frame allocated for window
75
 *                              is wider than window needs, expand window
76
 *                              to fill frame.  0 means don't make window
77
 *                              any larger than needed.
78
 * FILLY:                       Same as FILLX, except for height.
79
 * EXPAND:                      1 means this window's frame will absorb any
80
 *                              extra space in the parent window.
81
 * OLD_STYLE:                   1 means this window is being managed with
82
 *                              the old-style packer algorithms (before
83
 *                              Tk version 3.3).  The main difference is
84
 *                              that padding and filling are done differently.
85
 * DONT_PROPAGATE:              1 means don't set this window's requested
86
 *                              size.  0 means if this window is a master
87
 *                              then Tk will set its requested size to fit
88
 *                              the needs of its slaves.
89
 */
90
 
91
#define REQUESTED_REPACK        1
92
#define FILLX                   2
93
#define FILLY                   4
94
#define EXPAND                  8
95
#define OLD_STYLE               16
96
#define DONT_PROPAGATE          32
97
 
98
/*
99
 * Hash table used to map from Tk_Window tokens to corresponding
100
 * Packer structures:
101
 */
102
 
103
static Tcl_HashTable packerHashTable;
104
 
105
/*
106
 * Have statics in this module been initialized?
107
 */
108
 
109
static int initialized = 0;
110
 
111
/*
112
 * The following structure is the official type record for the
113
 * packer:
114
 */
115
 
116
static void             PackReqProc _ANSI_ARGS_((ClientData clientData,
117
                            Tk_Window tkwin));
118
static void             PackLostSlaveProc _ANSI_ARGS_((ClientData clientData,
119
                            Tk_Window tkwin));
120
 
121
static Tk_GeomMgr packerType = {
122
    "pack",                     /* name */
123
    PackReqProc,                /* requestProc */
124
    PackLostSlaveProc,          /* lostSlaveProc */
125
};
126
 
127
/*
128
 * Forward declarations for procedures defined later in this file:
129
 */
130
 
131
static void             ArrangePacking _ANSI_ARGS_((ClientData clientData));
132
static int              ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
133
                            Tk_Window tkwin, int argc, char *argv[]));
134
static void             DestroyPacker _ANSI_ARGS_((char *memPtr));
135
static Packer *         GetPacker _ANSI_ARGS_((Tk_Window tkwin));
136
static int              PackAfter _ANSI_ARGS_((Tcl_Interp *interp,
137
                            Packer *prevPtr, Packer *masterPtr, int argc,
138
                            char **argv));
139
static void             PackReqProc _ANSI_ARGS_((ClientData clientData,
140
                            Tk_Window tkwin));
141
static void             PackStructureProc _ANSI_ARGS_((ClientData clientData,
142
                            XEvent *eventPtr));
143
static void             Unlink _ANSI_ARGS_((Packer *packPtr));
144
static int              XExpansion _ANSI_ARGS_((Packer *slavePtr,
145
                            int cavityWidth));
146
static int              YExpansion _ANSI_ARGS_((Packer *slavePtr,
147
                            int cavityHeight));
148
 
149
/*
150
 *--------------------------------------------------------------
151
 *
152
 * Tk_PackCmd --
153
 *
154
 *      This procedure is invoked to process the "pack" Tcl command.
155
 *      See the user documentation for details on what it does.
156
 *
157
 * Results:
158
 *      A standard Tcl result.
159
 *
160
 * Side effects:
161
 *      See the user documentation.
162
 *
163
 *--------------------------------------------------------------
164
 */
165
 
166
int
167
Tk_PackCmd(clientData, interp, argc, argv)
168
    ClientData clientData;      /* Main window associated with
169
                                 * interpreter. */
170
    Tcl_Interp *interp;         /* Current interpreter. */
171
    int argc;                   /* Number of arguments. */
172
    char **argv;                /* Argument strings. */
173
{
174
    Tk_Window tkwin = (Tk_Window) clientData;
175
    size_t length;
176
    int c;
177
 
178
    if ((argc >= 2) && (argv[1][0] == '.')) {
179
        return ConfigureSlaves(interp, tkwin, argc-1, argv+1);
180
    }
181
    if (argc < 3) {
182
        Tcl_AppendResult(interp, "wrong # args: should be \"",
183
                argv[0], " option arg ?arg ...?\"", (char *) NULL);
184
        return TCL_ERROR;
185
    }
186
    c = argv[1][0];
187
    length = strlen(argv[1]);
188
    if ((c == 'a') && (length >= 2)
189
            && (strncmp(argv[1], "after", length) == 0)) {
190
        Packer *prevPtr;
191
        Tk_Window tkwin2;
192
 
193
        tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
194
        if (tkwin2 == NULL) {
195
            return TCL_ERROR;
196
        }
197
        prevPtr = GetPacker(tkwin2);
198
        if (prevPtr->masterPtr == NULL) {
199
            Tcl_AppendResult(interp, "window \"", argv[2],
200
                    "\" isn't packed", (char *) NULL);
201
            return TCL_ERROR;
202
        }
203
        return PackAfter(interp, prevPtr, prevPtr->masterPtr, argc-3, argv+3);
204
    } else if ((c == 'a') && (length >= 2)
205
            && (strncmp(argv[1], "append", length) == 0)) {
206
        Packer *masterPtr;
207
        register Packer *prevPtr;
208
        Tk_Window tkwin2;
209
 
210
        tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
211
        if (tkwin2 == NULL) {
212
            return TCL_ERROR;
213
        }
214
        masterPtr = GetPacker(tkwin2);
215
        prevPtr = masterPtr->slavePtr;
216
        if (prevPtr != NULL) {
217
            while (prevPtr->nextPtr != NULL) {
218
                prevPtr = prevPtr->nextPtr;
219
            }
220
        }
221
        return PackAfter(interp, prevPtr, masterPtr, argc-3, argv+3);
222
    } else if ((c == 'b') && (strncmp(argv[1], "before", length) == 0)) {
223
        Packer *packPtr, *masterPtr;
224
        register Packer *prevPtr;
225
        Tk_Window tkwin2;
226
 
227
        tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
228
        if (tkwin2 == NULL) {
229
            return TCL_ERROR;
230
        }
231
        packPtr = GetPacker(tkwin2);
232
        if (packPtr->masterPtr == NULL) {
233
            Tcl_AppendResult(interp, "window \"", argv[2],
234
                    "\" isn't packed", (char *) NULL);
235
            return TCL_ERROR;
236
        }
237
        masterPtr = packPtr->masterPtr;
238
        prevPtr = masterPtr->slavePtr;
239
        if (prevPtr == packPtr) {
240
            prevPtr = NULL;
241
        } else {
242
            for ( ; ; prevPtr = prevPtr->nextPtr) {
243
                if (prevPtr == NULL) {
244
                    panic("\"pack before\" couldn't find predecessor");
245
                }
246
                if (prevPtr->nextPtr == packPtr) {
247
                    break;
248
                }
249
            }
250
        }
251
        return PackAfter(interp, prevPtr, masterPtr, argc-3, argv+3);
252
    } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
253
        if (argv[2][0] != '.') {
254
            Tcl_AppendResult(interp, "bad argument \"", argv[2],
255
                    "\": must be name of window", (char *) NULL);
256
            return TCL_ERROR;
257
        }
258
        return ConfigureSlaves(interp, tkwin, argc-2, argv+2);
259
    } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
260
        Tk_Window slave;
261
        Packer *slavePtr;
262
        int i;
263
 
264
        for (i = 2; i < argc; i++) {
265
            slave = Tk_NameToWindow(interp, argv[i], tkwin);
266
            if (slave == NULL) {
267
                continue;
268
            }
269
            slavePtr = GetPacker(slave);
270
            if ((slavePtr != NULL) && (slavePtr->masterPtr != NULL)) {
271
                Tk_ManageGeometry(slave, (Tk_GeomMgr *) NULL,
272
                        (ClientData) NULL);
273
                if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
274
                    Tk_UnmaintainGeometry(slavePtr->tkwin,
275
                            slavePtr->masterPtr->tkwin);
276
                }
277
                Unlink(slavePtr);
278
                Tk_UnmapWindow(slavePtr->tkwin);
279
            }
280
        }
281
    } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
282
        register Packer *slavePtr;
283
        Tk_Window slave;
284
        char buffer[300];
285
        static char *sideNames[] = {"top", "bottom", "left", "right"};
286
 
287
        if (argc != 3) {
288
            Tcl_AppendResult(interp, "wrong # args: should be \"",
289
                    argv[0], " info window\"", (char *) NULL);
290
            return TCL_ERROR;
291
        }
292
        slave = Tk_NameToWindow(interp, argv[2], tkwin);
293
        if (slave == NULL) {
294
            return TCL_ERROR;
295
        }
296
        slavePtr = GetPacker(slave);
297
        if (slavePtr->masterPtr == NULL) {
298
            Tcl_AppendResult(interp, "window \"", argv[2],
299
                    "\" isn't packed", (char *) NULL);
300
            return TCL_ERROR;
301
        }
302
        Tcl_AppendElement(interp, "-in");
303
        Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin));
304
        Tcl_AppendElement(interp, "-anchor");
305
        Tcl_AppendElement(interp, Tk_NameOfAnchor(slavePtr->anchor));
306
        Tcl_AppendResult(interp, " -expand ",
307
                (slavePtr->flags & EXPAND) ? "1" : "0", " -fill ",
308
                (char *) NULL);
309
        switch (slavePtr->flags & (FILLX|FILLY)) {
310
            case 0:
311
                Tcl_AppendResult(interp, "none", (char *) NULL);
312
                break;
313
            case FILLX:
314
                Tcl_AppendResult(interp, "x", (char *) NULL);
315
                break;
316
            case FILLY:
317
                Tcl_AppendResult(interp, "y", (char *) NULL);
318
                break;
319
            case FILLX|FILLY:
320
                Tcl_AppendResult(interp, "both", (char *) NULL);
321
                break;
322
        }
323
        sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
324
                slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
325
                slavePtr->padY/2);
326
        Tcl_AppendResult(interp, buffer, " -side ", sideNames[slavePtr->side],
327
                (char *) NULL);
328
    } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
329
        Tk_Window master;
330
        Packer *masterPtr;
331
        int propagate;
332
 
333
        if (argc > 4) {
334
            Tcl_AppendResult(interp, "wrong # args: should be \"",
335
                    argv[0], " propagate window ?boolean?\"", (char *) NULL);
336
            return TCL_ERROR;
337
        }
338
        master = Tk_NameToWindow(interp, argv[2], tkwin);
339
        if (master == NULL) {
340
            return TCL_ERROR;
341
        }
342
        masterPtr = GetPacker(master);
343
        if (argc == 3) {
344
            if (masterPtr->flags & DONT_PROPAGATE) {
345
                interp->result = "0";
346
            } else {
347
                interp->result = "1";
348
            }
349
            return TCL_OK;
350
        }
351
        if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
352
            return TCL_ERROR;
353
        }
354
        if (propagate) {
355
            masterPtr->flags &= ~DONT_PROPAGATE;
356
 
357
            /*
358
             * Repack the master to allow new geometry information to
359
             * propagate upwards to the master's master.
360
             */
361
 
362
            if (masterPtr->abortPtr != NULL) {
363
                *masterPtr->abortPtr = 1;
364
            }
365
            if (!(masterPtr->flags & REQUESTED_REPACK)) {
366
                masterPtr->flags |= REQUESTED_REPACK;
367
                Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
368
            }
369
        } else {
370
            masterPtr->flags |= DONT_PROPAGATE;
371
        }
372
    } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
373
        Tk_Window master;
374
        Packer *masterPtr, *slavePtr;
375
 
376
        if (argc != 3) {
377
            Tcl_AppendResult(interp, "wrong # args: should be \"",
378
                    argv[0], " slaves window\"", (char *) NULL);
379
            return TCL_ERROR;
380
        }
381
        master = Tk_NameToWindow(interp, argv[2], tkwin);
382
        if (master == NULL) {
383
            return TCL_ERROR;
384
        }
385
        masterPtr = GetPacker(master);
386
        for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
387
                slavePtr = slavePtr->nextPtr) {
388
            Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin));
389
        }
390
    } else if ((c == 'u') && (strncmp(argv[1], "unpack", length) == 0)) {
391
        Tk_Window tkwin2;
392
        Packer *packPtr;
393
 
394
        if (argc != 3) {
395
            Tcl_AppendResult(interp, "wrong # args: should be \"",
396
                    argv[0], " unpack window\"", (char *) NULL);
397
            return TCL_ERROR;
398
        }
399
        tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
400
        if (tkwin2 == NULL) {
401
            return TCL_ERROR;
402
        }
403
        packPtr = GetPacker(tkwin2);
404
        if ((packPtr != NULL) && (packPtr->masterPtr != NULL)) {
405
            Tk_ManageGeometry(tkwin2, (Tk_GeomMgr *) NULL,
406
                    (ClientData) NULL);
407
            if (packPtr->masterPtr->tkwin != Tk_Parent(packPtr->tkwin)) {
408
                Tk_UnmaintainGeometry(packPtr->tkwin,
409
                        packPtr->masterPtr->tkwin);
410
            }
411
            Unlink(packPtr);
412
            Tk_UnmapWindow(packPtr->tkwin);
413
        }
414
    } else {
415
        Tcl_AppendResult(interp, "bad option \"", argv[1],
416
                "\": must be configure, forget, info, ",
417
                "propagate, or slaves", (char *) NULL);
418
        return TCL_ERROR;
419
    }
420
    return TCL_OK;
421
}
422
 
423
/*
424
 *--------------------------------------------------------------
425
 *
426
 * PackReqProc --
427
 *
428
 *      This procedure is invoked by Tk_GeometryRequest for
429
 *      windows managed by the packer.
430
 *
431
 * Results:
432
 *      None.
433
 *
434
 * Side effects:
435
 *      Arranges for tkwin, and all its managed siblings, to
436
 *      be re-packed at the next idle point.
437
 *
438
 *--------------------------------------------------------------
439
 */
440
 
441
        /* ARGSUSED */
442
static void
443
PackReqProc(clientData, tkwin)
444
    ClientData clientData;      /* Packer's information about
445
                                 * window that got new preferred
446
                                 * geometry.  */
447
    Tk_Window tkwin;            /* Other Tk-related information
448
                                 * about the window. */
449
{
450
    register Packer *packPtr = (Packer *) clientData;
451
 
452
    packPtr = packPtr->masterPtr;
453
    if (!(packPtr->flags & REQUESTED_REPACK)) {
454
        packPtr->flags |= REQUESTED_REPACK;
455
        Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
456
    }
457
}
458
 
459
/*
460
 *--------------------------------------------------------------
461
 *
462
 * PackLostSlaveProc --
463
 *
464
 *      This procedure is invoked by Tk whenever some other geometry
465
 *      claims control over a slave that used to be managed by us.
466
 *
467
 * Results:
468
 *      None.
469
 *
470
 * Side effects:
471
 *      Forgets all packer-related information about the slave.
472
 *
473
 *--------------------------------------------------------------
474
 */
475
 
476
        /* ARGSUSED */
477
static void
478
PackLostSlaveProc(clientData, tkwin)
479
    ClientData clientData;      /* Packer structure for slave window that
480
                                 * was stolen away. */
481
    Tk_Window tkwin;            /* Tk's handle for the slave window. */
482
{
483
    register Packer *slavePtr = (Packer *) clientData;
484
 
485
    if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
486
        Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
487
    }
488
    Unlink(slavePtr);
489
    Tk_UnmapWindow(slavePtr->tkwin);
490
}
491
 
492
/*
493
 *--------------------------------------------------------------
494
 *
495
 * ArrangePacking --
496
 *
497
 *      This procedure is invoked (using the Tcl_DoWhenIdle
498
 *      mechanism) to re-layout a set of windows managed by
499
 *      the packer.  It is invoked at idle time so that a
500
 *      series of packer requests can be merged into a single
501
 *      layout operation.
502
 *
503
 * Results:
504
 *      None.
505
 *
506
 * Side effects:
507
 *      The packed slaves of masterPtr may get resized or
508
 *      moved.
509
 *
510
 *--------------------------------------------------------------
511
 */
512
 
513
static void
514
ArrangePacking(clientData)
515
    ClientData clientData;      /* Structure describing parent whose slaves
516
                                 * are to be re-layed out. */
517
{
518
    register Packer *masterPtr = (Packer *) clientData;
519
    register Packer *slavePtr;
520
    int cavityX, cavityY, cavityWidth, cavityHeight;
521
                                /* These variables keep track of the
522
                                 * as-yet-unallocated space remaining in
523
                                 * the middle of the parent window. */
524
    int frameX, frameY, frameWidth, frameHeight;
525
                                /* These variables keep track of the frame
526
                                 * allocated to the current window. */
527
    int x, y, width, height;    /* These variables are used to hold the
528
                                 * actual geometry of the current window. */
529
    int intBWidth;              /* Width of internal border in parent window,
530
                                 * if any. */
531
    int abort;                  /* May get set to non-zero to abort this
532
                                 * repacking operation. */
533
    int borderX, borderY;
534
    int maxWidth, maxHeight, tmp;
535
 
536
    masterPtr->flags &= ~REQUESTED_REPACK;
537
 
538
    /*
539
     * If the parent has no slaves anymore, then don't do anything
540
     * at all:  just leave the parent's size as-is.
541
     */
542
 
543
    if (masterPtr->slavePtr == NULL) {
544
        return;
545
    }
546
 
547
    /*
548
     * Abort any nested call to ArrangePacking for this window, since
549
     * we'll do everything necessary here, and set up so this call
550
     * can be aborted if necessary.
551
     */
552
 
553
    if (masterPtr->abortPtr != NULL) {
554
        *masterPtr->abortPtr = 1;
555
    }
556
    masterPtr->abortPtr = &abort;
557
    abort = 0;
558
    Tcl_Preserve((ClientData) masterPtr);
559
 
560
    /*
561
     * Pass #1: scan all the slaves to figure out the total amount
562
     * of space needed.  Two separate width and height values are
563
     * computed:
564
     *
565
     * width -          Holds the sum of the widths (plus padding) of
566
     *                  all the slaves seen so far that were packed LEFT
567
     *                  or RIGHT.
568
     * height -         Holds the sum of the heights (plus padding) of
569
     *                  all the slaves seen so far that were packed TOP
570
     *                  or BOTTOM.
571
     *
572
     * maxWidth -       Gradually builds up the width needed by the master
573
     *                  to just barely satisfy all the slave's needs.  For
574
     *                  each slave, the code computes the width needed for
575
     *                  all the slaves so far and updates maxWidth if the
576
     *                  new value is greater.
577
     * maxHeight -      Same as maxWidth, except keeps height info.
578
     */
579
 
580
    intBWidth = Tk_InternalBorderWidth(masterPtr->tkwin);
581
    width = height = maxWidth = maxHeight = 2*intBWidth;
582
    for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
583
            slavePtr = slavePtr->nextPtr) {
584
        if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
585
            tmp = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
586
                    + slavePtr->padX + slavePtr->iPadX + width;
587
            if (tmp > maxWidth) {
588
                maxWidth = tmp;
589
            }
590
            height += Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
591
                    + slavePtr->padY + slavePtr->iPadY;
592
        } else {
593
            tmp = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
594
                    + slavePtr->padY + slavePtr->iPadY + height;
595
            if (tmp > maxHeight) {
596
                maxHeight = tmp;
597
            }
598
            width += Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
599
                    + slavePtr->padX + slavePtr->iPadX;
600
        }
601
    }
602
    if (width > maxWidth) {
603
        maxWidth = width;
604
    }
605
    if (height > maxHeight) {
606
        maxHeight = height;
607
    }
608
 
609
    /*
610
     * If the total amount of space needed in the parent window has
611
     * changed, and if we're propagating geometry information, then
612
     * notify the next geometry manager up and requeue ourselves to
613
     * start again after the parent has had a chance to
614
     * resize us.
615
     */
616
 
617
    if (((maxWidth != Tk_ReqWidth(masterPtr->tkwin))
618
            || (maxHeight != Tk_ReqHeight(masterPtr->tkwin)))
619
            && !(masterPtr->flags & DONT_PROPAGATE)) {
620
        Tk_GeometryRequest(masterPtr->tkwin, maxWidth, maxHeight);
621
        masterPtr->flags |= REQUESTED_REPACK;
622
        Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
623
        goto done;
624
    }
625
 
626
    /*
627
     * Pass #2: scan the slaves a second time assigning
628
     * new sizes.  The "cavity" variables keep track of the
629
     * unclaimed space in the cavity of the window;  this
630
     * shrinks inward as we allocate windows around the
631
     * edges.  The "frame" variables keep track of the space
632
     * allocated to the current window and its frame.  The
633
     * current window is then placed somewhere inside the
634
     * frame, depending on anchor.
635
     */
636
 
637
    cavityX = cavityY = x = y = intBWidth;
638
    cavityWidth = Tk_Width(masterPtr->tkwin) - 2*intBWidth;
639
    cavityHeight = Tk_Height(masterPtr->tkwin) - 2*intBWidth;
640
    for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
641
            slavePtr = slavePtr->nextPtr) {
642
        if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
643
            frameWidth = cavityWidth;
644
            frameHeight = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
645
                    + slavePtr->padY + slavePtr->iPadY;
646
            if (slavePtr->flags & EXPAND) {
647
                frameHeight += YExpansion(slavePtr, cavityHeight);
648
            }
649
            cavityHeight -= frameHeight;
650
            if (cavityHeight < 0) {
651
                frameHeight += cavityHeight;
652
                cavityHeight = 0;
653
            }
654
            frameX = cavityX;
655
            if (slavePtr->side == TOP) {
656
                frameY = cavityY;
657
                cavityY += frameHeight;
658
            } else {
659
                frameY = cavityY + cavityHeight;
660
            }
661
        } else {
662
            frameHeight = cavityHeight;
663
            frameWidth = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
664
                    + slavePtr->padX + slavePtr->iPadX;
665
            if (slavePtr->flags & EXPAND) {
666
                frameWidth += XExpansion(slavePtr, cavityWidth);
667
            }
668
            cavityWidth -= frameWidth;
669
            if (cavityWidth < 0) {
670
                frameWidth += cavityWidth;
671
                cavityWidth = 0;
672
            }
673
            frameY = cavityY;
674
            if (slavePtr->side == LEFT) {
675
                frameX = cavityX;
676
                cavityX += frameWidth;
677
            } else {
678
                frameX = cavityX + cavityWidth;
679
            }
680
        }
681
 
682
        /*
683
         * Now that we've got the size of the frame for the window,
684
         * compute the window's actual size and location using the
685
         * fill, padding, and frame factors.  The variables "borderX"
686
         * and "borderY" are used to handle the differences between
687
         * old-style packing and the new style (in old-style, iPadX
688
         * and iPadY are always zero and padding is completely ignored
689
         * except when computing frame size).
690
         */
691
 
692
        if (slavePtr->flags & OLD_STYLE) {
693
            borderX = borderY = 0;
694
        } else {
695
            borderX = slavePtr->padX;
696
            borderY = slavePtr->padY;
697
        }
698
        width = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
699
                + slavePtr->iPadX;
700
        if ((slavePtr->flags & FILLX)
701
                || (width > (frameWidth - borderX))) {
702
            width = frameWidth - borderX;
703
        }
704
        height = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
705
                + slavePtr->iPadY;
706
        if ((slavePtr->flags & FILLY)
707
                || (height > (frameHeight - borderY))) {
708
            height = frameHeight - borderY;
709
        }
710
        borderX /= 2;
711
        borderY /= 2;
712
        switch (slavePtr->anchor) {
713
            case TK_ANCHOR_N:
714
                x = frameX + (frameWidth - width)/2;
715
                y = frameY + borderY;
716
                break;
717
            case TK_ANCHOR_NE:
718
                x = frameX + frameWidth - width - borderX;
719
                y = frameY + borderY;
720
                break;
721
            case TK_ANCHOR_E:
722
                x = frameX + frameWidth - width - borderX;
723
                y = frameY + (frameHeight - height)/2;
724
                break;
725
            case TK_ANCHOR_SE:
726
                x = frameX + frameWidth - width - borderX;
727
                y = frameY + frameHeight - height - borderY;
728
                break;
729
            case TK_ANCHOR_S:
730
                x = frameX + (frameWidth - width)/2;
731
                y = frameY + frameHeight - height - borderY;
732
                break;
733
            case TK_ANCHOR_SW:
734
                x = frameX + borderX;
735
                y = frameY + frameHeight - height - borderY;
736
                break;
737
            case TK_ANCHOR_W:
738
                x = frameX + borderX;
739
                y = frameY + (frameHeight - height)/2;
740
                break;
741
            case TK_ANCHOR_NW:
742
                x = frameX + borderX;
743
                y = frameY + borderY;
744
                break;
745
            case TK_ANCHOR_CENTER:
746
                x = frameX + (frameWidth - width)/2;
747
                y = frameY + (frameHeight - height)/2;
748
                break;
749
            default:
750
                panic("bad frame factor in ArrangePacking");
751
        }
752
        width -= slavePtr->doubleBw;
753
        height -= slavePtr->doubleBw;
754
 
755
        /*
756
         * The final step is to set the position, size, and mapped/unmapped
757
         * state of the slave.  If the slave is a child of the master, then
758
         * do this here.  Otherwise let Tk_MaintainGeometry do the work.
759
         */
760
 
761
        if (masterPtr->tkwin == Tk_Parent(slavePtr->tkwin)) {
762
            if ((width <= 0) || (height <= 0)) {
763
                Tk_UnmapWindow(slavePtr->tkwin);
764
            } else {
765
                if ((x != Tk_X(slavePtr->tkwin))
766
                        || (y != Tk_Y(slavePtr->tkwin))
767
                        || (width != Tk_Width(slavePtr->tkwin))
768
                        || (height != Tk_Height(slavePtr->tkwin))) {
769
                    Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height);
770
                }
771
                if (abort) {
772
                    goto done;
773
                }
774
 
775
                /*
776
                 * Don't map the slave if the master isn't mapped: wait
777
                 * until the master gets mapped later.
778
                 */
779
 
780
                if (Tk_IsMapped(masterPtr->tkwin)) {
781
                    Tk_MapWindow(slavePtr->tkwin);
782
                }
783
            }
784
        } else {
785
            if ((width <= 0) || (height <= 0)) {
786
                Tk_UnmaintainGeometry(slavePtr->tkwin, masterPtr->tkwin);
787
                Tk_UnmapWindow(slavePtr->tkwin);
788
            } else {
789
                Tk_MaintainGeometry(slavePtr->tkwin, masterPtr->tkwin,
790
                        x, y, width, height);
791
            }
792
        }
793
 
794
        /*
795
         * Changes to the window's structure could cause almost anything
796
         * to happen, including deleting the parent or child.  If this
797
         * happens, we'll be told to abort.
798
         */
799
 
800
        if (abort) {
801
            goto done;
802
        }
803
    }
804
 
805
    done:
806
    masterPtr->abortPtr = NULL;
807
    Tcl_Release((ClientData) masterPtr);
808
}
809
 
810
/*
811
 *----------------------------------------------------------------------
812
 *
813
 * XExpansion --
814
 *
815
 *      Given a list of packed slaves, the first of which is packed
816
 *      on the left or right and is expandable, compute how much to
817
 *      expand the child.
818
 *
819
 * Results:
820
 *      The return value is the number of additional pixels to give to
821
 *      the child.
822
 *
823
 * Side effects:
824
 *      None.
825
 *
826
 *----------------------------------------------------------------------
827
 */
828
 
829
static int
830
XExpansion(slavePtr, cavityWidth)
831
    register Packer *slavePtr;          /* First in list of remaining
832
                                         * slaves. */
833
    int cavityWidth;                    /* Horizontal space left for all
834
                                         * remaining slaves. */
835
{
836
    int numExpand, minExpand, curExpand;
837
    int childWidth;
838
 
839
    /*
840
     * This procedure is tricky because windows packed top or bottom can
841
     * be interspersed among expandable windows packed left or right.
842
     * Scan through the list, keeping a running sum of the widths of
843
     * all left and right windows (actually, count the cavity space not
844
     * allocated) and a running count of all expandable left and right
845
     * windows.  At each top or bottom window, and at the end of the
846
     * list, compute the expansion factor that seems reasonable at that
847
     * point.  Return the smallest factor seen at any of these points.
848
     */
849
 
850
    minExpand = cavityWidth;
851
    numExpand = 0;
852
    for ( ; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
853
        childWidth = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
854
                + slavePtr->padX + slavePtr->iPadX;
855
        if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
856
            curExpand = (cavityWidth - childWidth)/numExpand;
857
            if (curExpand < minExpand) {
858
                minExpand = curExpand;
859
            }
860
        } else {
861
            cavityWidth -= childWidth;
862
            if (slavePtr->flags & EXPAND) {
863
                numExpand++;
864
            }
865
        }
866
    }
867
    curExpand = cavityWidth/numExpand;
868
    if (curExpand < minExpand) {
869
        minExpand = curExpand;
870
    }
871
    return (minExpand < 0) ? 0 : minExpand;
872
}
873
 
874
/*
875
 *----------------------------------------------------------------------
876
 *
877
 * YExpansion --
878
 *
879
 *      Given a list of packed slaves, the first of which is packed
880
 *      on the top or bottom and is expandable, compute how much to
881
 *      expand the child.
882
 *
883
 * Results:
884
 *      The return value is the number of additional pixels to give to
885
 *      the child.
886
 *
887
 * Side effects:
888
 *      None.
889
 *
890
 *----------------------------------------------------------------------
891
 */
892
 
893
static int
894
YExpansion(slavePtr, cavityHeight)
895
    register Packer *slavePtr;          /* First in list of remaining
896
                                         * slaves. */
897
    int cavityHeight;                   /* Vertical space left for all
898
                                         * remaining slaves. */
899
{
900
    int numExpand, minExpand, curExpand;
901
    int childHeight;
902
 
903
    /*
904
     * See comments for XExpansion.
905
     */
906
 
907
    minExpand = cavityHeight;
908
    numExpand = 0;
909
    for ( ; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
910
        childHeight = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
911
                + slavePtr->padY + slavePtr->iPadY;
912
        if ((slavePtr->side == LEFT) || (slavePtr->side == RIGHT)) {
913
            curExpand = (cavityHeight - childHeight)/numExpand;
914
            if (curExpand < minExpand) {
915
                minExpand = curExpand;
916
            }
917
        } else {
918
            cavityHeight -= childHeight;
919
            if (slavePtr->flags & EXPAND) {
920
                numExpand++;
921
            }
922
        }
923
    }
924
    curExpand = cavityHeight/numExpand;
925
    if (curExpand < minExpand) {
926
        minExpand = curExpand;
927
    }
928
    return (minExpand < 0) ? 0 : minExpand;
929
}
930
 
931
/*
932
 *--------------------------------------------------------------
933
 *
934
 * GetPacker --
935
 *
936
 *      This internal procedure is used to locate a Packer
937
 *      structure for a given window, creating one if one
938
 *      doesn't exist already.
939
 *
940
 * Results:
941
 *      The return value is a pointer to the Packer structure
942
 *      corresponding to tkwin.
943
 *
944
 * Side effects:
945
 *      A new packer structure may be created.  If so, then
946
 *      a callback is set up to clean things up when the
947
 *      window is deleted.
948
 *
949
 *--------------------------------------------------------------
950
 */
951
 
952
static Packer *
953
GetPacker(tkwin)
954
    Tk_Window tkwin;            /* Token for window for which
955
                                 * packer structure is desired. */
956
{
957
    register Packer *packPtr;
958
    Tcl_HashEntry *hPtr;
959
    int new;
960
 
961
    if (!initialized) {
962
        initialized = 1;
963
        Tcl_InitHashTable(&packerHashTable, TCL_ONE_WORD_KEYS);
964
    }
965
 
966
    /*
967
     * See if there's already packer for this window.  If not,
968
     * then create a new one.
969
     */
970
 
971
    hPtr = Tcl_CreateHashEntry(&packerHashTable, (char *) tkwin, &new);
972
    if (!new) {
973
        return (Packer *) Tcl_GetHashValue(hPtr);
974
    }
975
    packPtr = (Packer *) ckalloc(sizeof(Packer));
976
    packPtr->tkwin = tkwin;
977
    packPtr->masterPtr = NULL;
978
    packPtr->nextPtr = NULL;
979
    packPtr->slavePtr = NULL;
980
    packPtr->side = TOP;
981
    packPtr->anchor = TK_ANCHOR_CENTER;
982
    packPtr->padX = packPtr->padY = 0;
983
    packPtr->iPadX = packPtr->iPadY = 0;
984
    packPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
985
    packPtr->abortPtr = NULL;
986
    packPtr->flags = 0;
987
    Tcl_SetHashValue(hPtr, packPtr);
988
    Tk_CreateEventHandler(tkwin, StructureNotifyMask,
989
            PackStructureProc, (ClientData) packPtr);
990
    return packPtr;
991
}
992
 
993
/*
994
 *--------------------------------------------------------------
995
 *
996
 * PackAfter --
997
 *
998
 *      This procedure does most of the real work of adding
999
 *      one or more windows into the packing order for its parent.
1000
 *
1001
 * Results:
1002
 *      A standard Tcl return value.
1003
 *
1004
 * Side effects:
1005
 *      The geometry of the specified windows may change, both now and
1006
 *      again in the future.
1007
 *
1008
 *--------------------------------------------------------------
1009
 */
1010
 
1011
static int
1012
PackAfter(interp, prevPtr, masterPtr, argc, argv)
1013
    Tcl_Interp *interp;         /* Interpreter for error reporting. */
1014
    Packer *prevPtr;            /* Pack windows in argv just after this
1015
                                 * window;  NULL means pack as first
1016
                                 * child of masterPtr. */
1017
    Packer *masterPtr;          /* Master in which to pack windows. */
1018
    int argc;                   /* Number of elements in argv. */
1019
    char **argv;                /* Array of lists, each containing 2
1020
                                 * elements:  window name and side
1021
                                 * against which to pack. */
1022
{
1023
    register Packer *packPtr;
1024
    Tk_Window tkwin, ancestor, parent;
1025
    size_t length;
1026
    char **options;
1027
    int index, tmp, optionCount, c;
1028
 
1029
    /*
1030
     * Iterate over all of the window specifiers, each consisting of
1031
     * two arguments.  The first argument contains the window name and
1032
     * the additional arguments contain options such as "top" or
1033
     * "padx 20".
1034
     */
1035
 
1036
    for ( ; argc > 0; argc -= 2, argv += 2, prevPtr = packPtr) {
1037
        if (argc < 2) {
1038
            Tcl_AppendResult(interp, "wrong # args: window \"",
1039
                    argv[0], "\" should be followed by options",
1040
                    (char *) NULL);
1041
            return TCL_ERROR;
1042
        }
1043
 
1044
        /*
1045
         * Find the packer for the window to be packed, and make sure
1046
         * that the window in which it will be packed is either its
1047
         * or a descendant of its parent.
1048
         */
1049
 
1050
        tkwin = Tk_NameToWindow(interp, argv[0], masterPtr->tkwin);
1051
        if (tkwin == NULL) {
1052
            return TCL_ERROR;
1053
        }
1054
 
1055
        parent = Tk_Parent(tkwin);
1056
        for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
1057
            if (ancestor == parent) {
1058
                break;
1059
            }
1060
            if (((Tk_FakeWin *) (ancestor))->flags & TK_TOP_LEVEL) {
1061
                badWindow:
1062
                Tcl_AppendResult(interp, "can't pack ", argv[0],
1063
                        " inside ", Tk_PathName(masterPtr->tkwin),
1064
                        (char *) NULL);
1065
                return TCL_ERROR;
1066
            }
1067
        }
1068
        if (((Tk_FakeWin *) (tkwin))->flags & TK_TOP_LEVEL) {
1069
            goto badWindow;
1070
        }
1071
        if (tkwin == masterPtr->tkwin) {
1072
            goto badWindow;
1073
        }
1074
        packPtr = GetPacker(tkwin);
1075
 
1076
        /*
1077
         * Process options for this window.
1078
         */
1079
 
1080
        if (Tcl_SplitList(interp, argv[1], &optionCount, &options) != TCL_OK) {
1081
            return TCL_ERROR;
1082
        }
1083
        packPtr->side = TOP;
1084
        packPtr->anchor = TK_ANCHOR_CENTER;
1085
        packPtr->padX = packPtr->padY = 0;
1086
        packPtr->iPadX = packPtr->iPadY = 0;
1087
        packPtr->flags &= ~(FILLX|FILLY|EXPAND);
1088
        packPtr->flags |= OLD_STYLE;
1089
        for (index = 0 ; index < optionCount; index++) {
1090
            char *curOpt = options[index];
1091
 
1092
            c = curOpt[0];
1093
            length = strlen(curOpt);
1094
 
1095
            if ((c == 't')
1096
                    && (strncmp(curOpt, "top", length)) == 0) {
1097
                packPtr->side = TOP;
1098
            } else if ((c == 'b')
1099
                    && (strncmp(curOpt, "bottom", length)) == 0) {
1100
                packPtr->side = BOTTOM;
1101
            } else if ((c == 'l')
1102
                    && (strncmp(curOpt, "left", length)) == 0) {
1103
                packPtr->side = LEFT;
1104
            } else if ((c == 'r')
1105
                    && (strncmp(curOpt, "right", length)) == 0) {
1106
                packPtr->side = RIGHT;
1107
            } else if ((c == 'e')
1108
                    && (strncmp(curOpt, "expand", length)) == 0) {
1109
                packPtr->flags |= EXPAND;
1110
            } else if ((c == 'f')
1111
                    && (strcmp(curOpt, "fill")) == 0) {
1112
                packPtr->flags |= FILLX|FILLY;
1113
            } else if ((length == 5) && (strcmp(curOpt, "fillx")) == 0) {
1114
                packPtr->flags |= FILLX;
1115
            } else if ((length == 5) && (strcmp(curOpt, "filly")) == 0) {
1116
                packPtr->flags |= FILLY;
1117
            } else if ((c == 'p') && (strcmp(curOpt, "padx")) == 0) {
1118
                if (optionCount < (index+2)) {
1119
                    missingPad:
1120
                    Tcl_AppendResult(interp, "wrong # args: \"", curOpt,
1121
                            "\" option must be followed by screen distance",
1122
                            (char *) NULL);
1123
                    goto error;
1124
                }
1125
                if ((Tk_GetPixels(interp, tkwin, options[index+1], &tmp)
1126
                        != TCL_OK) || (tmp < 0)) {
1127
                    badPad:
1128
                    Tcl_AppendResult(interp, "bad pad value \"",
1129
                            options[index+1],
1130
                            "\": must be positive screen distance",
1131
                            (char *) NULL);
1132
                    goto error;
1133
                }
1134
                packPtr->padX = tmp;
1135
                packPtr->iPadX = 0;
1136
                index++;
1137
            } else if ((c == 'p') && (strcmp(curOpt, "pady")) == 0) {
1138
                if (optionCount < (index+2)) {
1139
                    goto missingPad;
1140
                }
1141
                if ((Tk_GetPixels(interp, tkwin, options[index+1], &tmp)
1142
                        != TCL_OK) || (tmp < 0)) {
1143
                    goto badPad;
1144
                }
1145
                packPtr->padY = tmp;
1146
                packPtr->iPadY = 0;
1147
                index++;
1148
            } else if ((c == 'f') && (length > 1)
1149
                    && (strncmp(curOpt, "frame", length) == 0)) {
1150
                if (optionCount < (index+2)) {
1151
                    Tcl_AppendResult(interp, "wrong # args: \"frame\" ",
1152
                            "option must be followed by anchor point",
1153
                            (char *) NULL);
1154
                    goto error;
1155
                }
1156
                if (Tk_GetAnchor(interp, options[index+1],
1157
                        &packPtr->anchor) != TCL_OK) {
1158
                    goto error;
1159
                }
1160
                index++;
1161
            } else {
1162
                Tcl_AppendResult(interp, "bad option \"", curOpt,
1163
                        "\": should be top, bottom, left, right, ",
1164
                        "expand, fill, fillx, filly, padx, pady, or frame",
1165
                        (char *) NULL);
1166
                goto error;
1167
            }
1168
        }
1169
 
1170
        if (packPtr != prevPtr) {
1171
 
1172
            /*
1173
             * Unpack this window if it's currently packed.
1174
             */
1175
 
1176
            if (packPtr->masterPtr != NULL) {
1177
                if ((packPtr->masterPtr != masterPtr) &&
1178
                        (packPtr->masterPtr->tkwin
1179
                        != Tk_Parent(packPtr->tkwin))) {
1180
                    Tk_UnmaintainGeometry(packPtr->tkwin,
1181
                            packPtr->masterPtr->tkwin);
1182
                }
1183
                Unlink(packPtr);
1184
            }
1185
 
1186
            /*
1187
             * Add the window in the correct place in its parent's
1188
             * packing order, then make sure that the window is
1189
             * managed by us.
1190
             */
1191
 
1192
            packPtr->masterPtr = masterPtr;
1193
            if (prevPtr == NULL) {
1194
                packPtr->nextPtr = masterPtr->slavePtr;
1195
                masterPtr->slavePtr = packPtr;
1196
            } else {
1197
                packPtr->nextPtr = prevPtr->nextPtr;
1198
                prevPtr->nextPtr = packPtr;
1199
            }
1200
            Tk_ManageGeometry(tkwin, &packerType, (ClientData) packPtr);
1201
        }
1202
        ckfree((char *) options);
1203
    }
1204
 
1205
    /*
1206
     * Arrange for the parent to be re-packed at the first
1207
     * idle moment.
1208
     */
1209
 
1210
    if (masterPtr->abortPtr != NULL) {
1211
        *masterPtr->abortPtr = 1;
1212
    }
1213
    if (!(masterPtr->flags & REQUESTED_REPACK)) {
1214
        masterPtr->flags |= REQUESTED_REPACK;
1215
        Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
1216
    }
1217
    return TCL_OK;
1218
 
1219
    error:
1220
    ckfree((char *) options);
1221
    return TCL_ERROR;
1222
}
1223
 
1224
/*
1225
 *----------------------------------------------------------------------
1226
 *
1227
 * Unlink --
1228
 *
1229
 *      Remove a packer from its parent's list of slaves.
1230
 *
1231
 * Results:
1232
 *      None.
1233
 *
1234
 * Side effects:
1235
 *      The parent will be scheduled for repacking.
1236
 *
1237
 *----------------------------------------------------------------------
1238
 */
1239
 
1240
static void
1241
Unlink(packPtr)
1242
    register Packer *packPtr;           /* Window to unlink. */
1243
{
1244
    register Packer *masterPtr, *packPtr2;
1245
 
1246
    masterPtr = packPtr->masterPtr;
1247
    if (masterPtr == NULL) {
1248
        return;
1249
    }
1250
    if (masterPtr->slavePtr == packPtr) {
1251
        masterPtr->slavePtr = packPtr->nextPtr;
1252
    } else {
1253
        for (packPtr2 = masterPtr->slavePtr; ; packPtr2 = packPtr2->nextPtr) {
1254
            if (packPtr2 == NULL) {
1255
                panic("Unlink couldn't find previous window");
1256
            }
1257
            if (packPtr2->nextPtr == packPtr) {
1258
                packPtr2->nextPtr = packPtr->nextPtr;
1259
                break;
1260
            }
1261
        }
1262
    }
1263
    if (!(masterPtr->flags & REQUESTED_REPACK)) {
1264
        masterPtr->flags |= REQUESTED_REPACK;
1265
        Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
1266
    }
1267
    if (masterPtr->abortPtr != NULL) {
1268
        *masterPtr->abortPtr = 1;
1269
    }
1270
 
1271
    packPtr->masterPtr = NULL;
1272
}
1273
 
1274
/*
1275
 *----------------------------------------------------------------------
1276
 *
1277
 * DestroyPacker --
1278
 *
1279
 *      This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
1280
 *      to clean up the internal structure of a packer at a safe time
1281
 *      (when no-one is using it anymore).
1282
 *
1283
 * Results:
1284
 *      None.
1285
 *
1286
 * Side effects:
1287
 *      Everything associated with the packer is freed up.
1288
 *
1289
 *----------------------------------------------------------------------
1290
 */
1291
 
1292
static void
1293
DestroyPacker(memPtr)
1294
    char *memPtr;               /* Info about packed window that
1295
                                 * is now dead. */
1296
{
1297
    register Packer *packPtr = (Packer *) memPtr;
1298
    ckfree((char *) packPtr);
1299
}
1300
 
1301
/*
1302
 *----------------------------------------------------------------------
1303
 *
1304
 * PackStructureProc --
1305
 *
1306
 *      This procedure is invoked by the Tk event dispatcher in response
1307
 *      to StructureNotify events.
1308
 *
1309
 * Results:
1310
 *      None.
1311
 *
1312
 * Side effects:
1313
 *      If a window was just deleted, clean up all its packer-related
1314
 *      information.  If it was just resized, repack its slaves, if
1315
 *      any.
1316
 *
1317
 *----------------------------------------------------------------------
1318
 */
1319
 
1320
static void
1321
PackStructureProc(clientData, eventPtr)
1322
    ClientData clientData;              /* Our information about window
1323
                                         * referred to by eventPtr. */
1324
    XEvent *eventPtr;                   /* Describes what just happened. */
1325
{
1326
    register Packer *packPtr = (Packer *) clientData;
1327
    if (eventPtr->type == ConfigureNotify) {
1328
        if ((packPtr->slavePtr != NULL)
1329
                && !(packPtr->flags & REQUESTED_REPACK)) {
1330
            packPtr->flags |= REQUESTED_REPACK;
1331
            Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
1332
        }
1333
        if (packPtr->doubleBw != 2*Tk_Changes(packPtr->tkwin)->border_width) {
1334
            if ((packPtr->masterPtr != NULL)
1335
                    && !(packPtr->masterPtr->flags & REQUESTED_REPACK)) {
1336
                packPtr->doubleBw = 2*Tk_Changes(packPtr->tkwin)->border_width;
1337
                packPtr->masterPtr->flags |= REQUESTED_REPACK;
1338
                Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr->masterPtr);
1339
            }
1340
        }
1341
    } else if (eventPtr->type == DestroyNotify) {
1342
        register Packer *slavePtr, *nextPtr;
1343
 
1344
        if (packPtr->masterPtr != NULL) {
1345
            Unlink(packPtr);
1346
        }
1347
        for (slavePtr = packPtr->slavePtr; slavePtr != NULL;
1348
                slavePtr = nextPtr) {
1349
            Tk_ManageGeometry(slavePtr->tkwin, (Tk_GeomMgr *) NULL,
1350
                    (ClientData) NULL);
1351
            Tk_UnmapWindow(slavePtr->tkwin);
1352
            slavePtr->masterPtr = NULL;
1353
            nextPtr = slavePtr->nextPtr;
1354
            slavePtr->nextPtr = NULL;
1355
        }
1356
        Tcl_DeleteHashEntry(Tcl_FindHashEntry(&packerHashTable,
1357
                (char *) packPtr->tkwin));
1358
        if (packPtr->flags & REQUESTED_REPACK) {
1359
            Tcl_CancelIdleCall(ArrangePacking, (ClientData) packPtr);
1360
        }
1361
        packPtr->tkwin = NULL;
1362
        Tcl_EventuallyFree((ClientData) packPtr, DestroyPacker);
1363
    } else if (eventPtr->type == MapNotify) {
1364
        /*
1365
         * When a master gets mapped, must redo the geometry computation
1366
         * so that all of its slaves get remapped.
1367
         */
1368
 
1369
        if ((packPtr->slavePtr != NULL)
1370
                && !(packPtr->flags & REQUESTED_REPACK)) {
1371
            packPtr->flags |= REQUESTED_REPACK;
1372
            Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
1373
        }
1374
    } else if (eventPtr->type == UnmapNotify) {
1375
        Packer *packPtr2;
1376
 
1377
        /*
1378
         * Unmap all of the slaves when the master gets unmapped,
1379
         * so that they don't bother to keep redisplaying
1380
         * themselves.
1381
         */
1382
 
1383
        for (packPtr2 = packPtr->slavePtr; packPtr2 != NULL;
1384
                packPtr2 = packPtr2->nextPtr) {
1385
            Tk_UnmapWindow(packPtr2->tkwin);
1386
        }
1387
    }
1388
}
1389
 
1390
/*
1391
 *----------------------------------------------------------------------
1392
 *
1393
 * ConfigureSlaves --
1394
 *
1395
 *      This implements the guts of the "pack configure" command.  Given
1396
 *      a list of slaves and configuration options, it arranges for the
1397
 *      packer to manage the slaves and sets the specified options.
1398
 *
1399
 * Results:
1400
 *      TCL_OK is returned if all went well.  Otherwise, TCL_ERROR is
1401
 *      returned and interp->result is set to contain an error message.
1402
 *
1403
 * Side effects:
1404
 *      Slave windows get taken over by the packer.
1405
 *
1406
 *----------------------------------------------------------------------
1407
 */
1408
 
1409
static int
1410
ConfigureSlaves(interp, tkwin, argc, argv)
1411
    Tcl_Interp *interp;         /* Interpreter for error reporting. */
1412
    Tk_Window tkwin;            /* Any window in application containing
1413
                                 * slaves.  Used to look up slave names. */
1414
    int argc;                   /* Number of elements in argv. */
1415
    char *argv[];               /* Argument strings:  contains one or more
1416
                                 * window names followed by any number
1417
                                 * of "option value" pairs.  Caller must
1418
                                 * make sure that there is at least one
1419
                                 * window name. */
1420
{
1421
    Packer *masterPtr, *slavePtr, *prevPtr, *otherPtr;
1422
    Tk_Window other, slave, parent, ancestor;
1423
    int i, j, numWindows, c, tmp, positionGiven;
1424
    size_t length;
1425
 
1426
    /*
1427
     * Find out how many windows are specified.
1428
     */
1429
 
1430
    for (numWindows = 0; numWindows < argc; numWindows++) {
1431
        if (argv[numWindows][0] != '.') {
1432
            break;
1433
        }
1434
    }
1435
 
1436
    /*
1437
     * Iterate over all of the slave windows, parsing the configuration
1438
     * options for each slave.  It's a bit wasteful to re-parse the
1439
     * options for each slave, but things get too messy if we try to
1440
     * parse the arguments just once at the beginning.  For example,
1441
     * if a slave already is packed we want to just change a few
1442
     * existing values without resetting everything.  If there are
1443
     * multiple windows, the -after, -before, and -in options only
1444
     * get processed for the first window.
1445
     */
1446
 
1447
    masterPtr = NULL;
1448
    prevPtr = NULL;
1449
    positionGiven = 0;
1450
    for (j = 0; j < numWindows; j++) {
1451
        slave = Tk_NameToWindow(interp, argv[j], tkwin);
1452
        if (slave == NULL) {
1453
            return TCL_ERROR;
1454
        }
1455
        if (Tk_IsTopLevel(slave)) {
1456
            Tcl_AppendResult(interp, "can't pack \"", argv[j],
1457
                    "\": it's a top-level window", (char *) NULL);
1458
            return TCL_ERROR;
1459
        }
1460
        slavePtr = GetPacker(slave);
1461
        slavePtr->flags &= ~OLD_STYLE;
1462
 
1463
        /*
1464
         * If the slave isn't currently packed, reset all of its
1465
         * configuration information to default values (there could
1466
         * be old values left from a previous packing).
1467
         */
1468
 
1469
        if (slavePtr->masterPtr == NULL) {
1470
            slavePtr->side = TOP;
1471
            slavePtr->anchor = TK_ANCHOR_CENTER;
1472
            slavePtr->padX = slavePtr->padY = 0;
1473
            slavePtr->iPadX = slavePtr->iPadY = 0;
1474
            slavePtr->flags &= ~(FILLX|FILLY|EXPAND);
1475
        }
1476
 
1477
        for (i = numWindows; i < argc; i+=2) {
1478
            if ((i+2) > argc) {
1479
                Tcl_AppendResult(interp, "extra option \"", argv[i],
1480
                        "\" (option with no value?)", (char *) NULL);
1481
                return TCL_ERROR;
1482
            }
1483
            length = strlen(argv[i]);
1484
            if (length < 2) {
1485
                goto badOption;
1486
            }
1487
            c = argv[i][1];
1488
            if ((c == 'a') && (strncmp(argv[i], "-after", length) == 0)
1489
                    && (length >= 2)) {
1490
                if (j == 0) {
1491
                    other = Tk_NameToWindow(interp, argv[i+1], tkwin);
1492
                    if (other == NULL) {
1493
                        return TCL_ERROR;
1494
                    }
1495
                    prevPtr = GetPacker(other);
1496
                    if (prevPtr->masterPtr == NULL) {
1497
                        notPacked:
1498
                        Tcl_AppendResult(interp, "window \"", argv[i+1],
1499
                                "\" isn't packed", (char *) NULL);
1500
                        return TCL_ERROR;
1501
                    }
1502
                    masterPtr = prevPtr->masterPtr;
1503
                    positionGiven = 1;
1504
                }
1505
            } else if ((c == 'a') && (strncmp(argv[i], "-anchor", length) == 0)
1506
                    && (length >= 2)) {
1507
                if (Tk_GetAnchor(interp, argv[i+1], &slavePtr->anchor)
1508
                        != TCL_OK) {
1509
                    return TCL_ERROR;
1510
                }
1511
            } else if ((c == 'b')
1512
                    && (strncmp(argv[i], "-before", length) == 0)) {
1513
                if (j == 0) {
1514
                    other = Tk_NameToWindow(interp, argv[i+1], tkwin);
1515
                    if (other == NULL) {
1516
                        return TCL_ERROR;
1517
                    }
1518
                    otherPtr = GetPacker(other);
1519
                    if (otherPtr->masterPtr == NULL) {
1520
                        goto notPacked;
1521
                    }
1522
                    masterPtr = otherPtr->masterPtr;
1523
                    prevPtr = masterPtr->slavePtr;
1524
                    if (prevPtr == otherPtr) {
1525
                        prevPtr = NULL;
1526
                    } else {
1527
                        while (prevPtr->nextPtr != otherPtr) {
1528
                            prevPtr = prevPtr->nextPtr;
1529
                        }
1530
                    }
1531
                    positionGiven = 1;
1532
                }
1533
            } else if ((c == 'e')
1534
                    && (strncmp(argv[i], "-expand", length) == 0)) {
1535
                if (Tcl_GetBoolean(interp, argv[i+1], &tmp) != TCL_OK) {
1536
                    return TCL_ERROR;
1537
                }
1538
                slavePtr->flags &= ~EXPAND;
1539
                if (tmp) {
1540
                    slavePtr->flags |= EXPAND;
1541
                }
1542
            } else if ((c == 'f') && (strncmp(argv[i], "-fill", length) == 0)) {
1543
                if (strcmp(argv[i+1], "none") == 0) {
1544
                    slavePtr->flags &= ~(FILLX|FILLY);
1545
                } else if (strcmp(argv[i+1], "x") == 0) {
1546
                    slavePtr->flags = (slavePtr->flags & ~FILLY) | FILLX;
1547
                } else if (strcmp(argv[i+1], "y") == 0) {
1548
                    slavePtr->flags = (slavePtr->flags & ~FILLX) | FILLY;
1549
                } else if (strcmp(argv[i+1], "both") == 0) {
1550
                    slavePtr->flags |= FILLX|FILLY;
1551
                } else {
1552
                    Tcl_AppendResult(interp, "bad fill style \"", argv[i+1],
1553
                            "\": must be none, x, y, or both", (char *) NULL);
1554
                    return TCL_ERROR;
1555
                }
1556
            } else if ((c == 'i') && (strcmp(argv[i], "-in") == 0)) {
1557
                if (j == 0) {
1558
                    other = Tk_NameToWindow(interp, argv[i+1], tkwin);
1559
                    if (other == NULL) {
1560
                        return TCL_ERROR;
1561
                    }
1562
                    masterPtr = GetPacker(other);
1563
                    prevPtr = masterPtr->slavePtr;
1564
                    if (prevPtr != NULL) {
1565
                        while (prevPtr->nextPtr != NULL) {
1566
                            prevPtr = prevPtr->nextPtr;
1567
                        }
1568
                    }
1569
                    positionGiven = 1;
1570
                }
1571
            } else if ((c == 'i') && (strcmp(argv[i], "-ipadx") == 0)) {
1572
                if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
1573
                        || (tmp < 0)) {
1574
                    badPad:
1575
                    Tcl_ResetResult(interp);
1576
                    Tcl_AppendResult(interp, "bad pad value \"", argv[i+1],
1577
                            "\": must be positive screen distance",
1578
                            (char *) NULL);
1579
                    return TCL_ERROR;
1580
                }
1581
                slavePtr->iPadX = tmp*2;
1582
            } else if ((c == 'i') && (strcmp(argv[i], "-ipady") == 0)) {
1583
                if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
1584
                        || (tmp< 0)) {
1585
                    goto badPad;
1586
                }
1587
                slavePtr->iPadY = tmp*2;
1588
            } else if ((c == 'p') && (strcmp(argv[i], "-padx") == 0)) {
1589
                if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
1590
                        || (tmp< 0)) {
1591
                    goto badPad;
1592
                }
1593
                slavePtr->padX = tmp*2;
1594
            } else if ((c == 'p') && (strcmp(argv[i], "-pady") == 0)) {
1595
                if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
1596
                        || (tmp< 0)) {
1597
                    goto badPad;
1598
                }
1599
                slavePtr->padY = tmp*2;
1600
            } else if ((c == 's') && (strncmp(argv[i], "-side", length) == 0)) {
1601
                c = argv[i+1][0];
1602
                if ((c == 't') && (strcmp(argv[i+1], "top") == 0)) {
1603
                    slavePtr->side = TOP;
1604
                } else if ((c == 'b') && (strcmp(argv[i+1], "bottom") == 0)) {
1605
                    slavePtr->side = BOTTOM;
1606
                } else if ((c == 'l') && (strcmp(argv[i+1], "left") == 0)) {
1607
                    slavePtr->side = LEFT;
1608
                } else if ((c == 'r') && (strcmp(argv[i+1], "right") == 0)) {
1609
                    slavePtr->side = RIGHT;
1610
                } else {
1611
                    Tcl_AppendResult(interp, "bad side \"", argv[i+1],
1612
                            "\": must be top, bottom, left, or right",
1613
                            (char *) NULL);
1614
                    return TCL_ERROR;
1615
                }
1616
            } else {
1617
                badOption:
1618
                Tcl_AppendResult(interp, "unknown or ambiguous option \"",
1619
                        argv[i], "\": must be -after, -anchor, -before, ",
1620
                        "-expand, -fill, -in, -ipadx, -ipady, -padx, ",
1621
                        "-pady, or -side", (char *) NULL);
1622
                return TCL_ERROR;
1623
            }
1624
        }
1625
 
1626
        /*
1627
         * If no position in a packing list was specified and the slave
1628
         * is already packed, then leave it in its current location in
1629
         * its current packing list.
1630
         */
1631
 
1632
        if (!positionGiven && (slavePtr->masterPtr != NULL)) {
1633
            masterPtr = slavePtr->masterPtr;
1634
            goto scheduleLayout;
1635
        }
1636
 
1637
        /*
1638
         * If the slave is going to be put back after itself then
1639
         * skip the whole operation, since it won't work anyway.
1640
         */
1641
 
1642
        if (prevPtr == slavePtr) {
1643
            masterPtr = slavePtr->masterPtr;
1644
            goto scheduleLayout;
1645
        }
1646
 
1647
        /*
1648
         * If none of the "-in", "-before", or "-after" options has
1649
         * been specified, arrange for the slave to go at the end of
1650
         * the order for its parent.
1651
         */
1652
 
1653
        if (!positionGiven) {
1654
            masterPtr = GetPacker(Tk_Parent(slave));
1655
            prevPtr = masterPtr->slavePtr;
1656
            if (prevPtr != NULL) {
1657
                while (prevPtr->nextPtr != NULL) {
1658
                    prevPtr = prevPtr->nextPtr;
1659
                }
1660
            }
1661
        }
1662
 
1663
        /*
1664
         * Make sure that the slave's parent is either the master or
1665
         * an ancestor of the master, and that the master and slave
1666
         * aren't the same.
1667
         */
1668
 
1669
        parent = Tk_Parent(slave);
1670
        for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
1671
            if (ancestor == parent) {
1672
                break;
1673
            }
1674
            if (Tk_IsTopLevel(ancestor)) {
1675
                Tcl_AppendResult(interp, "can't pack ", argv[j],
1676
                        " inside ", Tk_PathName(masterPtr->tkwin),
1677
                        (char *) NULL);
1678
                return TCL_ERROR;
1679
            }
1680
        }
1681
        if (slave == masterPtr->tkwin) {
1682
            Tcl_AppendResult(interp, "can't pack ", argv[j],
1683
                    " inside itself", (char *) NULL);
1684
            return TCL_ERROR;
1685
        }
1686
 
1687
        /*
1688
         * Unpack the slave if it's currently packed, then position it
1689
         * after prevPtr.
1690
         */
1691
 
1692
        if (slavePtr->masterPtr != NULL) {
1693
            if ((slavePtr->masterPtr != masterPtr) &&
1694
                    (slavePtr->masterPtr->tkwin
1695
                    != Tk_Parent(slavePtr->tkwin))) {
1696
                Tk_UnmaintainGeometry(slavePtr->tkwin,
1697
                        slavePtr->masterPtr->tkwin);
1698
            }
1699
            Unlink(slavePtr);
1700
        }
1701
        slavePtr->masterPtr = masterPtr;
1702
        if (prevPtr == NULL) {
1703
            slavePtr->nextPtr = masterPtr->slavePtr;
1704
            masterPtr->slavePtr = slavePtr;
1705
        } else {
1706
            slavePtr->nextPtr = prevPtr->nextPtr;
1707
            prevPtr->nextPtr = slavePtr;
1708
        }
1709
        Tk_ManageGeometry(slave, &packerType, (ClientData) slavePtr);
1710
        prevPtr = slavePtr;
1711
 
1712
        /*
1713
         * Arrange for the parent to be re-packed at the first
1714
         * idle moment.
1715
         */
1716
 
1717
        scheduleLayout:
1718
        if (masterPtr->abortPtr != NULL) {
1719
            *masterPtr->abortPtr = 1;
1720
        }
1721
        if (!(masterPtr->flags & REQUESTED_REPACK)) {
1722
            masterPtr->flags |= REQUESTED_REPACK;
1723
            Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
1724
        }
1725
    }
1726
    return TCL_OK;
1727
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.