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