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

Subversion Repositories or1k

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

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

Line No. Rev Author Line
1 578 markom
/*
2
 * tkGrid.c --
3
 *
4
 *      Grid based geometry manager.
5
 *
6
 * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
7
 *
8
 * See the file "license.terms" for information on usage and redistribution
9
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10
 *
11
 * RCS: @(#) $Id: tkGrid.c,v 1.1.1.1 2002-01-16 10:25:52 markom Exp $
12
 */
13
 
14
#include "tkInt.h"
15
 
16
/*
17
 * Convenience Macros
18
 */
19
 
20
#ifdef MAX
21
#   undef MAX
22
#endif
23
#define MAX(x,y)        ((x) > (y) ? (x) : (y))
24
#ifdef MIN
25
#   undef MIN
26
#endif
27
#define MIN(x,y)        ((x) > (y) ? (y) : (x))
28
 
29
#define COLUMN  (1)             /* working on column offsets */
30
#define ROW     (2)             /* working on row offsets */
31
 
32
#define CHECK_ONLY      (1)     /* check max slot constraint */
33
#define CHECK_SPACE     (2)     /* alloc more space, don't change max */
34
 
35
/*
36
 * Pre-allocate enough row and column slots for "typical" sized tables
37
 * this value should be chosen so by the time the extra malloc's are
38
 * required, the layout calculations overwehlm them. [A "slot" contains
39
 * information for either a row or column, depending upon the context.]
40
 */
41
 
42
#define TYPICAL_SIZE    25  /* (arbitrary guess) */
43
#define PREALLOC        10  /* extra slots to allocate */
44
 
45
/*
46
 * Data structures are allocated dynamically to support arbitrary sized tables.
47
 * However, the space is proportional to the highest numbered slot with
48
 * some non-default property.  This limit is used to head off mistakes and
49
 * denial of service attacks by limiting the amount of storage required.
50
 */
51
 
52
#define MAX_ELEMENT     10000
53
 
54
/*
55
 * Special characters to support relative layouts.
56
 */
57
 
58
#define REL_SKIP        'x'     /* Skip this column. */
59
#define REL_HORIZ       '-'     /* Extend previous widget horizontally. */
60
#define REL_VERT        '^'     /* Extend widget from row above. */
61
 
62
/*
63
 *  Structure to hold information for grid masters.  A slot is either
64
 *  a row or column.
65
 */
66
 
67
typedef struct SlotInfo {
68
        int minSize;            /* The minimum size of this slot (in pixels).
69
                                 * It is set via the rowconfigure or
70
                                 * columnconfigure commands. */
71
        int weight;             /* The resize weight of this slot. (0) means
72
                                 * this slot doesn't resize. Extra space in
73
                                 * the layout is given distributed among slots
74
                                 * inproportion to their weights. */
75
        int pad;                /* Extra padding, in pixels, required for
76
                                 * this slot.  This amount is "added" to the
77
                                 * largest slave in the slot. */
78
        int offset;             /* This is a cached value used for
79
                                 * introspection.  It is the pixel
80
                                 * offset of the right or bottom edge
81
                                 * of this slot from the beginning of the
82
                                 * layout. */
83
        int temp;               /* This is a temporary value used for
84
                                 * calculating adjusted weights when
85
                                 * shrinking the layout below its
86
                                 * nominal size. */
87
} SlotInfo;
88
 
89
/*
90
 * Structure to hold information during layout calculations.  There
91
 * is one of these for each slot, an array for each of the rows or columns.
92
 */
93
 
94
typedef struct GridLayout {
95
    struct Gridder *binNextPtr; /* The next slave window in this bin.
96
                                 * Each bin contains a list of all
97
                                 * slaves whose spans are >1 and whose
98
                                 * right edges fall in this slot. */
99
    int minSize;                /* Minimum size needed for this slot,
100
                                 * in pixels.  This is the space required
101
                                 * to hold any slaves contained entirely
102
                                 * in this slot, adjusted for any slot
103
                                 * constrants, such as size or padding. */
104
    int pad;                    /* Padding needed for this slot */
105
    int weight;                 /* Slot weight, controls resizing. */
106
    int minOffset;              /* The minimum offset, in pixels, from
107
                                 * the beginning of the layout to the
108
                                 * right/bottom edge of the slot calculated
109
                                 * from top/left to bottom/right. */
110
    int maxOffset;              /* The maximum offset, in pixels, from
111
                                 * the beginning of the layout to the
112
                                 * right-or-bottom edge of the slot calculated
113
                                 * from bottom-or-right to top-or-left. */
114
} GridLayout;
115
 
116
/*
117
 * Keep one of these for each geometry master.
118
 */
119
 
120
typedef struct {
121
    SlotInfo *columnPtr;        /* Pointer to array of column constraints. */
122
    SlotInfo *rowPtr;           /* Pointer to array of row constraints. */
123
    int columnEnd;              /* The last column occupied by any slave. */
124
    int columnMax;              /* The number of columns with constraints. */
125
    int columnSpace;            /* The number of slots currently allocated for
126
                                 * column constraints. */
127
    int rowEnd;                 /* The last row occupied by any slave. */
128
    int rowMax;                 /* The number of rows with constraints. */
129
    int rowSpace;               /* The number of slots currently allocated
130
                                 * for row constraints. */
131
    int startX;                 /* Pixel offset of this layout within its
132
                                 * parent. */
133
    int startY;                 /* Pixel offset of this layout within its
134
                                 * parent. */
135
} GridMaster;
136
 
137
/*
138
 * For each window that the grid cares about (either because
139
 * the window is managed by the grid or because the window
140
 * has slaves that are managed by the grid), there is a
141
 * structure of the following type:
142
 */
143
 
144
typedef struct Gridder {
145
    Tk_Window tkwin;            /* Tk token for window.  NULL means that
146
                                 * the window has been deleted, but the
147
                                 * gridder hasn't had a chance to clean up
148
                                 * yet because the structure is still in
149
                                 * use. */
150
    struct Gridder *masterPtr;  /* Master window within which this window
151
                                 * is managed (NULL means this window
152
                                 * isn't managed by the gridder). */
153
    struct Gridder *nextPtr;    /* Next window managed within same
154
                                 * parent.  List order doesn't matter. */
155
    struct Gridder *slavePtr;   /* First in list of slaves managed
156
                                 * inside this window (NULL means
157
                                 * no grid slaves). */
158
    GridMaster *masterDataPtr;  /* Additional data for geometry master. */
159
    int column, row;            /* Location in the grid (starting
160
                                 * from zero). */
161
    int numCols, numRows;       /* Number of columns or rows this slave spans.
162
                                 * Should be at least 1. */
163
    int padX, padY;             /* Total additional pixels to leave around the
164
                                 * window (half of this space is left on each
165
                                 * side).  This is space *outside* the window:
166
                                 * we'll allocate extra space in frame but
167
                                 * won't enlarge window). */
168
    int iPadX, iPadY;           /* Total extra pixels to allocate inside the
169
                                 * window (half this amount will appear on
170
                                 * each side). */
171
    int sticky;                 /* which sides of its cavity this window
172
                                 * sticks to. See below for definitions */
173
    int doubleBw;               /* Twice the window's last known border
174
                                 * width.  If this changes, the window
175
                                 * must be re-arranged within its parent. */
176
    int *abortPtr;              /* If non-NULL, it means that there is a nested
177
                                 * call to ArrangeGrid already working on
178
                                 * this window.  *abortPtr may be set to 1 to
179
                                 * abort that nested call.  This happens, for
180
                                 * example, if tkwin or any of its slaves
181
                                 * is deleted. */
182
    int flags;                  /* Miscellaneous flags;  see below
183
                                 * for definitions. */
184
 
185
    /*
186
     * These fields are used temporarily for layout calculations only.
187
     */
188
 
189
    struct Gridder *binNextPtr; /* Link to next span>1 slave in this bin. */
190
    int size;                   /* Nominal size (width or height) in pixels
191
                                 * of the slave.  This includes the padding. */
192
} Gridder;
193
 
194
/* Flag values for "sticky"ness  The 16 combinations subsume the packer's
195
 * notion of anchor and fill.
196
 *
197
 * STICK_NORTH          This window sticks to the top of its cavity.
198
 * STICK_EAST           This window sticks to the right edge of its cavity.
199
 * STICK_SOUTH          This window sticks to the bottom of its cavity.
200
 * STICK_WEST           This window sticks to the left edge of its cavity.
201
 */
202
 
203
#define STICK_NORTH             1
204
#define STICK_EAST              2
205
#define STICK_SOUTH             4
206
#define STICK_WEST              8
207
 
208
/*
209
 * Flag values for Grid structures:
210
 *
211
 * REQUESTED_RELAYOUT:          1 means a Tcl_DoWhenIdle request
212
 *                              has already been made to re-arrange
213
 *                              all the slaves of this window.
214
 *
215
 * DONT_PROPAGATE:              1 means don't set this window's requested
216
 *                              size.  0 means if this window is a master
217
 *                              then Tk will set its requested size to fit
218
 *                              the needs of its slaves.
219
 */
220
 
221
#define REQUESTED_RELAYOUT      1
222
#define DONT_PROPAGATE          2
223
 
224
/*
225
 * Hash table used to map from Tk_Window tokens to corresponding
226
 * Grid structures:
227
 */
228
 
229
static Tcl_HashTable gridHashTable;
230
static int initialized = 0;
231
 
232
/*
233
 * Prototypes for procedures used only in this file:
234
 */
235
 
236
static void     AdjustForSticky _ANSI_ARGS_((Gridder *slavePtr, int *xPtr,
237
                    int *yPtr, int *widthPtr, int *heightPtr));
238
static int      AdjustOffsets _ANSI_ARGS_((int width,
239
                        int elements, SlotInfo *slotPtr));
240
static void     ArrangeGrid _ANSI_ARGS_((ClientData clientData));
241
static int      CheckSlotData _ANSI_ARGS_((Gridder *masterPtr, int slot,
242
                        int slotType, int checkOnly));
243
static int      ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
244
                        Tk_Window tkwin, int argc, char *argv[]));
245
static void     DestroyGrid _ANSI_ARGS_((char *memPtr));
246
static Gridder *GetGrid _ANSI_ARGS_((Tk_Window tkwin));
247
static void     GridStructureProc _ANSI_ARGS_((
248
                        ClientData clientData, XEvent *eventPtr));
249
static void     GridLostSlaveProc _ANSI_ARGS_((ClientData clientData,
250
                        Tk_Window tkwin));
251
static void     GridReqProc _ANSI_ARGS_((ClientData clientData,
252
                        Tk_Window tkwin));
253
static void     InitMasterData _ANSI_ARGS_((Gridder *masterPtr));
254
static int      ResolveConstraints _ANSI_ARGS_((Gridder *gridPtr,
255
                        int rowOrColumn, int maxOffset));
256
static void     SetGridSize _ANSI_ARGS_((Gridder *gridPtr));
257
static void     StickyToString _ANSI_ARGS_((int flags, char *result));
258
static int      StringToSticky _ANSI_ARGS_((char *string));
259
static void     Unlink _ANSI_ARGS_((Gridder *gridPtr));
260
 
261
static Tk_GeomMgr gridMgrType = {
262
    "grid",                     /* name */
263
    GridReqProc,                /* requestProc */
264
    GridLostSlaveProc,          /* lostSlaveProc */
265
};
266
 
267
/*
268
 *--------------------------------------------------------------
269
 *
270
 * Tk_GridCmd --
271
 *
272
 *      This procedure is invoked to process the "grid" Tcl command.
273
 *      See the user documentation for details on what it does.
274
 *
275
 * Results:
276
 *      A standard Tcl result.
277
 *
278
 * Side effects:
279
 *      See the user documentation.
280
 *
281
 *--------------------------------------------------------------
282
 */
283
 
284
int
285
Tk_GridCmd(clientData, interp, argc, argv)
286
    ClientData clientData;      /* Main window associated with
287
                                 * interpreter. */
288
    Tcl_Interp *interp;         /* Current interpreter. */
289
    int argc;                   /* Number of arguments. */
290
    char **argv;                /* Argument strings. */
291
{
292
    Tk_Window tkwin = (Tk_Window) clientData;
293
    Gridder *masterPtr;         /* master grid record */
294
    GridMaster *gridPtr;        /* pointer to grid data */
295
    size_t length;              /* streing length of argument */
296
    char c;                     /* 1st character of argument */
297
 
298
    if ((argc >= 2) && ((argv[1][0] == '.') || (argv[1][0] == REL_SKIP) ||
299
                (argv[1][0] == REL_VERT))) {
300
        return ConfigureSlaves(interp, tkwin, argc-1, argv+1);
301
    }
302
    if (argc < 3) {
303
        Tcl_AppendResult(interp, "wrong # args: should be \"",
304
                argv[0], " option arg ?arg ...?\"", (char *) NULL);
305
        return TCL_ERROR;
306
    }
307
    c = argv[1][0];
308
    length = strlen(argv[1]);
309
 
310
    if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
311
        Tk_Window master;
312
        int row, column;        /* origin for bounding box */
313
        int row2, column2;      /* end of bounding box */
314
        int endX, endY;         /* last column/row in the layout */
315
        int x=0, y=0;             /* starting pixels for this bounding box */
316
        int width, height;      /* size of the bounding box */
317
 
318
        if (argc!=3 && argc != 5 && argc != 7) {
319
            Tcl_AppendResult(interp, "wrong number of arguments: ",
320
                    "must be \"",argv[0],
321
                    " bbox master ?column row ?column row??\"",
322
                    (char *) NULL);
323
            return TCL_ERROR;
324
        }
325
 
326
        master = Tk_NameToWindow(interp, argv[2], tkwin);
327
        if (master == NULL) {
328
            return TCL_ERROR;
329
        }
330
        masterPtr = GetGrid(master);
331
 
332
        if (argc >= 5) {
333
            if (Tcl_GetInt(interp, argv[3], &column) != TCL_OK) {
334
                return TCL_ERROR;
335
            }
336
            if (Tcl_GetInt(interp, argv[4], &row) != TCL_OK) {
337
                return TCL_ERROR;
338
            }
339
            column2 = column;
340
            row2 = row;
341
        }
342
 
343
        if (argc == 7) {
344
            if (Tcl_GetInt(interp, argv[5], &column2) != TCL_OK) {
345
                return TCL_ERROR;
346
            }
347
            if (Tcl_GetInt(interp, argv[6], &row2) != TCL_OK) {
348
                return TCL_ERROR;
349
            }
350
        }
351
 
352
        gridPtr = masterPtr->masterDataPtr;
353
        if (gridPtr == NULL) {
354
            sprintf(interp->result, "%d %d %d %d",0,0,0,0);
355
            return(TCL_OK);
356
        }
357
 
358
        SetGridSize(masterPtr);
359
        endX = MAX(gridPtr->columnEnd, gridPtr->columnMax);
360
        endY = MAX(gridPtr->rowEnd, gridPtr->rowMax);
361
 
362
        if ((endX == 0) || (endY == 0)) {
363
            sprintf(interp->result, "%d %d %d %d",0,0,0,0);
364
            return(TCL_OK);
365
        }
366
        if (argc == 3) {
367
            row = column = 0;
368
            row2 = endY;
369
            column2 = endX;
370
        }
371
 
372
        if (column > column2) {
373
            int temp = column;
374
            column = column2, column2 = temp;
375
        }
376
        if (row > row2) {
377
            int temp = row;
378
            row = row2, row2 = temp;
379
        }
380
 
381
        if (column > 0 && column < endX) {
382
            x = gridPtr->columnPtr[column-1].offset;
383
        } else if  (column > 0) {
384
            x = gridPtr->columnPtr[endX-1].offset;
385
        }
386
 
387
        if (row > 0 && row < endY) {
388
            y = gridPtr->rowPtr[row-1].offset;
389
        } else if (row > 0) {
390
            y = gridPtr->rowPtr[endY-1].offset;
391
        }
392
 
393
        if (column2 < 0) {
394
            width = 0;
395
        } else if (column2 >= endX) {
396
            width = gridPtr->columnPtr[endX-1].offset - x;
397
        } else {
398
            width = gridPtr->columnPtr[column2].offset - x;
399
        }
400
 
401
        if (row2 < 0) {
402
            height = 0;
403
        } else if (row2 >= endY) {
404
            height = gridPtr->rowPtr[endY-1].offset - y;
405
        } else {
406
            height = gridPtr->rowPtr[row2].offset - y;
407
        }
408
 
409
        sprintf(interp->result, "%d %d %d %d",
410
                x + gridPtr->startX, y + gridPtr->startY, width, height);
411
    } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
412
        if (argv[2][0] != '.') {
413
            Tcl_AppendResult(interp, "bad argument \"", argv[2],
414
                    "\": must be name of window", (char *) NULL);
415
            return TCL_ERROR;
416
        }
417
        return ConfigureSlaves(interp, tkwin, argc-2, argv+2);
418
    } else if (((c == 'f') && (strncmp(argv[1], "forget", length) == 0))  ||
419
            ((c == 'r') && (strncmp(argv[1], "remove", length) == 0))) {
420
        Tk_Window slave;
421
        Gridder *slavePtr;
422
        int i;
423
 
424
        for (i = 2; i < argc; i++) {
425
            slave = Tk_NameToWindow(interp, argv[i], tkwin);
426
            if (slave == NULL) {
427
                return TCL_ERROR;
428
            }
429
            slavePtr = GetGrid(slave);
430
            if (slavePtr->masterPtr != NULL) {
431
 
432
                /*
433
                 * For "forget", reset all the settings to their defaults
434
                 */
435
 
436
                if (c == 'f') {
437
                    slavePtr->column = slavePtr->row = -1;
438
                    slavePtr->numCols = 1;
439
                    slavePtr->numRows = 1;
440
                    slavePtr->padX = slavePtr->padY = 0;
441
                    slavePtr->iPadX = slavePtr->iPadY = 0;
442
                    slavePtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
443
                    slavePtr->flags = 0;
444
                    slavePtr->sticky = 0;
445
                }
446
                Tk_ManageGeometry(slave, (Tk_GeomMgr *) NULL,
447
                        (ClientData) NULL);
448
                if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
449
                    Tk_UnmaintainGeometry(slavePtr->tkwin,
450
                            slavePtr->masterPtr->tkwin);
451
                }
452
                Unlink(slavePtr);
453
                Tk_UnmapWindow(slavePtr->tkwin);
454
            }
455
        }
456
    } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
457
        register Gridder *slavePtr;
458
        Tk_Window slave;
459
        char buffer[70];
460
 
461
        if (argc != 3) {
462
            Tcl_AppendResult(interp, "wrong # args: should be \"",
463
                    argv[0], " info window\"", (char *) NULL);
464
            return TCL_ERROR;
465
        }
466
        slave = Tk_NameToWindow(interp, argv[2], tkwin);
467
        if (slave == NULL) {
468
            return TCL_ERROR;
469
        }
470
        slavePtr = GetGrid(slave);
471
        if (slavePtr->masterPtr == NULL) {
472
            interp->result[0] = '\0';
473
            return TCL_OK;
474
        }
475
 
476
        Tcl_AppendElement(interp, "-in");
477
        Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin));
478
        sprintf(buffer, " -column %d -row %d -columnspan %d -rowspan %d",
479
                slavePtr->column, slavePtr->row,
480
                slavePtr->numCols, slavePtr->numRows);
481
        Tcl_AppendResult(interp, buffer, (char *) NULL);
482
        sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
483
                slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
484
                slavePtr->padY/2);
485
        Tcl_AppendResult(interp, buffer, (char *) NULL);
486
        StickyToString(slavePtr->sticky,buffer);
487
        Tcl_AppendResult(interp, " -sticky ", buffer, (char *) NULL);
488
    } else if((c == 'l') && (strncmp(argv[1], "location", length) == 0)) {
489
        Tk_Window master;
490
        register SlotInfo *slotPtr;
491
        int x, y;               /* Offset in pixels, from edge of parent. */
492
        int i, j;               /* Corresponding column and row indeces. */
493
        int endX, endY;         /* end of grid */
494
 
495
        if (argc != 5) {
496
            Tcl_AppendResult(interp, "wrong # args: should be \"",
497
                    argv[0], " location master x y\"", (char *)NULL);
498
            return TCL_ERROR;
499
        }
500
 
501
        master = Tk_NameToWindow(interp, argv[2], tkwin);
502
        if (master == NULL) {
503
            return TCL_ERROR;
504
        }
505
 
506
        if (Tk_GetPixels(interp, master, argv[3], &x) != TCL_OK) {
507
            return TCL_ERROR;
508
        }
509
        if (Tk_GetPixels(interp, master, argv[4], &y) != TCL_OK) {
510
            return TCL_ERROR;
511
        }
512
 
513
        masterPtr = GetGrid(master);
514
        if (masterPtr->masterDataPtr == NULL) {
515
            sprintf(interp->result, "%d %d", -1, -1);
516
            return TCL_OK;
517
        }
518
        gridPtr = masterPtr->masterDataPtr;
519
 
520
        /*
521
         * Update any pending requests.  This is not always the
522
         * steady state value, as more configure events could be in
523
         * the pipeline, but its as close as its easy to get.
524
         */
525
 
526
        while (masterPtr->flags & REQUESTED_RELAYOUT) {
527
            Tk_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
528
            ArrangeGrid ((ClientData) masterPtr);
529
        }
530
        SetGridSize(masterPtr);
531
        endX = MAX(gridPtr->columnEnd, gridPtr->columnMax);
532
        endY = MAX(gridPtr->rowEnd, gridPtr->rowMax);
533
 
534
        slotPtr  = masterPtr->masterDataPtr->columnPtr;
535
        if (x < masterPtr->masterDataPtr->startX) {
536
            i = -1;
537
        } else {
538
            x -= masterPtr->masterDataPtr->startX;
539
            for (i=0;slotPtr[i].offset < x && i < endX; i++) {
540
                /* null body */
541
            }
542
        }
543
 
544
        slotPtr  = masterPtr->masterDataPtr->rowPtr;
545
        if (y < masterPtr->masterDataPtr->startY) {
546
            j = -1;
547
        } else {
548
            y -= masterPtr->masterDataPtr->startY;
549
            for (j=0;slotPtr[j].offset < y && j < endY; j++) {
550
                /* null body */
551
            }
552
        }
553
 
554
        sprintf(interp->result, "%d %d", i, j);
555
    } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
556
        Tk_Window master;
557
        int propagate;
558
 
559
        if (argc > 4) {
560
            Tcl_AppendResult(interp, "wrong # args: should be \"",
561
                    argv[0], " propagate window ?boolean?\"",
562
                    (char *) NULL);
563
            return TCL_ERROR;
564
        }
565
        master = Tk_NameToWindow(interp, argv[2], tkwin);
566
        if (master == NULL) {
567
            return TCL_ERROR;
568
        }
569
        masterPtr = GetGrid(master);
570
        if (argc == 3) {
571
            interp->result = (masterPtr->flags & DONT_PROPAGATE) ? "0" : "1";
572
            return TCL_OK;
573
        }
574
        if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
575
            return TCL_ERROR;
576
        }
577
        if ((!propagate) ^ (masterPtr->flags&DONT_PROPAGATE)) {
578
            masterPtr->flags  ^= DONT_PROPAGATE;
579
 
580
            /*
581
             * Re-arrange the master to allow new geometry information to
582
             * propagate upwards to the master's master.
583
             */
584
 
585
            if (masterPtr->abortPtr != NULL) {
586
                *masterPtr->abortPtr = 1;
587
            }
588
            if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
589
                masterPtr->flags |= REQUESTED_RELAYOUT;
590
                Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
591
            }
592
        }
593
    } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)
594
            && (length > 1)) {
595
        Tk_Window master;
596
 
597
        if (argc != 3) {
598
            Tcl_AppendResult(interp, "wrong # args: should be \"",
599
                    argv[0], " size window\"", (char *) NULL);
600
            return TCL_ERROR;
601
        }
602
        master = Tk_NameToWindow(interp, argv[2], tkwin);
603
        if (master == NULL) {
604
            return TCL_ERROR;
605
        }
606
        masterPtr = GetGrid(master);
607
 
608
        if (masterPtr->masterDataPtr != NULL) {
609
            SetGridSize(masterPtr);
610
            gridPtr = masterPtr->masterDataPtr;
611
            sprintf(interp->result, "%d %d",
612
                MAX(gridPtr->columnEnd, gridPtr->columnMax),
613
                MAX(gridPtr->rowEnd, gridPtr->rowMax));
614
        } else {
615
            sprintf(interp->result, "%d %d",0, 0);
616
        }
617
    } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)
618
            && (length > 1)) {
619
        Tk_Window master;
620
        Gridder *slavePtr;
621
        int i, value;
622
        int row = -1, column = -1;
623
 
624
        if ((argc < 3) || ((argc%2) == 0)) {
625
            Tcl_AppendResult(interp, "wrong # args: should be \"",
626
                    argv[0], " slaves window ?-option value...?\"",
627
                    (char *) NULL);
628
            return TCL_ERROR;
629
        }
630
 
631
        for (i=3; i<argc; i+=2) {
632
            length = strlen(argv[i]);
633
            if ((*argv[i] != '-') || (length < 2)) {
634
                Tcl_AppendResult(interp, "invalid args: should be \"",
635
                        argv[0], " slaves window ?-option value...?\"",
636
                        (char *) NULL);
637
                return TCL_ERROR;
638
            }
639
            if (Tcl_GetInt(interp, argv[i+1], &value) != TCL_OK) {
640
                return TCL_ERROR;
641
            }
642
            if (value < 0) {
643
                Tcl_AppendResult(interp, argv[i],
644
                        " is an invalid value: should NOT be < 0",
645
                        (char *) NULL);
646
                return TCL_ERROR;
647
            }
648
            if (strncmp(argv[i], "-column", length) == 0) {
649
                column = value;
650
            } else if (strncmp(argv[i], "-row", length) == 0) {
651
                row = value;
652
            } else {
653
                Tcl_AppendResult(interp, argv[i],
654
                        " is an invalid option: should be \"",
655
                        "-row, -column\"",
656
                        (char *) NULL);
657
                return TCL_ERROR;
658
            }
659
        }
660
        master = Tk_NameToWindow(interp, argv[2], tkwin);
661
        if (master == NULL) {
662
            return TCL_ERROR;
663
        }
664
        masterPtr = GetGrid(master);
665
 
666
        for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
667
                                             slavePtr = slavePtr->nextPtr) {
668
            if (column>=0 && (slavePtr->column > column
669
                    || slavePtr->column+slavePtr->numCols-1 < column)) {
670
                continue;
671
            }
672
            if (row>=0 && (slavePtr->row > row ||
673
                    slavePtr->row+slavePtr->numRows-1 < row)) {
674
                continue;
675
            }
676
            Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin));
677
        }
678
 
679
    /*
680
     * Sample argument combinations:
681
     *  grid columnconfigure <master> <index> -option
682
     *  grid columnconfigure <master> <index> -option value -option value
683
     *  grid rowconfigure <master> <index>
684
     *  grid rowconfigure <master> <index> -option
685
     *  grid rowconfigure <master> <index> -option value -option value.
686
     */
687
 
688
    } else if(((c == 'c') && (strncmp(argv[1], "columnconfigure", length) == 0)
689
            && (length >= 3)) ||
690
            ((c == 'r') && (strncmp(argv[1], "rowconfigure", length) == 0)
691
            && (length >=2))) {
692
        Tk_Window master;
693
        SlotInfo *slotPtr = NULL;
694
        int slot;               /* the column or row number */
695
        size_t length;          /* the # of chars in the "-option" string */
696
        int slotType;           /* COLUMN or ROW */
697
        int size;               /* the configuration value */
698
        int checkOnly;          /* check the size only */
699
        int argcPtr;            /* Number of items in index list */
700
        char **argvPtr;         /* array of indeces */
701
        char **indexP;          /* String value of current index list item. */
702
        int ok;                 /* temporary TCL result code */
703
        int i;
704
 
705
        if (((argc%2 != 0) && (argc>6)) || (argc < 4)) {
706
            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
707
                    " ", argv[1], " master index ?-option value...?\"",
708
                    (char *)NULL);
709
            return TCL_ERROR;
710
        }
711
 
712
        master = Tk_NameToWindow(interp, argv[2], tkwin);
713
        if (master == NULL) {
714
            return TCL_ERROR;
715
        }
716
 
717
        if (Tcl_SplitList(interp, argv[3], &argcPtr, &argvPtr) != TCL_OK) {
718
            return TCL_ERROR;
719
        }
720
 
721
        checkOnly = ((argc == 4) || (argc == 5));
722
        masterPtr = GetGrid(master);
723
        slotType = (c == 'c') ? COLUMN : ROW;
724
        if (checkOnly && argcPtr > 1) {
725
            Tcl_AppendResult(interp, argv[3],
726
                    " must be a single element.", (char *) NULL);
727
            Tcl_Free((char *)argvPtr);
728
            return TCL_ERROR;
729
        }
730
        for (indexP=argvPtr; *indexP != NULL; indexP++) {
731
            if (Tcl_GetInt(interp, *indexP, &slot) != TCL_OK) {
732
                Tcl_Free((char *)argvPtr);
733
                return TCL_ERROR;
734
            }
735
            ok = CheckSlotData(masterPtr, slot, slotType, checkOnly);
736
            if ((ok!=TCL_OK) && ((argc<4) || (argc>5))) {
737
                Tcl_AppendResult(interp, argv[0],
738
                        " ", argv[1], ": \"", *argvPtr,"\" is out of range",
739
                        (char *) NULL);
740
                Tcl_Free((char *)argvPtr);
741
                return TCL_ERROR;
742
            } else if (ok == TCL_OK) {
743
                slotPtr = (slotType == COLUMN) ?
744
                        masterPtr->masterDataPtr->columnPtr :
745
                        masterPtr->masterDataPtr->rowPtr;
746
            }
747
 
748
            /*
749
             * Return all of the options for this row or column.  If the
750
             * request is out of range, return all 0's.
751
             */
752
 
753
            if (argc == 4) {
754
                Tcl_Free((char *)argvPtr);
755
            }
756
            if ((argc == 4) && (ok == TCL_OK)) {
757
                sprintf(interp->result,"-minsize %d -pad %d -weight %d",
758
                        slotPtr[slot].minSize,slotPtr[slot].pad,
759
                        slotPtr[slot].weight);
760
                return (TCL_OK);
761
            } else if (argc == 4) {
762
                sprintf(interp->result,"-minsize %d -pad %d -weight %d", 0,0,0);
763
                return (TCL_OK);
764
            }
765
 
766
            /*
767
             * Loop through each option value pair, setting the values as required.
768
             * If only one option is given, with no value, the current value is
769
             * returned.
770
             */
771
 
772
            for (i=4; i<argc; i+=2) {
773
                length = strlen(argv[i]);
774
                if ((*argv[i] != '-') || length < 2) {
775
                    Tcl_AppendResult(interp, "invalid arg \"",
776
                            argv[i], "\" :expecting -minsize, -pad, or -weight.",
777
                            (char *) NULL);
778
                    Tcl_Free((char *)argvPtr);
779
                    return TCL_ERROR;
780
                }
781
                if (strncmp(argv[i], "-minsize", length) == 0) {
782
                    if (argc == 5) {
783
                        int value =  ok == TCL_OK ? slotPtr[slot].minSize : 0;
784
                        sprintf(interp->result,"%d",value);
785
                    } else if (Tk_GetPixels(interp, master, argv[i+1], &size)
786
                            != TCL_OK) {
787
                        Tcl_Free((char *)argvPtr);
788
                        return TCL_ERROR;
789
                    } else {
790
                        slotPtr[slot].minSize = size;
791
                    }
792
                }
793
                else if (strncmp(argv[i], "-weight", length) == 0) {
794
                    int wt;
795
                    if (argc == 5) {
796
                        int value =  ok == TCL_OK ? slotPtr[slot].weight : 0;
797
                        sprintf(interp->result,"%d",value);
798
                    } else if (Tcl_GetInt(interp, argv[i+1], &wt) != TCL_OK) {
799
                        Tcl_Free((char *)argvPtr);
800
                        return TCL_ERROR;
801
                    } else if (wt < 0) {
802
                        Tcl_AppendResult(interp, "invalid arg \"", argv[i],
803
                                "\": should be non-negative", (char *) NULL);
804
                        Tcl_Free((char *)argvPtr);
805
                        return TCL_ERROR;
806
                    } else {
807
                        slotPtr[slot].weight = wt;
808
                    }
809
                }
810
                else if (strncmp(argv[i], "-pad", length) == 0) {
811
                    if (argc == 5) {
812
                        int value =  ok == TCL_OK ? slotPtr[slot].pad : 0;
813
                        sprintf(interp->result,"%d",value);
814
                    } else if (Tk_GetPixels(interp, master, argv[i+1], &size)
815
                            != TCL_OK) {
816
                        Tcl_Free((char *)argvPtr);
817
                        return TCL_ERROR;
818
                    } else if (size < 0) {
819
                        Tcl_AppendResult(interp, "invalid arg \"", argv[i],
820
                                "\": should be non-negative", (char *) NULL);
821
                        Tcl_Free((char *)argvPtr);
822
                        return TCL_ERROR;
823
                    } else {
824
                        slotPtr[slot].pad = size;
825
                    }
826
                } else {
827
                    Tcl_AppendResult(interp, "invalid arg \"",
828
                            argv[i], "\": expecting -minsize, -pad, or -weight.",
829
                            (char *) NULL);
830
                    Tcl_Free((char *)argvPtr);
831
                    return TCL_ERROR;
832
                }
833
            }
834
        }
835
        Tcl_Free((char *)argvPtr);
836
 
837
        /*
838
         * If we changed a property, re-arrange the table,
839
         * and check for constraint shrinkage.
840
         */
841
 
842
        if (argc != 5) {
843
            if (slotType == ROW) {
844
                int last = masterPtr->masterDataPtr->rowMax - 1;
845
                while ((last >= 0) && (slotPtr[last].weight == 0)
846
                        && (slotPtr[last].pad == 0)
847
                        && (slotPtr[last].minSize == 0)) {
848
                    last--;
849
                }
850
                masterPtr->masterDataPtr->rowMax = last+1;
851
            } else {
852
                int last = masterPtr->masterDataPtr->columnMax - 1;
853
                while ((last >= 0) && (slotPtr[last].weight == 0)
854
                        && (slotPtr[last].pad == 0)
855
                        && (slotPtr[last].minSize == 0)) {
856
                    last--;
857
                }
858
                masterPtr->masterDataPtr->columnMax = last + 1;
859
            }
860
 
861
            if (masterPtr->abortPtr != NULL) {
862
                *masterPtr->abortPtr = 1;
863
            }
864
            if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
865
                masterPtr->flags |= REQUESTED_RELAYOUT;
866
                Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
867
            }
868
        }
869
    } else {
870
        Tcl_AppendResult(interp, "bad option \"", argv[1],
871
                "\":  must be bbox, columnconfigure, configure, forget, info, ",
872
                "location, propagate, remove, rowconfigure, size, or slaves.",
873
                (char *) NULL);
874
        return TCL_ERROR;
875
    }
876
    return TCL_OK;
877
}
878
 
879
/*
880
 *--------------------------------------------------------------
881
 *
882
 * GridReqProc --
883
 *
884
 *      This procedure is invoked by Tk_GeometryRequest for
885
 *      windows managed by the grid.
886
 *
887
 * Results:
888
 *      None.
889
 *
890
 * Side effects:
891
 *      Arranges for tkwin, and all its managed siblings, to
892
 *      be re-arranged at the next idle point.
893
 *
894
 *--------------------------------------------------------------
895
 */
896
 
897
static void
898
GridReqProc(clientData, tkwin)
899
    ClientData clientData;      /* Grid's information about
900
                                 * window that got new preferred
901
                                 * geometry.  */
902
    Tk_Window tkwin;            /* Other Tk-related information
903
                                 * about the window. */
904
{
905
    register Gridder *gridPtr = (Gridder *) clientData;
906
 
907
    gridPtr = gridPtr->masterPtr;
908
    if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
909
        gridPtr->flags |= REQUESTED_RELAYOUT;
910
        Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
911
    }
912
}
913
 
914
/*
915
 *--------------------------------------------------------------
916
 *
917
 * GridLostSlaveProc --
918
 *
919
 *      This procedure is invoked by Tk whenever some other geometry
920
 *      claims control over a slave that used to be managed by us.
921
 *
922
 * Results:
923
 *      None.
924
 *
925
 * Side effects:
926
 *      Forgets all grid-related information about the slave.
927
 *
928
 *--------------------------------------------------------------
929
 */
930
 
931
static void
932
GridLostSlaveProc(clientData, tkwin)
933
    ClientData clientData;      /* Grid structure for slave window that
934
                                 * was stolen away. */
935
    Tk_Window tkwin;            /* Tk's handle for the slave window. */
936
{
937
    register Gridder *slavePtr = (Gridder *) clientData;
938
 
939
    if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
940
        Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
941
    }
942
    Unlink(slavePtr);
943
    Tk_UnmapWindow(slavePtr->tkwin);
944
}
945
 
946
/*
947
 *--------------------------------------------------------------
948
 *
949
 * AdjustOffsets --
950
 *
951
 *      This procedure adjusts the size of the layout to fit in the
952
 *      space provided.  If it needs more space, the extra is added
953
 *      according to the weights.  If it needs less, the space is removed
954
 *      according to the weights, but at no time does the size drop below
955
 *      the minsize specified for that slot.
956
 *
957
 * Results:
958
 *      The initial offset of the layout,
959
 *      if all the weights are zero, else 0.
960
 *
961
 * Side effects:
962
 *      The slot offsets are modified to shrink the layout.
963
 *
964
 *--------------------------------------------------------------
965
 */
966
 
967
static int
968
AdjustOffsets(size, slots, slotPtr)
969
    int size;                   /* The total layout size (in pixels). */
970
    int slots;                  /* Number of slots. */
971
    register SlotInfo *slotPtr; /* Pointer to slot array. */
972
{
973
    register int slot;          /* Current slot. */
974
    int diff;                   /* Extra pixels needed to add to the layout. */
975
    int totalWeight = 0; /* Sum of the weights for all the slots. */
976
    int weight = 0;              /* Sum of the weights so far. */
977
    int minSize = 0;             /* Minimum possible layout size. */
978
    int newDiff;                /* The most pixels that can be added on
979
                                 * the current pass. */
980
 
981
    diff = size - slotPtr[slots-1].offset;
982
 
983
    /*
984
     * The layout is already the correct size; all done.
985
     */
986
 
987
    if (diff == 0) {
988
        return(0);
989
    }
990
 
991
    /*
992
     * If all the weights are zero, center the layout in its parent if
993
     * there is extra space, else clip on the bottom/right.
994
     */
995
 
996
    for (slot=0; slot < slots; slot++) {
997
        totalWeight += slotPtr[slot].weight;
998
    }
999
 
1000
    if (totalWeight == 0 ) {
1001
        return(diff > 0 ? diff/2 : 0);
1002
    }
1003
 
1004
    /*
1005
     * Add extra space according to the slot weights.  This is done
1006
     * cumulatively to prevent round-off error accumulation.
1007
     */
1008
 
1009
    if (diff > 0) {
1010
        for (weight=slot=0; slot < slots; slot++) {
1011
            weight += slotPtr[slot].weight;
1012
            slotPtr[slot].offset += diff * weight / totalWeight;
1013
        }
1014
        return(0);
1015
    }
1016
 
1017
    /*
1018
     * The layout must shrink below its requested size.  Compute the
1019
     * minimum possible size by looking at the slot minSizes.
1020
     */
1021
 
1022
    for (slot=0; slot < slots; slot++) {
1023
        if (slotPtr[slot].weight > 0) {
1024
            minSize += slotPtr[slot].minSize;
1025
        } else if (slot > 0) {
1026
            minSize += slotPtr[slot].offset - slotPtr[slot-1].offset;
1027
        } else {
1028
            minSize += slotPtr[slot].offset;
1029
        }
1030
    }
1031
 
1032
    /*
1033
     * If the requested size is less than the minimum required size,
1034
     * set the slot sizes to their minimum values, then clip on the
1035
     * bottom/right.
1036
     */
1037
 
1038
    if (size <= minSize) {
1039
        int offset = 0;
1040
        for (slot=0; slot < slots; slot++) {
1041
            if (slotPtr[slot].weight > 0) {
1042
                offset += slotPtr[slot].minSize;
1043
            } else if (slot > 0) {
1044
                offset += slotPtr[slot].offset - slotPtr[slot-1].offset;
1045
            } else {
1046
                offset += slotPtr[slot].offset;
1047
            }
1048
            slotPtr[slot].offset = offset;
1049
        }
1050
        return(0);
1051
    }
1052
 
1053
    /*
1054
     * Remove space from slots according to their weights.  The weights
1055
     * get renormalized anytime a slot shrinks to its minimum size.
1056
     */
1057
 
1058
    while (diff < 0) {
1059
 
1060
        /*
1061
         * Find the total weight for the shrinkable slots.
1062
         */
1063
 
1064
        for (totalWeight=slot=0; slot < slots; slot++) {
1065
            int current = (slot == 0) ? slotPtr[slot].offset :
1066
                    slotPtr[slot].offset - slotPtr[slot-1].offset;
1067
            if (current > slotPtr[slot].minSize) {
1068
                totalWeight += slotPtr[slot].weight;
1069
                slotPtr[slot].temp = slotPtr[slot].weight;
1070
            } else {
1071
                slotPtr[slot].temp = 0;
1072
            }
1073
        }
1074
        if (totalWeight == 0) {
1075
            break;
1076
        }
1077
 
1078
        /*
1079
         * Find the maximum amount of space we can distribute this pass.
1080
         */
1081
 
1082
        newDiff = diff;
1083
        for (slot = 0; slot < slots; slot++) {
1084
            int current;                /* current size of this slot */
1085
            int maxDiff;                /* max diff that would cause
1086
                                         * this slot to equal its minsize */
1087
            if (slotPtr[slot].temp == 0) {
1088
                continue;
1089
            }
1090
            current = (slot == 0) ? slotPtr[slot].offset :
1091
                    slotPtr[slot].offset - slotPtr[slot-1].offset;
1092
            maxDiff = totalWeight * (slotPtr[slot].minSize - current)
1093
                    / slotPtr[slot].temp;
1094
            if (maxDiff > newDiff) {
1095
                newDiff = maxDiff;
1096
            }
1097
        }
1098
 
1099
        /*
1100
         * Now distribute the space.
1101
         */
1102
 
1103
        for (weight=slot=0; slot < slots; slot++) {
1104
            weight += slotPtr[slot].temp;
1105
            slotPtr[slot].offset += newDiff * weight / totalWeight;
1106
        }
1107
        diff -= newDiff;
1108
    }
1109
    return(0);
1110
}
1111
 
1112
/*
1113
 *--------------------------------------------------------------
1114
 *
1115
 * AdjustForSticky --
1116
 *
1117
 *      This procedure adjusts the size of a slave in its cavity based
1118
 *      on its "sticky" flags.
1119
 *
1120
 * Results:
1121
 *      The input x, y, width, and height are changed to represent the
1122
 *      desired coordinates of the slave.
1123
 *
1124
 * Side effects:
1125
 *      None.
1126
 *
1127
 *--------------------------------------------------------------
1128
 */
1129
 
1130
static void
1131
AdjustForSticky(slavePtr, xPtr, yPtr, widthPtr, heightPtr)
1132
    Gridder *slavePtr;  /* Slave window to arrange in its cavity. */
1133
    int *xPtr;          /* Pixel location of the left edge of the cavity. */
1134
    int *yPtr;          /* Pixel location of the top edge of the cavity. */
1135
    int *widthPtr;      /* Width of the cavity (in pixels). */
1136
    int *heightPtr;     /* Height of the cavity (in pixels). */
1137
{
1138
    int diffx=0; /* Cavity width - slave width. */
1139
    int diffy=0; /* Cavity hight - slave height. */
1140
    int sticky = slavePtr->sticky;
1141
 
1142
    *xPtr += slavePtr->padX/2;
1143
    *widthPtr -= slavePtr->padX;
1144
    *yPtr += slavePtr->padY/2;
1145
    *heightPtr -= slavePtr->padY;
1146
 
1147
    if (*widthPtr > (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX)) {
1148
        diffx = *widthPtr - (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX);
1149
        *widthPtr = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX;
1150
    }
1151
 
1152
    if (*heightPtr > (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY)) {
1153
        diffy = *heightPtr - (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY);
1154
        *heightPtr = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY;
1155
    }
1156
 
1157
    if (sticky&STICK_EAST && sticky&STICK_WEST) {
1158
        *widthPtr += diffx;
1159
    }
1160
    if (sticky&STICK_NORTH && sticky&STICK_SOUTH) {
1161
        *heightPtr += diffy;
1162
    }
1163
    if (!(sticky&STICK_WEST)) {
1164
        *xPtr += (sticky&STICK_EAST) ? diffx : diffx/2;
1165
    }
1166
    if (!(sticky&STICK_NORTH)) {
1167
        *yPtr += (sticky&STICK_SOUTH) ? diffy : diffy/2;
1168
    }
1169
}
1170
 
1171
/*
1172
 *--------------------------------------------------------------
1173
 *
1174
 * ArrangeGrid --
1175
 *
1176
 *      This procedure is invoked (using the Tcl_DoWhenIdle
1177
 *      mechanism) to re-layout a set of windows managed by
1178
 *      the grid.  It is invoked at idle time so that a
1179
 *      series of grid requests can be merged into a single
1180
 *      layout operation.
1181
 *
1182
 * Results:
1183
 *      None.
1184
 *
1185
 * Side effects:
1186
 *      The slaves of masterPtr may get resized or moved.
1187
 *
1188
 *--------------------------------------------------------------
1189
 */
1190
 
1191
static void
1192
ArrangeGrid(clientData)
1193
    ClientData clientData;      /* Structure describing parent whose slaves
1194
                                 * are to be re-layed out. */
1195
{
1196
    register Gridder *masterPtr = (Gridder *) clientData;
1197
    register Gridder *slavePtr;
1198
    GridMaster *slotPtr = masterPtr->masterDataPtr;
1199
    int abort;
1200
    int width, height;          /* requested size of layout, in pixels */
1201
    int realWidth, realHeight;  /* actual size layout should take-up */
1202
 
1203
    masterPtr->flags &= ~REQUESTED_RELAYOUT;
1204
 
1205
    /*
1206
     * If the parent has no slaves anymore, then don't do anything
1207
     * at all:  just leave the parent's size as-is.  Otherwise there is
1208
     * no way to "relinquish" control over the parent so another geometry
1209
     * manager can take over.
1210
     */
1211
 
1212
    if (masterPtr->slavePtr == NULL) {
1213
        return;
1214
    }
1215
 
1216
    if (masterPtr->masterDataPtr == NULL) {
1217
        return;
1218
    }
1219
 
1220
    /*
1221
     * Abort any nested call to ArrangeGrid for this window, since
1222
     * we'll do everything necessary here, and set up so this call
1223
     * can be aborted if necessary.
1224
     */
1225
 
1226
    if (masterPtr->abortPtr != NULL) {
1227
        *masterPtr->abortPtr = 1;
1228
    }
1229
    masterPtr->abortPtr = &abort;
1230
    abort = 0;
1231
    Tcl_Preserve((ClientData) masterPtr);
1232
 
1233
    /*
1234
     * Call the constraint engine to fill in the row and column offsets.
1235
     */
1236
 
1237
    SetGridSize(masterPtr);
1238
    width =  ResolveConstraints(masterPtr, COLUMN, 0);
1239
    height = ResolveConstraints(masterPtr, ROW, 0);
1240
    width += 2*Tk_InternalBorderWidth(masterPtr->tkwin);
1241
    height += 2*Tk_InternalBorderWidth(masterPtr->tkwin);
1242
 
1243
    if (((width != Tk_ReqWidth(masterPtr->tkwin))
1244
            || (height != Tk_ReqHeight(masterPtr->tkwin)))
1245
            && !(masterPtr->flags & DONT_PROPAGATE)) {
1246
        Tk_GeometryRequest(masterPtr->tkwin, width, height);
1247
        if (width>1 && height>1) {
1248
            masterPtr->flags |= REQUESTED_RELAYOUT;
1249
            Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
1250
        }
1251
        masterPtr->abortPtr = NULL;
1252
        Tcl_Release((ClientData) masterPtr);
1253
        return;
1254
    }
1255
 
1256
    /*
1257
     * If the currently requested layout size doesn't match the parent's
1258
     * window size, then adjust the slot offsets according to the
1259
     * weights.  If all of the weights are zero, center the layout in
1260
     * its parent.  I haven't decided what to do if the parent is smaller
1261
     * than the requested size.
1262
     */
1263
 
1264
    realWidth = Tk_Width(masterPtr->tkwin) -
1265
            2*Tk_InternalBorderWidth(masterPtr->tkwin);
1266
    realHeight = Tk_Height(masterPtr->tkwin) -
1267
            2*Tk_InternalBorderWidth(masterPtr->tkwin);
1268
    slotPtr->startX = AdjustOffsets(realWidth,
1269
            MAX(slotPtr->columnEnd,slotPtr->columnMax), slotPtr->columnPtr);
1270
    slotPtr->startY = AdjustOffsets(realHeight,
1271
            MAX(slotPtr->rowEnd,slotPtr->rowMax), slotPtr->rowPtr);
1272
    slotPtr->startX += Tk_InternalBorderWidth(masterPtr->tkwin);
1273
    slotPtr->startY += Tk_InternalBorderWidth(masterPtr->tkwin);
1274
 
1275
    /*
1276
     * Now adjust the actual size of the slave to its cavity by
1277
     * computing the cavity size, and adjusting the widget according
1278
     * to its stickyness.
1279
     */
1280
 
1281
    for (slavePtr = masterPtr->slavePtr; slavePtr != NULL && !abort;
1282
            slavePtr = slavePtr->nextPtr) {
1283
        int x, y;                       /* top left coordinate */
1284
        int width, height;              /* slot or slave size */
1285
        int col = slavePtr->column;
1286
        int row = slavePtr->row;
1287
 
1288
        x = (col>0) ? slotPtr->columnPtr[col-1].offset : 0;
1289
        y = (row>0) ? slotPtr->rowPtr[row-1].offset : 0;
1290
 
1291
        width = slotPtr->columnPtr[slavePtr->numCols+col-1].offset - x;
1292
        height = slotPtr->rowPtr[slavePtr->numRows+row-1].offset - y;
1293
 
1294
        x += slotPtr->startX;
1295
        y += slotPtr->startY;
1296
 
1297
        AdjustForSticky(slavePtr, &x, &y, &width, &height);
1298
 
1299
        /*
1300
         * Now put the window in the proper spot.  (This was taken directly
1301
         * from tkPack.c.)  If the slave is a child of the master, then
1302
         * do this here.  Otherwise let Tk_MaintainGeometry do the work.
1303
         */
1304
 
1305
        if (masterPtr->tkwin == Tk_Parent(slavePtr->tkwin)) {
1306
            if ((width <= 0) || (height <= 0)) {
1307
                Tk_UnmapWindow(slavePtr->tkwin);
1308
            } else {
1309
                if ((x != Tk_X(slavePtr->tkwin))
1310
                        || (y != Tk_Y(slavePtr->tkwin))
1311
                        || (width != Tk_Width(slavePtr->tkwin))
1312
                        || (height != Tk_Height(slavePtr->tkwin))) {
1313
                    Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height);
1314
                }
1315
                if (abort) {
1316
                    break;
1317
                }
1318
 
1319
                /*
1320
                 * Don't map the slave if the master isn't mapped: wait
1321
                 * until the master gets mapped later.
1322
                 */
1323
 
1324
                if (Tk_IsMapped(masterPtr->tkwin)) {
1325
                    Tk_MapWindow(slavePtr->tkwin);
1326
                }
1327
            }
1328
        } else {
1329
            if ((width <= 0) || (height <= 0)) {
1330
                Tk_UnmaintainGeometry(slavePtr->tkwin, masterPtr->tkwin);
1331
                Tk_UnmapWindow(slavePtr->tkwin);
1332
            } else {
1333
                Tk_MaintainGeometry(slavePtr->tkwin, masterPtr->tkwin,
1334
                        x, y, width, height);
1335
            }
1336
        }
1337
    }
1338
 
1339
    masterPtr->abortPtr = NULL;
1340
    Tcl_Release((ClientData) masterPtr);
1341
}
1342
 
1343
/*
1344
 *--------------------------------------------------------------
1345
 *
1346
 * ResolveConstraints --
1347
 *
1348
 *      Resolve all of the column and row boundaries.  Most of
1349
 *      the calculations are identical for rows and columns, so this procedure
1350
 *      is called twice, once for rows, and again for columns.
1351
 *
1352
 * Results:
1353
 *      The offset (in pixels) from the left/top edge of this layout is
1354
 *      returned.
1355
 *
1356
 * Side effects:
1357
 *      The slot offsets are copied into the SlotInfo structure for the
1358
 *      geometry master.
1359
 *
1360
 *--------------------------------------------------------------
1361
 */
1362
 
1363
static int
1364
ResolveConstraints(masterPtr, slotType, maxOffset)
1365
    Gridder *masterPtr;         /* The geometry master for this grid. */
1366
    int slotType;               /* Either ROW or COLUMN. */
1367
    int maxOffset;              /* The actual maximum size of this layout
1368
                                 * in pixels,  or 0 (not currently used). */
1369
{
1370
    register SlotInfo *slotPtr; /* Pointer to row/col constraints. */
1371
    register Gridder *slavePtr; /* List of slave windows in this grid. */
1372
    int constraintCount;        /* Count of rows or columns that have
1373
                                 * constraints. */
1374
    int slotCount;              /* Last occupied row or column. */
1375
    int gridCount;              /* The larger of slotCount and constraintCount.
1376
                                 */
1377
    GridLayout *layoutPtr;      /* Temporary layout structure. */
1378
    int requiredSize;           /* The natural size of the grid (pixels).
1379
                                 * This is the minimum size needed to
1380
                                 * accomodate all of the slaves at their
1381
                                 * requested sizes. */
1382
    int offset;                 /* The pixel offset of the right edge of the
1383
                                 * current slot from the beginning of the
1384
                                 * layout. */
1385
    int slot;                   /* The current slot. */
1386
    int start;                  /* The first slot of a contiguous set whose
1387
                                 * constraints are not yet fully resolved. */
1388
    int end;                    /* The Last slot of a contiguous set whose
1389
                                 * constraints are not yet fully resolved. */
1390
 
1391
    /*
1392
     * For typical sized tables, we'll use stack space for the layout data
1393
     * to avoid the overhead of a malloc and free for every layout.
1394
     */
1395
 
1396
    GridLayout layoutData[TYPICAL_SIZE + 1];
1397
 
1398
    if (slotType == COLUMN) {
1399
        constraintCount = masterPtr->masterDataPtr->columnMax;
1400
        slotCount = masterPtr->masterDataPtr->columnEnd;
1401
        slotPtr  = masterPtr->masterDataPtr->columnPtr;
1402
    } else {
1403
        constraintCount = masterPtr->masterDataPtr->rowMax;
1404
        slotCount = masterPtr->masterDataPtr->rowEnd;
1405
        slotPtr  = masterPtr->masterDataPtr->rowPtr;
1406
    }
1407
 
1408
    /*
1409
     * Make sure there is enough memory for the layout.
1410
     */
1411
 
1412
    gridCount = MAX(constraintCount,slotCount);
1413
    if (gridCount >= TYPICAL_SIZE) {
1414
        layoutPtr = (GridLayout *) Tcl_Alloc(sizeof(GridLayout) * (1+gridCount));
1415
    } else {
1416
        layoutPtr = layoutData;
1417
    }
1418
 
1419
    /*
1420
     * Allocate an extra layout slot to represent the left/top edge of
1421
     * the 0th slot to make it easier to calculate slot widths from
1422
     * offsets without special case code.
1423
     * Initialize the "dummy" slot to the left/top of the table.
1424
     * This slot avoids special casing the first slot.
1425
     */
1426
 
1427
    layoutPtr->minOffset = 0;
1428
    layoutPtr->maxOffset = 0;
1429
    layoutPtr++;
1430
 
1431
    /*
1432
     * Step 1.
1433
     * Copy the slot constraints into the layout structure,
1434
     * and initialize the rest of the fields.
1435
     */
1436
 
1437
    for (slot=0; slot < constraintCount; slot++) {
1438
        layoutPtr[slot].minSize = slotPtr[slot].minSize;
1439
        layoutPtr[slot].weight =  slotPtr[slot].weight;
1440
        layoutPtr[slot].pad =  slotPtr[slot].pad;
1441
        layoutPtr[slot].binNextPtr = NULL;
1442
    }
1443
    for(;slot<gridCount;slot++) {
1444
        layoutPtr[slot].minSize = 0;
1445
        layoutPtr[slot].weight = 0;
1446
        layoutPtr[slot].pad = 0;
1447
        layoutPtr[slot].binNextPtr = NULL;
1448
    }
1449
 
1450
    /*
1451
     * Step 2.
1452
     * Slaves with a span of 1 are used to determine the minimum size of
1453
     * each slot.  Slaves whose span is two or more slots don't
1454
     * contribute to the minimum size of each slot directly, but can cause
1455
     * slots to grow if their size exceeds the the sizes of the slots they
1456
     * span.
1457
     *
1458
     * Bin all slaves whose spans are > 1 by their right edges.  This
1459
     * allows the computation on minimum and maximum possible layout
1460
     * sizes at each slot boundary, without the need to re-sort the slaves.
1461
     */
1462
 
1463
    switch (slotType) {
1464
        case COLUMN:
1465
            for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
1466
                        slavePtr = slavePtr->nextPtr) {
1467
                int rightEdge = slavePtr->column + slavePtr->numCols - 1;
1468
                slavePtr->size = Tk_ReqWidth(slavePtr->tkwin) +
1469
                        slavePtr->padX + slavePtr->iPadX + slavePtr->doubleBw;
1470
                if (slavePtr->numCols > 1) {
1471
                    slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr;
1472
                    layoutPtr[rightEdge].binNextPtr = slavePtr;
1473
                } else {
1474
                    int size = slavePtr->size + layoutPtr[rightEdge].pad;
1475
                    if (size > layoutPtr[rightEdge].minSize) {
1476
                        layoutPtr[rightEdge].minSize = size;
1477
                    }
1478
                }
1479
            }
1480
            break;
1481
        case ROW:
1482
            for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
1483
                        slavePtr = slavePtr->nextPtr) {
1484
                int rightEdge = slavePtr->row + slavePtr->numRows - 1;
1485
                slavePtr->size = Tk_ReqHeight(slavePtr->tkwin) +
1486
                        slavePtr->padY + slavePtr->iPadY + slavePtr->doubleBw;
1487
                if (slavePtr->numRows > 1) {
1488
                    slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr;
1489
                    layoutPtr[rightEdge].binNextPtr = slavePtr;
1490
                } else {
1491
                    int size = slavePtr->size + layoutPtr[rightEdge].pad;
1492
                    if (size > layoutPtr[rightEdge].minSize) {
1493
                        layoutPtr[rightEdge].minSize = size;
1494
                    }
1495
                }
1496
            }
1497
            break;
1498
        }
1499
 
1500
    /*
1501
     * Step 3.
1502
     * Determine the minimum slot offsets going from left to right
1503
     * that would fit all of the slaves.  This determines the minimum
1504
     */
1505
 
1506
    for (offset=slot=0; slot < gridCount; slot++) {
1507
        layoutPtr[slot].minOffset = layoutPtr[slot].minSize + offset;
1508
        for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL;
1509
                    slavePtr = slavePtr->binNextPtr) {
1510
            int span = (slotType == COLUMN) ? slavePtr->numCols : slavePtr->numRows;
1511
            int required = slavePtr->size + layoutPtr[slot - span].minOffset;
1512
            if (required > layoutPtr[slot].minOffset) {
1513
                layoutPtr[slot].minOffset = required;
1514
            }
1515
        }
1516
        offset = layoutPtr[slot].minOffset;
1517
    }
1518
 
1519
    /*
1520
     * At this point, we know the minimum required size of the entire layout.
1521
     * It might be prudent to stop here if our "master" will resize itself
1522
     * to this size.
1523
     */
1524
 
1525
    requiredSize = offset;
1526
    if (maxOffset > offset) {
1527
        offset=maxOffset;
1528
    }
1529
 
1530
    /*
1531
     * Step 4.
1532
     * Determine the minimum slot offsets going from right to left,
1533
     * bounding the pixel range of each slot boundary.
1534
     * Pre-fill all of the right offsets with the actual size of the table;
1535
     * they will be reduced as required.
1536
     */
1537
 
1538
    for (slot=0; slot < gridCount; slot++) {
1539
        layoutPtr[slot].maxOffset = offset;
1540
    }
1541
    for (slot=gridCount-1; slot > 0;) {
1542
        for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL;
1543
                    slavePtr = slavePtr->binNextPtr) {
1544
            int span = (slotType == COLUMN) ? slavePtr->numCols : slavePtr->numRows;
1545
            int require = offset - slavePtr->size;
1546
            int startSlot  = slot - span;
1547
            if (startSlot >=0 && require < layoutPtr[startSlot].maxOffset) {
1548
                layoutPtr[startSlot].maxOffset = require;
1549
            }
1550
        }
1551
        offset -= layoutPtr[slot].minSize;
1552
        slot--;
1553
        if (layoutPtr[slot].maxOffset < offset) {
1554
            offset = layoutPtr[slot].maxOffset;
1555
        } else {
1556
            layoutPtr[slot].maxOffset = offset;
1557
        }
1558
    }
1559
 
1560
    /*
1561
     * Step 5.
1562
     * At this point, each slot boundary has a range of values that
1563
     * will satisfy the overall layout size.
1564
     * Make repeated passes over the layout structure looking for
1565
     * spans of slot boundaries where the minOffsets are less than
1566
     * the maxOffsets, and adjust the offsets according to the slot
1567
     * weights.  At each pass, at least one slot boundary will have
1568
     * its range of possible values fixed at a single value.
1569
     */
1570
 
1571
    for (start=0; start < gridCount;) {
1572
        int totalWeight = 0;     /* Sum of the weights for all of the
1573
                                 * slots in this span. */
1574
        int need = 0;            /* The minimum space needed to layout
1575
                                 * this span. */
1576
        int have;               /* The actual amount of space that will
1577
                                 * be taken up by this span. */
1578
        int weight;             /* Cumulative weights of the columns in
1579
                                 * this span. */
1580
        int noWeights = 0;       /* True if the span has no weights. */
1581
 
1582
        /*
1583
         * Find a span by identifying ranges of slots whose edges are
1584
         * already constrained at fixed offsets, but whose internal
1585
         * slot boundaries have a range of possible positions.
1586
         */
1587
 
1588
        if (layoutPtr[start].minOffset == layoutPtr[start].maxOffset) {
1589
            start++;
1590
            continue;
1591
        }
1592
 
1593
        for (end=start+1; end<gridCount; end++) {
1594
            if (layoutPtr[end].minOffset == layoutPtr[end].maxOffset) {
1595
                break;
1596
            }
1597
        }
1598
 
1599
        /*
1600
         * We found a span.  Compute the total weight, minumum space required,
1601
         * for this span, and the actual amount of space the span should
1602
         * use.
1603
         */
1604
 
1605
        for (slot=start; slot<=end; slot++) {
1606
            totalWeight += layoutPtr[slot].weight;
1607
            need += layoutPtr[slot].minSize;
1608
        }
1609
        have = layoutPtr[end].maxOffset - layoutPtr[start-1].minOffset;
1610
 
1611
        /*
1612
         * If all the weights in the span are zero, then distribute the
1613
         * extra space evenly.
1614
         */
1615
 
1616
        if (totalWeight == 0) {
1617
            noWeights++;
1618
            totalWeight = end - start + 1;
1619
        }
1620
 
1621
        /*
1622
         * It might not be possible to give the span all of the space
1623
         * available on this pass without violating the size constraints
1624
         * of one or more of the internal slot boundaries.
1625
         * Determine the maximum amount of space that when added to the
1626
         * entire span, would cause a slot boundary to have its possible
1627
         * range reduced to one value, and reduce the amount of extra
1628
         * space allocated on this pass accordingly.
1629
         *
1630
         * The calculation is done cumulatively to avoid accumulating
1631
         * roundoff errors.
1632
         */
1633
 
1634
        for (weight=0,slot=start; slot<end; slot++) {
1635
            int diff = layoutPtr[slot].maxOffset - layoutPtr[slot].minOffset;
1636
            weight += noWeights ? 1 : layoutPtr[slot].weight;
1637
            if ((noWeights || layoutPtr[slot].weight>0) &&
1638
                    (diff*totalWeight/weight) < (have-need)) {
1639
                have = diff * totalWeight / weight + need;
1640
            }
1641
        }
1642
 
1643
        /*
1644
         * Now distribute the extra space among the slots by
1645
         * adjusting the minSizes and minOffsets.
1646
         */
1647
 
1648
        for (weight=0,slot=start; slot<end; slot++) {
1649
            weight += noWeights ? 1 : layoutPtr[slot].weight;
1650
            layoutPtr[slot].minOffset +=
1651
                (int)((double) (have-need) * weight/totalWeight + 0.5);
1652
            layoutPtr[slot].minSize = layoutPtr[slot].minOffset
1653
                    - layoutPtr[slot-1].minOffset;
1654
        }
1655
        layoutPtr[slot].minSize = layoutPtr[slot].minOffset
1656
                - layoutPtr[slot-1].minOffset;
1657
 
1658
        /*
1659
         * Having pushed the top/left boundaries of the slots to
1660
         * take up extra space, the bottom/right space is recalculated
1661
         * to propagate the new space allocation.
1662
         */
1663
 
1664
        for (slot=end; slot > start; slot--) {
1665
            layoutPtr[slot-1].maxOffset =
1666
                    layoutPtr[slot].maxOffset-layoutPtr[slot].minSize;
1667
        }
1668
    }
1669
 
1670
 
1671
    /*
1672
     * Step 6.
1673
     * All of the space has been apportioned; copy the
1674
     * layout information back into the master.
1675
     */
1676
 
1677
    for (slot=0; slot < gridCount; slot++) {
1678
        slotPtr[slot].offset = layoutPtr[slot].minOffset;
1679
    }
1680
 
1681
    --layoutPtr;
1682
    if (layoutPtr != layoutData) {
1683
        Tcl_Free((char *)layoutPtr);
1684
    }
1685
    return requiredSize;
1686
}
1687
 
1688
/*
1689
 *--------------------------------------------------------------
1690
 *
1691
 * GetGrid --
1692
 *
1693
 *      This internal procedure is used to locate a Grid
1694
 *      structure for a given window, creating one if one
1695
 *      doesn't exist already.
1696
 *
1697
 * Results:
1698
 *      The return value is a pointer to the Grid structure
1699
 *      corresponding to tkwin.
1700
 *
1701
 * Side effects:
1702
 *      A new grid structure may be created.  If so, then
1703
 *      a callback is set up to clean things up when the
1704
 *      window is deleted.
1705
 *
1706
 *--------------------------------------------------------------
1707
 */
1708
 
1709
static Gridder *
1710
GetGrid(tkwin)
1711
    Tk_Window tkwin;            /* Token for window for which
1712
                                 * grid structure is desired. */
1713
{
1714
    register Gridder *gridPtr;
1715
    Tcl_HashEntry *hPtr;
1716
    int new;
1717
 
1718
    if (!initialized) {
1719
        initialized = 1;
1720
        Tcl_InitHashTable(&gridHashTable, TCL_ONE_WORD_KEYS);
1721
    }
1722
 
1723
    /*
1724
     * See if there's already grid for this window.  If not,
1725
     * then create a new one.
1726
     */
1727
 
1728
    hPtr = Tcl_CreateHashEntry(&gridHashTable, (char *) tkwin, &new);
1729
    if (!new) {
1730
        return (Gridder *) Tcl_GetHashValue(hPtr);
1731
    }
1732
    gridPtr = (Gridder *) Tcl_Alloc(sizeof(Gridder));
1733
    gridPtr->tkwin = tkwin;
1734
    gridPtr->masterPtr = NULL;
1735
    gridPtr->masterDataPtr = NULL;
1736
    gridPtr->nextPtr = NULL;
1737
    gridPtr->slavePtr = NULL;
1738
    gridPtr->binNextPtr = NULL;
1739
 
1740
    gridPtr->column = gridPtr->row = -1;
1741
    gridPtr->numCols = 1;
1742
    gridPtr->numRows = 1;
1743
 
1744
    gridPtr->padX = gridPtr->padY = 0;
1745
    gridPtr->iPadX = gridPtr->iPadY = 0;
1746
    gridPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
1747
    gridPtr->abortPtr = NULL;
1748
    gridPtr->flags = 0;
1749
    gridPtr->sticky = 0;
1750
    gridPtr->size = 0;
1751
    gridPtr->masterDataPtr = NULL;
1752
    Tcl_SetHashValue(hPtr, gridPtr);
1753
    Tk_CreateEventHandler(tkwin, StructureNotifyMask,
1754
            GridStructureProc, (ClientData) gridPtr);
1755
    return gridPtr;
1756
}
1757
 
1758
/*
1759
 *--------------------------------------------------------------
1760
 *
1761
 * SetGridSize --
1762
 *
1763
 *      This internal procedure sets the size of the grid occupied
1764
 *      by slaves.
1765
 *
1766
 * Results:
1767
 *      none
1768
 *
1769
 * Side effects:
1770
 *      The width and height arguments are filled in the master data structure.
1771
 *      Additional space is allocated for the constraints to accomodate
1772
 *      the offsets.
1773
 *
1774
 *--------------------------------------------------------------
1775
 */
1776
 
1777
static void
1778
SetGridSize(masterPtr)
1779
    Gridder *masterPtr;                 /* The geometry master for this grid. */
1780
{
1781
    register Gridder *slavePtr;         /* Current slave window. */
1782
    int maxX = 0, maxY = 0;
1783
 
1784
    for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
1785
                slavePtr = slavePtr->nextPtr) {
1786
        maxX = MAX(maxX,slavePtr->numCols + slavePtr->column);
1787
        maxY = MAX(maxY,slavePtr->numRows + slavePtr->row);
1788
    }
1789
    masterPtr->masterDataPtr->columnEnd = maxX;
1790
    masterPtr->masterDataPtr->rowEnd = maxY;
1791
    CheckSlotData(masterPtr, maxX, COLUMN, CHECK_SPACE);
1792
    CheckSlotData(masterPtr, maxY, ROW, CHECK_SPACE);
1793
}
1794
 
1795
/*
1796
 *--------------------------------------------------------------
1797
 *
1798
 * CheckSlotData --
1799
 *
1800
 *      This internal procedure is used to manage the storage for
1801
 *      row and column (slot) constraints.
1802
 *
1803
 * Results:
1804
 *      TRUE if the index is OK, False otherwise.
1805
 *
1806
 * Side effects:
1807
 *      A new master grid structure may be created.  If so, then
1808
 *      it is initialized.  In addition, additional storage for
1809
 *      a row or column constraints may be allocated, and the constraint
1810
 *      maximums are adjusted.
1811
 *
1812
 *--------------------------------------------------------------
1813
 */
1814
 
1815
static int
1816
CheckSlotData(masterPtr, slot, slotType, checkOnly)
1817
    Gridder *masterPtr; /* the geometry master for this grid */
1818
    int slot;           /* which slot to look at */
1819
    int slotType;       /* ROW or COLUMN */
1820
    int checkOnly;      /* don't allocate new space if true */
1821
{
1822
    int numSlot;        /* number of slots already allocated (Space) */
1823
    int end;            /* last used constraint */
1824
 
1825
    /*
1826
     * If slot is out of bounds, return immediately.
1827
     */
1828
 
1829
    if (slot < 0 || slot >= MAX_ELEMENT) {
1830
        return TCL_ERROR;
1831
    }
1832
 
1833
    if ((checkOnly == CHECK_ONLY) && (masterPtr->masterDataPtr == NULL)) {
1834
        return TCL_ERROR;
1835
    }
1836
 
1837
    /*
1838
     * If we need to allocate more space, allocate a little extra to avoid
1839
     * repeated re-alloc's for large tables.  We need enough space to
1840
     * hold all of the offsets as well.
1841
     */
1842
 
1843
    InitMasterData(masterPtr);
1844
    end = (slotType == ROW) ? masterPtr->masterDataPtr->rowMax :
1845
            masterPtr->masterDataPtr->columnMax;
1846
    if (checkOnly == CHECK_ONLY) {
1847
        return  (end < slot) ? TCL_ERROR : TCL_OK;
1848
    } else {
1849
        numSlot = (slotType == ROW) ? masterPtr->masterDataPtr->rowSpace
1850
                                    : masterPtr->masterDataPtr->columnSpace;
1851
        if (slot >= numSlot) {
1852
            int      newNumSlot = slot + PREALLOC ;
1853
            size_t   oldSize = numSlot    * sizeof(SlotInfo) ;
1854
            size_t   newSize = newNumSlot * sizeof(SlotInfo) ;
1855
            SlotInfo *new = (SlotInfo *) Tcl_Alloc(newSize);
1856
            SlotInfo *old = (slotType == ROW) ?
1857
                    masterPtr->masterDataPtr->rowPtr :
1858
                    masterPtr->masterDataPtr->columnPtr;
1859
            memcpy((VOID *) new, (VOID *) old, oldSize );
1860
            memset((VOID *) (new+numSlot), 0, newSize - oldSize );
1861
            Tcl_Free((char *) old);
1862
            if (slotType == ROW) {
1863
                masterPtr->masterDataPtr->rowPtr = new ;
1864
                masterPtr->masterDataPtr->rowSpace = newNumSlot ;
1865
            } else {
1866
                masterPtr->masterDataPtr->columnPtr = new;
1867
                masterPtr->masterDataPtr->columnSpace = newNumSlot ;
1868
            }
1869
        }
1870
        if (slot >= end && checkOnly != CHECK_SPACE) {
1871
            if (slotType == ROW) {
1872
                masterPtr->masterDataPtr->rowMax = slot+1;
1873
            } else {
1874
                masterPtr->masterDataPtr->columnMax = slot+1;
1875
            }
1876
        }
1877
        return TCL_OK;
1878
    }
1879
}
1880
 
1881
/*
1882
 *--------------------------------------------------------------
1883
 *
1884
 * InitMasterData --
1885
 *
1886
 *      This internal procedure is used to allocate and initialize
1887
 *      the data for a geometry master, if the data
1888
 *      doesn't exist already.
1889
 *
1890
 * Results:
1891
 *      none
1892
 *
1893
 * Side effects:
1894
 *      A new master grid structure may be created.  If so, then
1895
 *      it is initialized.
1896
 *
1897
 *--------------------------------------------------------------
1898
 */
1899
 
1900
static void
1901
InitMasterData(masterPtr)
1902
    Gridder *masterPtr;
1903
{
1904
    size_t size;
1905
    if (masterPtr->masterDataPtr == NULL) {
1906
        GridMaster *gridPtr = masterPtr->masterDataPtr =
1907
                (GridMaster *) Tcl_Alloc(sizeof(GridMaster));
1908
        size = sizeof(SlotInfo) * TYPICAL_SIZE;
1909
 
1910
        gridPtr->columnEnd = 0;
1911
        gridPtr->columnMax = 0;
1912
        gridPtr->columnPtr = (SlotInfo *) Tcl_Alloc(size);
1913
        gridPtr->columnSpace = 0;
1914
        gridPtr->columnSpace = TYPICAL_SIZE;
1915
        gridPtr->rowEnd = 0;
1916
        gridPtr->rowMax = 0;
1917
        gridPtr->rowPtr = (SlotInfo *) Tcl_Alloc(size);
1918
        gridPtr->rowSpace = 0;
1919
        gridPtr->rowSpace = TYPICAL_SIZE;
1920
 
1921
        memset((VOID *) gridPtr->columnPtr, 0, size);
1922
        memset((VOID *) gridPtr->rowPtr, 0, size);
1923
    }
1924
}
1925
 
1926
/*
1927
 *----------------------------------------------------------------------
1928
 *
1929
 * Unlink --
1930
 *
1931
 *      Remove a grid from its parent's list of slaves.
1932
 *
1933
 * Results:
1934
 *      None.
1935
 *
1936
 * Side effects:
1937
 *      The parent will be scheduled for re-arranging, and the size of the
1938
 *      grid will be adjusted accordingly
1939
 *
1940
 *----------------------------------------------------------------------
1941
 */
1942
 
1943
static void
1944
Unlink(slavePtr)
1945
    register Gridder *slavePtr;         /* Window to unlink. */
1946
{
1947
    register Gridder *masterPtr, *slavePtr2;
1948
    GridMaster *gridPtr;        /* pointer to grid data */
1949
 
1950
    masterPtr = slavePtr->masterPtr;
1951
    if (masterPtr == NULL) {
1952
        return;
1953
    }
1954
 
1955
    gridPtr = masterPtr->masterDataPtr;
1956
    if (masterPtr->slavePtr == slavePtr) {
1957
        masterPtr->slavePtr = slavePtr->nextPtr;
1958
    }
1959
    else {
1960
        for (slavePtr2 = masterPtr->slavePtr; ; slavePtr2 = slavePtr2->nextPtr) {
1961
            if (slavePtr2 == NULL) {
1962
                panic("Unlink couldn't find previous window");
1963
            }
1964
            if (slavePtr2->nextPtr == slavePtr) {
1965
                slavePtr2->nextPtr = slavePtr->nextPtr;
1966
                break;
1967
            }
1968
        }
1969
    }
1970
    if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
1971
        masterPtr->flags |= REQUESTED_RELAYOUT;
1972
        Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
1973
    }
1974
    if (masterPtr->abortPtr != NULL) {
1975
        *masterPtr->abortPtr = 1;
1976
    }
1977
 
1978
    if ((slavePtr->numCols+slavePtr->column == gridPtr->columnMax)
1979
            || (slavePtr->numRows+slavePtr->row == gridPtr->rowMax)) {
1980
    }
1981
    slavePtr->masterPtr = NULL;
1982
}
1983
 
1984
/*
1985
 *----------------------------------------------------------------------
1986
 *
1987
 * DestroyGrid --
1988
 *
1989
 *      This procedure is invoked by Tk_EventuallyFree or Tcl_Release
1990
 *      to clean up the internal structure of a grid at a safe time
1991
 *      (when no-one is using it anymore).   Cleaning up the grid involves
1992
 *      freeing the main structure for all windows. and the master structure
1993
 *      for geometry managers.
1994
 *
1995
 * Results:
1996
 *      None.
1997
 *
1998
 * Side effects:
1999
 *      Everything associated with the grid is freed up.
2000
 *
2001
 *----------------------------------------------------------------------
2002
 */
2003
 
2004
static void
2005
DestroyGrid(memPtr)
2006
    char *memPtr;               /* Info about window that is now dead. */
2007
{
2008
    register Gridder *gridPtr = (Gridder *) memPtr;
2009
 
2010
    if (gridPtr->masterDataPtr != NULL) {
2011
        if (gridPtr->masterDataPtr->rowPtr != NULL) {
2012
            Tcl_Free((char *) gridPtr->masterDataPtr -> rowPtr);
2013
        }
2014
        if (gridPtr->masterDataPtr->columnPtr != NULL) {
2015
            Tcl_Free((char *) gridPtr->masterDataPtr -> columnPtr);
2016
        }
2017
        Tcl_Free((char *) gridPtr->masterDataPtr);
2018
    }
2019
    Tcl_Free((char *) gridPtr);
2020
}
2021
 
2022
/*
2023
 *----------------------------------------------------------------------
2024
 *
2025
 * GridStructureProc --
2026
 *
2027
 *      This procedure is invoked by the Tk event dispatcher in response
2028
 *      to StructureNotify events.
2029
 *
2030
 * Results:
2031
 *      None.
2032
 *
2033
 * Side effects:
2034
 *      If a window was just deleted, clean up all its grid-related
2035
 *      information.  If it was just resized, re-configure its slaves, if
2036
 *      any.
2037
 *
2038
 *----------------------------------------------------------------------
2039
 */
2040
 
2041
static void
2042
GridStructureProc(clientData, eventPtr)
2043
    ClientData clientData;              /* Our information about window
2044
                                         * referred to by eventPtr. */
2045
    XEvent *eventPtr;                   /* Describes what just happened. */
2046
{
2047
    register Gridder *gridPtr = (Gridder *) clientData;
2048
 
2049
    if (eventPtr->type == ConfigureNotify) {
2050
        if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
2051
            gridPtr->flags |= REQUESTED_RELAYOUT;
2052
            Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
2053
        }
2054
        if (gridPtr->doubleBw != 2*Tk_Changes(gridPtr->tkwin)->border_width) {
2055
            if ((gridPtr->masterPtr != NULL) &&
2056
                    !(gridPtr->masterPtr->flags & REQUESTED_RELAYOUT)) {
2057
                gridPtr->doubleBw = 2*Tk_Changes(gridPtr->tkwin)->border_width;
2058
                gridPtr->masterPtr->flags |= REQUESTED_RELAYOUT;
2059
                Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr->masterPtr);
2060
            }
2061
        }
2062
    } else if (eventPtr->type == DestroyNotify) {
2063
        register Gridder *gridPtr2, *nextPtr;
2064
 
2065
        if (gridPtr->masterPtr != NULL) {
2066
            Unlink(gridPtr);
2067
        }
2068
        for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
2069
                                           gridPtr2 = nextPtr) {
2070
            Tk_UnmapWindow(gridPtr2->tkwin);
2071
            gridPtr2->masterPtr = NULL;
2072
            nextPtr = gridPtr2->nextPtr;
2073
            gridPtr2->nextPtr = NULL;
2074
        }
2075
        Tcl_DeleteHashEntry(Tcl_FindHashEntry(&gridHashTable,
2076
                (char *) gridPtr->tkwin));
2077
        if (gridPtr->flags & REQUESTED_RELAYOUT) {
2078
            Tk_CancelIdleCall(ArrangeGrid, (ClientData) gridPtr);
2079
        }
2080
        gridPtr->tkwin = NULL;
2081
        Tk_EventuallyFree((ClientData) gridPtr, DestroyGrid);
2082
    } else if (eventPtr->type == MapNotify) {
2083
        if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
2084
            gridPtr->flags |= REQUESTED_RELAYOUT;
2085
            Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
2086
        }
2087
    } else if (eventPtr->type == UnmapNotify) {
2088
        register Gridder *gridPtr2;
2089
 
2090
        for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
2091
                                           gridPtr2 = gridPtr2->nextPtr) {
2092
            Tk_UnmapWindow(gridPtr2->tkwin);
2093
        }
2094
    }
2095
}
2096
 
2097
/*
2098
 *----------------------------------------------------------------------
2099
 *
2100
 * ConfigureSlaves --
2101
 *
2102
 *      This implements the guts of the "grid configure" command.  Given
2103
 *      a list of slaves and configuration options, it arranges for the
2104
 *      grid to manage the slaves and sets the specified options.
2105
 *      arguments consist of windows or window shortcuts followed by
2106
 *      "-option value" pairs.
2107
 *
2108
 * Results:
2109
 *      TCL_OK is returned if all went well.  Otherwise, TCL_ERROR is
2110
 *      returned and interp->result is set to contain an error message.
2111
 *
2112
 * Side effects:
2113
 *      Slave windows get taken over by the grid.
2114
 *
2115
 *----------------------------------------------------------------------
2116
 */
2117
 
2118
static int
2119
ConfigureSlaves(interp, tkwin, argc, argv)
2120
    Tcl_Interp *interp;         /* Interpreter for error reporting. */
2121
    Tk_Window tkwin;            /* Any window in application containing
2122
                                 * slaves.  Used to look up slave names. */
2123
    int argc;                   /* Number of elements in argv. */
2124
    char *argv[];               /* Argument strings:  contains one or more
2125
                                 * window names followed by any number
2126
                                 * of "option value" pairs.  Caller must
2127
                                 * make sure that there is at least one
2128
                                 * window name. */
2129
{
2130
    Gridder *masterPtr;
2131
    Gridder *slavePtr;
2132
    Tk_Window other, slave, parent, ancestor;
2133
    int i, j, c, tmp;
2134
    size_t length;
2135
    int numWindows;
2136
    int width;
2137
    int defaultColumn = 0;       /* default column number */
2138
    int defaultColumnSpan = 1;  /* default number of columns */
2139
    char *lastWindow;           /* use this window to base current
2140
                                 * Row/col on */
2141
 
2142
    /*
2143
     * Count the number of windows, or window short-cuts.
2144
     */
2145
 
2146
    for(numWindows=i=0;i<argc;i++) {
2147
        char firstChar = *argv[i];
2148
        if (firstChar == '.') {
2149
            numWindows++;
2150
            continue;
2151
        }
2152
        length = strlen(argv[i]);
2153
        if (length > 1 && firstChar == '-') {
2154
            break;
2155
        }
2156
        if (length > 1) {
2157
            Tcl_AppendResult(interp, "unexpected parameter, \"",
2158
                    argv[i], "\", in configure list. ",
2159
                    "Should be window name or option", (char *) NULL);
2160
            return TCL_ERROR;
2161
        }
2162
 
2163
        if ((firstChar == REL_HORIZ) && ((numWindows == 0) ||
2164
                (*argv[i-1] == REL_SKIP) || (*argv[i-1] == REL_VERT))) {
2165
            Tcl_AppendResult(interp,
2166
                    "Must specify window before shortcut '-'.",
2167
                    (char *) NULL);
2168
            return TCL_ERROR;
2169
        }
2170
 
2171
        if ((firstChar == REL_VERT) || (firstChar == REL_SKIP)
2172
                || (firstChar == REL_HORIZ)) {
2173
            continue;
2174
        }
2175
 
2176
        Tcl_AppendResult(interp, "invalid window shortcut, \"",
2177
                argv[i], "\" should be '-', 'x', or '^'", (char *) NULL);
2178
        return TCL_ERROR;
2179
    }
2180
    numWindows = i;
2181
 
2182
    if ((argc-numWindows)&1) {
2183
        Tcl_AppendResult(interp, "extra option or",
2184
                " option with no value", (char *) NULL);
2185
        return TCL_ERROR;
2186
    }
2187
 
2188
    /*
2189
     * Iterate over all of the slave windows and short-cuts, parsing
2190
     * options for each slave.  It's a bit wasteful to re-parse the
2191
     * options for each slave, but things get too messy if we try to
2192
     * parse the arguments just once at the beginning.  For example,
2193
     * if a slave already is managed we want to just change a few
2194
     * existing values without resetting everything.  If there are
2195
     * multiple windows, the -in option only gets processed for the
2196
     * first window.
2197
     */
2198
 
2199
    masterPtr = NULL;
2200
    for (j = 0; j < numWindows; j++) {
2201
        char firstChar = *argv[j];
2202
 
2203
        /*
2204
         * '^' and 'x' cause us to skip a column.  '-' is processed
2205
         * as part of its preceeding slave.
2206
         */
2207
 
2208
        if ((firstChar == REL_VERT) || (firstChar == REL_SKIP)) {
2209
            defaultColumn++;
2210
            continue;
2211
        }
2212
        if (firstChar == REL_HORIZ) {
2213
            continue;
2214
        }
2215
 
2216
        for (defaultColumnSpan=1;
2217
                j + defaultColumnSpan < numWindows &&
2218
                (*argv[j+defaultColumnSpan] == REL_HORIZ);
2219
                defaultColumnSpan++) {
2220
            /* null body */
2221
        }
2222
 
2223
        slave = Tk_NameToWindow(interp, argv[j], tkwin);
2224
        if (slave == NULL) {
2225
            return TCL_ERROR;
2226
        }
2227
        if (Tk_IsTopLevel(slave)) {
2228
            Tcl_AppendResult(interp, "can't manage \"", argv[j],
2229
                    "\": it's a top-level window", (char *) NULL);
2230
            return TCL_ERROR;
2231
        }
2232
        slavePtr = GetGrid(slave);
2233
 
2234
        /*
2235
         * The following statement is taken from tkPack.c:
2236
         *
2237
         * "If the slave isn't currently managed, reset all of its
2238
         * configuration information to default values (there could
2239
         * be old values left from a previous packer)."
2240
         *
2241
         * I [D.S.] disagree with this statement.  If a slave is disabled (using
2242
         * "forget") and then re-enabled, I submit that 90% of the time the
2243
         * programmer will want it to retain its old configuration information.
2244
         * If the programmer doesn't want this behavior, then the
2245
         * defaults can be reestablished by hand, without having to worry
2246
         * about keeping track of the old state.
2247
         */
2248
 
2249
        for (i = numWindows; i < argc; i+=2) {
2250
            length = strlen(argv[i]);
2251
            c = argv[i][1];
2252
 
2253
            if (length < 2) {
2254
                Tcl_AppendResult(interp, "unknown or ambiguous option \"",
2255
                        argv[i], "\": must be ",
2256
                        "-column, -columnspan, -in, -ipadx, -ipady, ",
2257
                        "-padx, -pady, -row, -rowspan, or -sticky",
2258
                        (char *) NULL);
2259
                return TCL_ERROR;
2260
            }
2261
            if ((c == 'c') && (strncmp(argv[i], "-column", length) == 0)) {
2262
                if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
2263
                    Tcl_ResetResult(interp);
2264
                    Tcl_AppendResult(interp, "bad column value \"", argv[i+1],
2265
                            "\": must be a non-negative integer", (char *)NULL);
2266
                    return TCL_ERROR;
2267
                }
2268
                slavePtr->column = tmp;
2269
            } else if ((c == 'c')
2270
                    && (strncmp(argv[i], "-columnspan", length) == 0)) {
2271
                if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp <= 0) {
2272
                    Tcl_ResetResult(interp);
2273
                    Tcl_AppendResult(interp, "bad columnspan value \"", argv[i+1],
2274
                            "\": must be a positive integer", (char *)NULL);
2275
                    return TCL_ERROR;
2276
                }
2277
                slavePtr->numCols = tmp;
2278
            } else if ((c == 'i') && (strncmp(argv[i], "-in", length) == 0)) {
2279
                other = Tk_NameToWindow(interp, argv[i+1], tkwin);
2280
                if (other == NULL) {
2281
                    return TCL_ERROR;
2282
                }
2283
                if (other == slave) {
2284
                    sprintf(interp->result,"Window can't be managed in itself");
2285
                    return TCL_ERROR;
2286
                }
2287
                masterPtr = GetGrid(other);
2288
                InitMasterData(masterPtr);
2289
            } else if ((c == 'i')
2290
                    && (strncmp(argv[i], "-ipadx", length) == 0)) {
2291
                if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
2292
                        || (tmp < 0)) {
2293
                    Tcl_ResetResult(interp);
2294
                    Tcl_AppendResult(interp, "bad ipadx value \"", argv[i+1],
2295
                            "\": must be positive screen distance",
2296
                            (char *) NULL);
2297
                    return TCL_ERROR;
2298
                }
2299
                slavePtr->iPadX = tmp*2;
2300
            } else if ((c == 'i')
2301
                    && (strncmp(argv[i], "-ipady", length) == 0)) {
2302
                if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
2303
                        || (tmp< 0)) {
2304
                    Tcl_ResetResult(interp);
2305
                    Tcl_AppendResult(interp, "bad ipady value \"", argv[i+1],
2306
                            "\": must be positive screen distance",
2307
                            (char *) NULL);
2308
                    return TCL_ERROR;
2309
                }
2310
                slavePtr->iPadY = tmp*2;
2311
            } else if ((c == 'p')
2312
                    && (strncmp(argv[i], "-padx", length) == 0)) {
2313
                if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
2314
                        || (tmp< 0)) {
2315
                    Tcl_ResetResult(interp);
2316
                    Tcl_AppendResult(interp, "bad padx value \"", argv[i+1],
2317
                            "\": must be positive screen distance",
2318
                            (char *) NULL);
2319
                    return TCL_ERROR;
2320
                }
2321
                slavePtr->padX = tmp*2;
2322
            } else if ((c == 'p')
2323
                    && (strncmp(argv[i], "-pady", length) == 0)) {
2324
                if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
2325
                        || (tmp< 0)) {
2326
                    Tcl_ResetResult(interp);
2327
                    Tcl_AppendResult(interp, "bad pady value \"", argv[i+1],
2328
                            "\": must be positive screen distance",
2329
                            (char *) NULL);
2330
                    return TCL_ERROR;
2331
                }
2332
                slavePtr->padY = tmp*2;
2333
            } else if ((c == 'r') && (strncmp(argv[i], "-row", length) == 0)) {
2334
                if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
2335
                    Tcl_ResetResult(interp);
2336
                    Tcl_AppendResult(interp, "bad grid value \"", argv[i+1],
2337
                            "\": must be a non-negative integer", (char *)NULL);
2338
                    return TCL_ERROR;
2339
                }
2340
                slavePtr->row = tmp;
2341
            } else if ((c == 'r')
2342
                    && (strncmp(argv[i], "-rowspan", length) == 0)) {
2343
                if ((Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK) || tmp<=0) {
2344
                    Tcl_ResetResult(interp);
2345
                    Tcl_AppendResult(interp, "bad rowspan value \"", argv[i+1],
2346
                            "\": must be a positive integer", (char *)NULL);
2347
                    return TCL_ERROR;
2348
                }
2349
                slavePtr->numRows = tmp;
2350
            } else if ((c == 's')
2351
                    && strncmp(argv[i], "-sticky", length) == 0) {
2352
                int sticky = StringToSticky(argv[i+1]);
2353
                if (sticky == -1) {
2354
                    Tcl_AppendResult(interp, "bad stickyness value \"", argv[i+1],
2355
                            "\": must be a string containing n, e, s, and/or w",
2356
                            (char *)NULL);
2357
                    return TCL_ERROR;
2358
                }
2359
                slavePtr->sticky = sticky;
2360
            } else {
2361
                Tcl_AppendResult(interp, "unknown or ambiguous option \"",
2362
                        argv[i], "\": must be ",
2363
                        "-column, -columnspan, -in, -ipadx, -ipady, ",
2364
                        "-padx, -pady, -row, -rowspan, or -sticky",
2365
                        (char *) NULL);
2366
                return TCL_ERROR;
2367
            }
2368
        }
2369
 
2370
        /*
2371
         * Make sure we have a geometry master.  We look at:
2372
         *  1)   the -in flag
2373
         *  2)   the geometry master of the first slave (if specified)
2374
         *  3)   the parent of the first slave.
2375
         */
2376
 
2377
        if (masterPtr == NULL) {
2378
            masterPtr = slavePtr->masterPtr;
2379
        }
2380
        parent = Tk_Parent(slave);
2381
        if (masterPtr == NULL) {
2382
            masterPtr = GetGrid(parent);
2383
            InitMasterData(masterPtr);
2384
        }
2385
 
2386
        if (slavePtr->masterPtr != NULL && slavePtr->masterPtr != masterPtr) {
2387
            Unlink(slavePtr);
2388
            slavePtr->masterPtr = NULL;
2389
        }
2390
 
2391
        if (slavePtr->masterPtr == NULL) {
2392
            Gridder *tempPtr = masterPtr->slavePtr;
2393
            slavePtr->masterPtr = masterPtr;
2394
            masterPtr->slavePtr = slavePtr;
2395
            slavePtr->nextPtr = tempPtr;
2396
        }
2397
 
2398
        /*
2399
         * Make sure that the slave's parent is either the master or
2400
         * an ancestor of the master, and that the master and slave
2401
         * aren't the same.
2402
         */
2403
 
2404
        for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
2405
            if (ancestor == parent) {
2406
                break;
2407
            }
2408
            if (Tk_IsTopLevel(ancestor)) {
2409
                Tcl_AppendResult(interp, "can't put ", argv[j],
2410
                        " inside ", Tk_PathName(masterPtr->tkwin),
2411
                        (char *) NULL);
2412
                Unlink(slavePtr);
2413
                return TCL_ERROR;
2414
            }
2415
        }
2416
 
2417
        /*
2418
         * Try to make sure our master isn't managed by us.
2419
         */
2420
 
2421
        if (masterPtr->masterPtr == slavePtr) {
2422
            Tcl_AppendResult(interp, "can't put ", argv[j],
2423
                    " inside ", Tk_PathName(masterPtr->tkwin),
2424
                    ", would cause management loop.",
2425
                    (char *) NULL);
2426
            Unlink(slavePtr);
2427
            return TCL_ERROR;
2428
        }
2429
 
2430
        Tk_ManageGeometry(slave, &gridMgrType, (ClientData) slavePtr);
2431
 
2432
        /*
2433
         * Assign default position information.
2434
         */
2435
 
2436
        if (slavePtr->column == -1) {
2437
            slavePtr->column = defaultColumn;
2438
        }
2439
        slavePtr->numCols += defaultColumnSpan - 1;
2440
        if (slavePtr->row == -1) {
2441
            if (masterPtr->masterDataPtr == NULL) {
2442
                slavePtr->row = 0;
2443
            } else {
2444
                slavePtr->row = masterPtr->masterDataPtr->rowEnd;
2445
            }
2446
        }
2447
        defaultColumn += slavePtr->numCols;
2448
        defaultColumnSpan = 1;
2449
 
2450
        /*
2451
         * Arrange for the parent to be re-arranged at the first
2452
         * idle moment.
2453
         */
2454
 
2455
        if (masterPtr->abortPtr != NULL) {
2456
            *masterPtr->abortPtr = 1;
2457
        }
2458
        if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
2459
            masterPtr->flags |= REQUESTED_RELAYOUT;
2460
            Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
2461
        }
2462
    }
2463
 
2464
    /* Now look for all the "^"'s. */
2465
 
2466
    lastWindow = NULL;
2467
    for (j = 0; j < numWindows; j++) {
2468
        struct Gridder *otherPtr;
2469
        int match;                      /* found a match for the ^ */
2470
        int lastRow, lastColumn;                /* implied end of table */
2471
 
2472
        if (*argv[j] == '.') {
2473
            lastWindow = argv[j];
2474
        }
2475
        if (*argv[j] != REL_VERT) {
2476
            continue;
2477
        }
2478
 
2479
        if (masterPtr == NULL) {
2480
            Tcl_AppendResult(interp, "can't use '^', cant find master",
2481
                    (char *) NULL);
2482
            return TCL_ERROR;
2483
        }
2484
 
2485
        for (width=1; width+j < numWindows && *argv[j+width] == REL_VERT;
2486
                width++) {
2487
            /* Null Body */
2488
        }
2489
 
2490
        /*
2491
         * Find the implied grid location of the ^
2492
         */
2493
 
2494
        if (lastWindow == NULL) {
2495
            if (masterPtr->masterDataPtr != NULL) {
2496
                SetGridSize(masterPtr);
2497
                lastRow = masterPtr->masterDataPtr->rowEnd - 1;
2498
            } else {
2499
                lastRow = 0;
2500
            }
2501
            lastColumn = 0;
2502
        } else {
2503
            other = Tk_NameToWindow(interp, lastWindow, tkwin);
2504
            otherPtr = GetGrid(other);
2505
            lastRow = otherPtr->row;
2506
            lastColumn = otherPtr->column + otherPtr->numCols;
2507
        }
2508
 
2509
        for (match=0, slavePtr = masterPtr->slavePtr; slavePtr != NULL;
2510
                                         slavePtr = slavePtr->nextPtr) {
2511
 
2512
            if (slavePtr->numCols == width
2513
                    && slavePtr->column == lastColumn
2514
                    && slavePtr->row + slavePtr->numRows == lastRow) {
2515
                slavePtr->numRows++;
2516
                match++;
2517
            }
2518
            lastWindow = Tk_PathName(slavePtr->tkwin);
2519
        }
2520
        if (!match) {
2521
            Tcl_AppendResult(interp, "can't find slave to extend with \"^\".",
2522
                    (char *) NULL);
2523
            return TCL_ERROR;
2524
        }
2525
        j += width - 1;
2526
    }
2527
 
2528
    if (masterPtr == NULL) {
2529
        Tcl_AppendResult(interp, "can't determine master window",
2530
                (char *) NULL);
2531
        return TCL_ERROR;
2532
    }
2533
    SetGridSize(masterPtr);
2534
    return TCL_OK;
2535
}
2536
 
2537
/*
2538
 *----------------------------------------------------------------------
2539
 *
2540
 * StickyToString
2541
 *
2542
 *      Converts the internal boolean combination of "sticky" bits onto
2543
 *      a TCL list element containing zero or mor of n, s, e, or w.
2544
 *
2545
 * Results:
2546
 *      A string is placed into the "result" pointer.
2547
 *
2548
 * Side effects:
2549
 *      none.
2550
 *
2551
 *----------------------------------------------------------------------
2552
 */
2553
 
2554
static void
2555
StickyToString(flags, result)
2556
    int flags;          /* the sticky flags */
2557
    char *result;       /* where to put the result */
2558
{
2559
    int count = 0;
2560
    if (flags&STICK_NORTH) {
2561
        result[count++] = 'n';
2562
    }
2563
    if (flags&STICK_EAST) {
2564
        result[count++] = 'e';
2565
    }
2566
    if (flags&STICK_SOUTH) {
2567
        result[count++] = 's';
2568
    }
2569
    if (flags&STICK_WEST) {
2570
        result[count++] = 'w';
2571
    }
2572
    if (count) {
2573
        result[count] = '\0';
2574
    } else {
2575
        sprintf(result,"{}");
2576
    }
2577
}
2578
 
2579
/*
2580
 *----------------------------------------------------------------------
2581
 *
2582
 * StringToSticky --
2583
 *
2584
 *      Converts an ascii string representing a widgets stickyness
2585
 *      into the boolean result.
2586
 *
2587
 * Results:
2588
 *      The boolean combination of the "sticky" bits is retuned.  If an
2589
 *      error occurs, such as an invalid character, -1 is returned instead.
2590
 *
2591
 * Side effects:
2592
 *      none
2593
 *
2594
 *----------------------------------------------------------------------
2595
 */
2596
 
2597
static int
2598
StringToSticky(string)
2599
    char *string;
2600
{
2601
    int sticky = 0;
2602
    char c;
2603
 
2604
    while ((c = *string++) != '\0') {
2605
        switch (c) {
2606
            case 'n': case 'N': sticky |= STICK_NORTH; break;
2607
            case 'e': case 'E': sticky |= STICK_EAST;  break;
2608
            case 's': case 'S': sticky |= STICK_SOUTH; break;
2609
            case 'w': case 'W': sticky |= STICK_WEST;  break;
2610
            case ' ': case ',': case '\t': case '\r': case '\n': break;
2611
            default: return -1;
2612
        }
2613
    }
2614
    return sticky;
2615
}

powered by: WebSVN 2.1.0

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