/[dtapublic]/projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkpack.c
ViewVC logotype

Contents of /projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkpack.c

Parent Directory Parent Directory | Revision Log Revision Log


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

Properties

Name Value
svn:eol-style native
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25