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