]> git.zerfleddert.de Git - micropolis/blob - src/tk/tkpack.c
Import Micropolis from http://www.donhopkins.com/home/micropolis/
[micropolis] / src / tk / tkpack.c
1 /*
2 * tkPack.c --
3 *
4 * This file contains code to implement the "packer"
5 * geometry manager for Tk.
6 *
7 * Copyright 1990 Regents of the University of California.
8 * Permission to use, copy, modify, and distribute this
9 * software and its documentation for any purpose and without
10 * fee is hereby granted, provided that the above copyright
11 * notice appear in all copies. The University of California
12 * makes no representations about the suitability of this
13 * software for any purpose. It is provided "as is" without
14 * express or implied warranty.
15 */
16
17 #ifndef lint
18 static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkPack.c,v 1.27 92/01/04 15:16:41 ouster Exp $ SPRITE (Berkeley)";
19 #endif
20
21 #include "tkconfig.h"
22 #include "tkint.h"
23
24 typedef enum {TOP, BOTTOM, LEFT, RIGHT} Side;
25
26 /* For each window that the packer cares about (either because
27 * the window is managed by the packer or because the window
28 * has children that are managed by the packer), there is a
29 * structure of the following type:
30 */
31
32 typedef struct Packer {
33 Tk_Window tkwin; /* Tk token for window. NULL means that
34 * the window has been deleted, but the
35 * packet hasn't had a chance to clean up
36 * yet because the structure is still in
37 * use. */
38 struct Packer *parentPtr; /* Parent within which this window
39 * is packed (NULL means this window
40 * isn't managed by the packer). */
41 struct Packer *nextPtr; /* Next window packed within same
42 * parent. List is priority-ordered:
43 * first on list gets packed first. */
44 struct Packer *childPtr; /* First in list of children packed
45 * inside this window (NULL means
46 * no packed children). */
47 Side side; /* Side of parent against which
48 * this window is packed. */
49 Tk_Anchor anchorPoint; /* If frame allocated for window is larger
50 * than window needs, this indicates how
51 * where to position window in frame. */
52 int padX, padY; /* Additional amounts of space to give window
53 * besides what it asked for. */
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 children
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 Tk_DoWhenIdle request
71 * has already been made to repack
72 * all the children 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 */
81
82 #define REQUESTED_REPACK 1
83 #define FILLX 2
84 #define FILLY 4
85 #define EXPAND 8
86
87 /*
88 * Hash table used to map from Tk_Window tokens to corresponding
89 * Packer structures:
90 */
91
92 static Tcl_HashTable packerHashTable;
93
94 /*
95 * Have statics in this module been initialized?
96 */
97
98 static initialized = 0;
99
100 /*
101 * Forward declarations for procedures defined later in this file:
102 */
103
104 static void ArrangePacking _ANSI_ARGS_((ClientData clientData));
105 static Packer * GetPacker _ANSI_ARGS_((Tk_Window tkwin));
106 static int PackAfter _ANSI_ARGS_((Tcl_Interp *interp,
107 Packer *prevPtr, Packer *parentPtr, int argc,
108 char **argv));
109 static void PackReqProc _ANSI_ARGS_((ClientData clientData,
110 Tk_Window tkwin));
111 static void PackStructureProc _ANSI_ARGS_((ClientData clientData,
112 XEvent *eventPtr));
113 static void Unlink _ANSI_ARGS_((Packer *packPtr));
114 \f
115 /*
116 *--------------------------------------------------------------
117 *
118 * Tk_PackCmd --
119 *
120 * This procedure is invoked to process the "pack" Tcl command.
121 * See the user documentation for details on what it does.
122 *
123 * Results:
124 * A standard Tcl result.
125 *
126 * Side effects:
127 * See the user documentation.
128 *
129 *--------------------------------------------------------------
130 */
131
132 int
133 Tk_PackCmd(clientData, interp, argc, argv)
134 ClientData clientData; /* Main window associated with
135 * interpreter. */
136 Tcl_Interp *interp; /* Current interpreter. */
137 int argc; /* Number of arguments. */
138 char **argv; /* Argument strings. */
139 {
140 Tk_Window tkwin = (Tk_Window) clientData;
141 int length;
142 char c;
143
144 if (argc < 3) {
145 Tcl_AppendResult(interp, "wrong # args: should be \"",
146 argv[0], " option arg ?arg ...?\"", (char *) NULL);
147 return TCL_ERROR;
148 }
149 c = argv[1][0];
150 length = strlen(argv[1]);
151 if ((c == 'a') && (length >= 2)
152 && (strncmp(argv[1], "after", length) == 0)) {
153 Packer *prevPtr;
154 Tk_Window tkwin2;
155
156 tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
157 if (tkwin2 == NULL) {
158 return TCL_ERROR;
159 }
160 prevPtr = GetPacker(tkwin2);
161 if (prevPtr->parentPtr == NULL) {
162 Tcl_AppendResult(interp, "window \"", argv[2],
163 "\" isn't packed", (char *) NULL);
164 return TCL_ERROR;
165 }
166 return PackAfter(interp, prevPtr, prevPtr->parentPtr, argc-3, argv+3);
167 } else if ((c == 'a') && (length >= 2)
168 && (strncmp(argv[1], "append", length) == 0)) {
169 Packer *parentPtr;
170 register Packer *prevPtr;
171 Tk_Window tkwin2;
172
173 tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
174 if (tkwin2 == NULL) {
175 return TCL_ERROR;
176 }
177 parentPtr = GetPacker(tkwin2);
178 prevPtr = parentPtr->childPtr;
179 if (prevPtr != NULL) {
180 while (prevPtr->nextPtr != NULL) {
181 prevPtr = prevPtr->nextPtr;
182 }
183 }
184 return PackAfter(interp, prevPtr, parentPtr, argc-3, argv+3);
185 } else if ((c == 'b') && (strncmp(argv[1], "before", length) == 0)) {
186 Packer *packPtr, *parentPtr;
187 register Packer *prevPtr;
188 Tk_Window tkwin2;
189
190 tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
191 if (tkwin2 == NULL) {
192 return TCL_ERROR;
193 }
194 packPtr = GetPacker(tkwin2);
195 if (packPtr->parentPtr == NULL) {
196 Tcl_AppendResult(interp, "window \"", argv[2],
197 "\" isn't packed", (char *) NULL);
198 return TCL_ERROR;
199 }
200 parentPtr = packPtr->parentPtr;
201 prevPtr = parentPtr->childPtr;
202 if (prevPtr == packPtr) {
203 prevPtr = NULL;
204 } else {
205 for ( ; ; prevPtr = prevPtr->nextPtr) {
206 if (prevPtr == NULL) {
207 panic("\"pack before\" couldn't find predecessor");
208 }
209 if (prevPtr->nextPtr == packPtr) {
210 break;
211 }
212 }
213 }
214 return PackAfter(interp, prevPtr, parentPtr, argc-3, argv+3);
215 } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
216 char *prefix;
217 register Packer *packPtr;
218 Tk_Window tkwin2;
219 char tmp[20];
220 static char *sideNames[] = {"top", "bottom", "left", "right"};
221
222 if (argc != 3) {
223 Tcl_AppendResult(interp, "wrong # args: should be \"",
224 argv[0], " info window\"", (char *) NULL);
225 return TCL_ERROR;
226 }
227 tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
228 if (tkwin2 == NULL) {
229 return TCL_ERROR;
230 }
231 packPtr = GetPacker(tkwin2);
232 prefix = "";
233 for (packPtr = packPtr->childPtr; packPtr != NULL;
234 packPtr = packPtr->nextPtr) {
235 Tcl_AppendResult(interp, prefix, Tk_PathName(packPtr->tkwin),
236 " {", sideNames[(int) packPtr->side],
237 " frame ", Tk_NameOfAnchor(packPtr->anchorPoint),
238 (char *) NULL);
239 if (packPtr->padX != 0) {
240 sprintf(tmp, "%d", packPtr->padX);
241 Tcl_AppendResult(interp, " padx ", tmp, (char *) NULL);
242 }
243 if (packPtr->padY != 0) {
244 sprintf(tmp, "%d", packPtr->padY);
245 Tcl_AppendResult(interp, " pady ", tmp, (char *) NULL);
246 }
247 if (packPtr->flags & EXPAND) {
248 Tcl_AppendResult(interp, " expand", (char *) NULL);
249 }
250 if ((packPtr->flags & (FILLX|FILLY)) == (FILLX|FILLY)) {
251 Tcl_AppendResult(interp, " fill", (char *) NULL);
252 } else if (packPtr->flags & FILLX) {
253 Tcl_AppendResult(interp, " fillx", (char *) NULL);
254 } else if (packPtr->flags & FILLY) {
255 Tcl_AppendResult(interp, " filly", (char *) NULL);
256 }
257 Tcl_AppendResult(interp, "}", (char *) NULL);
258 prefix = " ";
259 }
260 return TCL_OK;
261 } else if ((c == 'u') && (strncmp(argv[1], "unpack", length) == 0)) {
262 Tk_Window tkwin2;
263 Packer *packPtr;
264
265 if (argc != 3) {
266 Tcl_AppendResult(interp, "wrong # args: should be \"",
267 argv[0], " unpack window\"", (char *) NULL);
268 return TCL_ERROR;
269 }
270 tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
271 if (tkwin2 == NULL) {
272 return TCL_ERROR;
273 }
274 packPtr = GetPacker(tkwin2);
275 if ((packPtr != NULL) && (packPtr->parentPtr != NULL)) {
276 Tk_ManageGeometry(tkwin2, (Tk_GeometryProc *) NULL,
277 (ClientData) NULL);
278 Unlink(packPtr);
279 Tk_UnmapWindow(packPtr->tkwin);
280 }
281 } else {
282 Tcl_AppendResult(interp, "bad option \"", argv[1],
283 "\": must be after, append, before, or info", (char *) NULL);
284 return TCL_ERROR;
285 }
286 return TCL_OK;
287 }
288 \f
289 /*
290 *--------------------------------------------------------------
291 *
292 * PackReqProc --
293 *
294 * This procedure is invoked by Tk_GeometryRequest for
295 * windows managed by the packer.
296 *
297 * Results:
298 * None.
299 *
300 * Side effects:
301 * Arranges for tkwin, and all its managed siblings, to
302 * be re-packed at the next idle point.
303 *
304 *--------------------------------------------------------------
305 */
306
307 /* ARGSUSED */
308 static void
309 PackReqProc(clientData, tkwin)
310 ClientData clientData; /* Packer's information about
311 * window that got new preferred
312 * geometry. */
313 Tk_Window tkwin; /* Other Tk-related information
314 * about the window. */
315 {
316 register Packer *packPtr = (Packer *) clientData;
317
318 packPtr = packPtr->parentPtr;
319 if (!(packPtr->flags & REQUESTED_REPACK)) {
320 packPtr->flags |= REQUESTED_REPACK;
321 Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
322 }
323 }
324 \f
325 /*
326 *--------------------------------------------------------------
327 *
328 * ArrangePacking --
329 *
330 * This procedure is invoked (using the Tk_DoWhenIdle
331 * mechanism) to re-layout a set of windows managed by
332 * the packer. It is invoked at idle time so that a
333 * series of packer requests can be merged into a single
334 * layout operation.
335 *
336 * Results:
337 * None.
338 *
339 * Side effects:
340 * The packed children of parentPtr may get resized or
341 * moved.
342 *
343 *--------------------------------------------------------------
344 */
345
346 static void
347 ArrangePacking(clientData)
348 ClientData clientData; /* Structure describing parent
349 * whose children are to be
350 * re-layed out. */
351 {
352 register Packer *parentPtr = (Packer *) clientData;
353 register Packer *childPtr;
354 int numExpX, numExpY; /* # of windows that are expandable in
355 * each direction. */
356 int spareX, spareY; /* Amount of extra space to give to each
357 * expandable window. */
358 int leftOverX, leftOverY; /* Extra chunk of space to give to last
359 * expandable window. */
360 int cavityX, cavityY, cavityWidth, cavityHeight;
361 /* These variables keep track of the
362 * as-yet-unallocated space remaining in
363 * the middle of the parent window. */
364 int frameX, frameY, frameWidth, frameHeight;
365 /* These variables keep track of the frame
366 * allocated to the current window. */
367 int x, y, width, height; /* These variables are used to hold the
368 * actual geometry of the current window. */
369 int intBWidth; /* Width of internal border in parent window,
370 * if any. */
371 int abort; /* May get set to non-zero to abort this
372 * repacking operation. */
373 int maxWidth, maxHeight, tmp;
374
375 parentPtr->flags &= ~REQUESTED_REPACK;
376
377 /*
378 * If the parent has no children anymore, then don't do anything
379 * at all: just leave the parent's size as-is.
380 */
381
382 if (parentPtr->childPtr == NULL) {
383 return;
384 }
385
386 /*
387 * Abort any nested call to ArrangePacking for this window, since
388 * we'll do everything necessary here, and set up so this call
389 * can be aborted if necessary.
390 */
391
392 if (parentPtr->abortPtr != NULL) {
393 *parentPtr->abortPtr = 1;
394 }
395 parentPtr->abortPtr = &abort;
396 abort = 0;
397 Tk_Preserve((ClientData) parentPtr);
398
399 /*
400 * Pass #1: scan all the children to figure out the total amount
401 * of space needed. Two separate widths and heights are computed.
402 *
403 * "Width" and "height" compute the minimum parent size to meet
404 * the needs of each window in the direction "where there is
405 * flexibility". For example, if a child is packed TOP, then
406 * y is the flexible direction: the child's requested height
407 * will determine its size. For this window x is the inflexible
408 * direction: the window's width will be determined by the amount
409 * of space left in the parent's cavity, not by the window's
410 * requested width. "Width" and "height" are needed in order to
411 * compute how much extra space there is, so that it can be divided
412 * among the windows that have the EXPAND flag.
413 *
414 * "MaxWidth" and "maxHeight" compute the minimum parent size to
415 * meet all the needs of every window in both directions, flexible
416 * or inflexible. These values are needed to make geometry requests
417 * of the parent's parent.
418 */
419
420 intBWidth = Tk_InternalBorderWidth(parentPtr->tkwin);
421 width = height = maxWidth = maxHeight = 2*intBWidth;
422 numExpX = numExpY = 0;
423 for (childPtr = parentPtr->childPtr; childPtr != NULL;
424 childPtr = childPtr->nextPtr) {
425 if ((childPtr->side == TOP) || (childPtr->side == BOTTOM)) {
426 tmp = Tk_ReqWidth(childPtr->tkwin) + childPtr->doubleBw
427 + childPtr->padX + width;
428 if (tmp > maxWidth) {
429 maxWidth = tmp;
430 }
431 height += Tk_ReqHeight(childPtr->tkwin) + childPtr->doubleBw
432 + childPtr->padY;
433 if (childPtr->flags & EXPAND) {
434 numExpY++;
435 }
436 } else {
437 tmp = Tk_ReqHeight(childPtr->tkwin) + childPtr->doubleBw
438 + childPtr->padY + height;
439 if (tmp > maxHeight) {
440 maxHeight = tmp;
441 }
442 width += Tk_ReqWidth(childPtr->tkwin) + childPtr->doubleBw
443 + childPtr->padX;
444 if (childPtr->flags & EXPAND) {
445 numExpX++;
446 }
447 }
448 }
449 if (width > maxWidth) {
450 maxWidth = width;
451 }
452 if (height > maxHeight) {
453 maxHeight = height;
454 }
455
456 /*
457 * If the total amount of space needed in the parent window has
458 * changed, then notify the next geometry manager up and requeue
459 * ourselves to start again after the parent has had a chance to
460 * resize us.
461 */
462
463 if ((maxWidth != Tk_ReqWidth(parentPtr->tkwin))
464 || (maxHeight != Tk_ReqHeight(parentPtr->tkwin))) {
465 Tk_GeometryRequest(parentPtr->tkwin, maxWidth, maxHeight);
466 parentPtr->flags |= REQUESTED_REPACK;
467 Tk_DoWhenIdle(ArrangePacking, (ClientData) parentPtr);
468 goto done;
469 }
470
471 /*
472 * If there is spare space, figure out how much of it goes to
473 * each of the windows that is expandable.
474 */
475
476 spareX = Tk_Width(parentPtr->tkwin) - width;
477 spareY = Tk_Height(parentPtr->tkwin) - height;
478 if ((spareX <= 0) || (numExpX == 0)) {
479 leftOverX = 0;
480 spareX = 0;
481 } else {
482 leftOverX = spareX % numExpX;
483 spareX /= numExpX;
484 }
485 if ((spareY <= 0) || (numExpY == 0)) {
486 leftOverY = spareY;
487 spareY = 0;
488 } else {
489 leftOverY = spareY % numExpY;
490 spareY /= numExpY;
491 }
492
493 /*
494 * Pass #2: scan the children a second time assigning
495 * new sizes. The "cavity" variables keep track of the
496 * unclaimed space in the cavity of the window; this
497 * shrinks inward as we allocate windows around the
498 * edges. The "frame" variables keep track of the space
499 * allocated to the current window and its frame. The
500 * current window is then placed somewhere inside the
501 * frame, depending on anchorPoint.
502 */
503
504 cavityX = cavityY = x = y = intBWidth;
505 cavityWidth = Tk_Width(parentPtr->tkwin) - 2*intBWidth;
506 cavityHeight = Tk_Height(parentPtr->tkwin) - 2*intBWidth;
507 for (childPtr = parentPtr->childPtr; childPtr != NULL;
508 childPtr = childPtr->nextPtr) {
509 if ((childPtr->side == TOP) || (childPtr->side == BOTTOM)) {
510 frameWidth = cavityWidth;
511 frameHeight = Tk_ReqHeight(childPtr->tkwin) + childPtr->padY
512 + childPtr->doubleBw;
513 if (childPtr->flags & EXPAND) {
514 frameHeight += spareY;
515 numExpY--;
516 if (numExpY == 0) {
517 frameHeight += leftOverY;
518 }
519 }
520 cavityHeight -= frameHeight;
521 if (cavityHeight < 0) {
522 frameHeight += cavityHeight;
523 cavityHeight = 0;
524 }
525 frameX = cavityX;
526 if (childPtr->side == TOP) {
527 frameY = cavityY;
528 cavityY += frameHeight;
529 } else {
530 frameY = cavityY + cavityHeight;
531 }
532 } else {
533 frameHeight = cavityHeight;
534 frameWidth = Tk_ReqWidth(childPtr->tkwin) + childPtr->padX
535 + childPtr->doubleBw;
536 if (childPtr->flags & EXPAND) {
537 frameWidth += spareX;
538 numExpX--;
539 if (numExpX == 0) {
540 frameWidth += leftOverX;
541 }
542 }
543 cavityWidth -= frameWidth;
544 if (cavityWidth < 0) {
545 frameWidth += cavityWidth;
546 cavityWidth = 0;
547 }
548 frameY = cavityY;
549 if (childPtr->side == LEFT) {
550 frameX = cavityX;
551 cavityX += frameWidth;
552 } else {
553 frameX = cavityX + cavityWidth;
554 }
555 }
556
557 /*
558 * Now that we've got the size of the frame for the window,
559 * compute the window's actual size and location using the
560 * fill and frame factors.
561 */
562
563 width = Tk_ReqWidth(childPtr->tkwin) + childPtr->doubleBw;
564 if ((childPtr->flags & FILLX) || (width > frameWidth)) {
565 width = frameWidth;
566 }
567 height = Tk_ReqHeight(childPtr->tkwin) + childPtr->doubleBw;
568 if ((childPtr->flags & FILLY) || (height > frameHeight)) {
569 height = frameHeight;
570 }
571 switch (childPtr->anchorPoint) {
572 case TK_ANCHOR_N:
573 x = frameX + (frameWidth - width)/2;
574 y = frameY;
575 break;
576 case TK_ANCHOR_NE:
577 x = frameX + frameWidth - width;
578 y = frameY;
579 break;
580 case TK_ANCHOR_E:
581 x = frameX + frameWidth - width;
582 y = frameY + (frameHeight - height)/2;
583 break;
584 case TK_ANCHOR_SE:
585 x = frameX + frameWidth - width;
586 y = frameY + frameHeight - height;
587 break;
588 case TK_ANCHOR_S:
589 x = frameX + (frameWidth - width)/2;
590 y = frameY + frameHeight - height;
591 break;
592 case TK_ANCHOR_SW:
593 x = frameX;
594 y = frameY + frameHeight - height;
595 break;
596 case TK_ANCHOR_W:
597 x = frameX;
598 y = frameY + (frameHeight - height)/2;
599 break;
600 case TK_ANCHOR_NW:
601 x = frameX;
602 y = frameY;
603 break;
604 case TK_ANCHOR_CENTER:
605 x = frameX + (frameWidth - width)/2;
606 y = frameY + (frameHeight - height)/2;
607 break;
608 default:
609 panic("bad frame factor in ArrangePacking");
610 }
611 width -= childPtr->doubleBw;
612 height -= childPtr->doubleBw;
613
614 /*
615 * If the window is too small to be interesting then
616 * unmap it. Otherwise configure it and then make sure
617 * it's mapped.
618 */
619
620 if ((width <= 0) || (height <= 0)) {
621 Tk_UnmapWindow(childPtr->tkwin);
622 } else {
623 if ((x != Tk_X(childPtr->tkwin))
624 || (y != Tk_Y(childPtr->tkwin))
625 || (width != Tk_Width(childPtr->tkwin))
626 || (height != Tk_Height(childPtr->tkwin))) {
627 Tk_MoveResizeWindow(childPtr->tkwin, x, y,
628 (unsigned int) width, (unsigned int) height);
629 }
630 if (abort) {
631 goto done;
632 }
633 Tk_MapWindow(childPtr->tkwin);
634 }
635
636 /*
637 * Changes to the window's structure could cause almost anything
638 * to happen, including deleting the parent or child. If this
639 * happens, we'll be told to abort.
640 */
641
642 if (abort) {
643 goto done;
644 }
645 }
646
647 done:
648 parentPtr->abortPtr = NULL;
649 Tk_Release((ClientData) parentPtr);
650 }
651 \f
652 /*
653 *--------------------------------------------------------------
654 *
655 * GetPacker --
656 *
657 * This internal procedure is used to locate a Packer
658 * structure for a given window, creating one if one
659 * doesn't exist already.
660 *
661 * Results:
662 * The return value is a pointer to the Packer structure
663 * corresponding to tkwin.
664 *
665 * Side effects:
666 * A new packer structure may be created. If so, then
667 * a callback is set up to clean things up when the
668 * window is deleted.
669 *
670 *--------------------------------------------------------------
671 */
672
673 static Packer *
674 GetPacker(tkwin)
675 Tk_Window tkwin; /* Token for window for which
676 * packer structure is desired. */
677 {
678 register Packer *packPtr;
679 Tcl_HashEntry *hPtr;
680 int new;
681
682 if (!initialized) {
683 initialized = 1;
684 Tcl_InitHashTable(&packerHashTable, TCL_ONE_WORD_KEYS);
685 }
686
687 /*
688 * See if there's already packer for this window. If not,
689 * then create a new one.
690 */
691
692 hPtr = Tcl_CreateHashEntry(&packerHashTable, (char *) tkwin, &new);
693 if (!new) {
694 return (Packer *) Tcl_GetHashValue(hPtr);
695 }
696 packPtr = (Packer *) ckalloc(sizeof(Packer));
697 packPtr->tkwin = tkwin;
698 packPtr->parentPtr = NULL;
699 packPtr->nextPtr = NULL;
700 packPtr->childPtr = NULL;
701 packPtr->side = TOP;
702 packPtr->anchorPoint = TK_ANCHOR_CENTER;
703 packPtr->padX = packPtr->padY = 0;
704 packPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
705 packPtr->abortPtr = NULL;
706 packPtr->flags = 0;
707 Tcl_SetHashValue(hPtr, packPtr);
708 Tk_CreateEventHandler(tkwin, StructureNotifyMask,
709 PackStructureProc, (ClientData) packPtr);
710 return packPtr;
711 }
712 \f
713 /*
714 *--------------------------------------------------------------
715 *
716 * PackAfter --
717 *
718 * This procedure does most of the real work of adding
719 * one or more windows into the packing order for its parent.
720 *
721 * Results:
722 * A standard Tcl return value.
723 *
724 * Side effects:
725 * The geometry of the specified windows may change, both now and
726 * again in the future.
727 *
728 *--------------------------------------------------------------
729 */
730
731 static int
732 PackAfter(interp, prevPtr, parentPtr, argc, argv)
733 Tcl_Interp *interp; /* Interpreter for error reporting. */
734 Packer *prevPtr; /* Pack windows in argv just after this
735 * window; NULL means pack as first
736 * child of parentPtr. */
737 Packer *parentPtr; /* Parent in which to pack windows. */
738 int argc; /* Number of elements in argv. */
739 char **argv; /* Array of lists, each containing 2
740 * elements: window name and side
741 * against which to pack. */
742 {
743 register Packer *packPtr;
744 Tk_Window tkwin;
745 int length, optionCount;
746 char **options;
747 int index;
748 char c;
749
750 /*
751 * Iterate over all of the window specifiers, each consisting of
752 * two arguments. The first argument contains the window name and
753 * the additional arguments contain options such as "top" or
754 * "padx 20".
755 */
756
757 for ( ; argc > 0; argc -= 2, argv += 2, prevPtr = packPtr) {
758 if (argc < 2) {
759 Tcl_AppendResult(interp, "wrong # args: window \"",
760 argv[0], "\" should be followed by options",
761 (char *) NULL);
762 return TCL_ERROR;
763 }
764
765 /*
766 * Find the packer for the window to be packed, and make sure
767 * that the window in which it will be packed is its parent.
768 */
769
770 tkwin = Tk_NameToWindow(interp, argv[0], parentPtr->tkwin);
771 if (tkwin == NULL) {
772 return TCL_ERROR;
773 }
774 if (Tk_Parent(tkwin) != parentPtr->tkwin) {
775 Tcl_AppendResult(interp, "tried to pack \"",
776 argv[0], "\" in window that isn't its parent",
777 (char *) NULL);
778 return TCL_ERROR;
779 }
780 packPtr = GetPacker(tkwin);
781
782 /*
783 * Process options for this window.
784 */
785
786 if (Tcl_SplitList(interp, argv[1], &optionCount, &options) != TCL_OK) {
787 return TCL_ERROR;
788 }
789 packPtr->side = TOP;
790 packPtr->anchorPoint = TK_ANCHOR_CENTER;
791 packPtr->padX = packPtr->padY = 0;
792 packPtr->flags &= ~(FILLX|FILLY|EXPAND);
793 for (index = 0 ; index < optionCount; index++) {
794 char *curOpt = options[index];
795
796 c = curOpt[0];
797 length = strlen(curOpt);
798
799 if ((c == 't')
800 && (strncmp(curOpt, "top", length)) == 0) {
801 packPtr->side = TOP;
802 } else if ((c == 'b')
803 && (strncmp(curOpt, "bottom", length)) == 0) {
804 packPtr->side = BOTTOM;
805 } else if ((c == 'l')
806 && (strncmp(curOpt, "left", length)) == 0) {
807 packPtr->side = LEFT;
808 } else if ((c == 'r')
809 && (strncmp(curOpt, "right", length)) == 0) {
810 packPtr->side = RIGHT;
811 } else if ((c == 'e')
812 && (strncmp(curOpt, "expand", length)) == 0) {
813 packPtr->flags |= EXPAND;
814 } else if ((c == 'f')
815 && (strcmp(curOpt, "fill")) == 0) {
816 packPtr->flags |= FILLX|FILLY;
817 } else if ((length == 5) && (strcmp(curOpt, "fillx")) == 0) {
818 packPtr->flags |= FILLX;
819 } else if ((length == 5) && (strcmp(curOpt, "filly")) == 0) {
820 packPtr->flags |= FILLY;
821 } else if ((c == 'p') && (strcmp(curOpt, "padx")) == 0) {
822 if (optionCount < (index+2)) {
823 missingPad:
824 Tcl_AppendResult(interp, "wrong # args: \"", curOpt,
825 "\" option must be followed by count",
826 (char *) NULL);
827 goto error;
828 }
829 if ((Tcl_GetInt(interp, options[index+1], &packPtr->padX)
830 != TCL_OK) || (packPtr->padX < 0)) {
831 badPad:
832 Tcl_AppendResult(interp, "bad pad value \"",
833 options[index+1], "\": must be positive integer",
834 (char *) NULL);
835 goto error;
836 }
837 index++;
838 } else if ((c == 'p') && (strcmp(curOpt, "pady")) == 0) {
839 if (optionCount < (index+2)) {
840 goto missingPad;
841 }
842 if ((Tcl_GetInt(interp, options[index+1], &packPtr->padY)
843 != TCL_OK) || (packPtr->padY < 0)) {
844 goto badPad;
845 }
846 index++;
847 } else if ((c == 'f') && (length > 1)
848 && (strncmp(curOpt, "frame", length) == 0)) {
849 if (optionCount < (index+2)) {
850 Tcl_AppendResult(interp, "wrong # args: \"frame\" ",
851 "option must be followed by anchor point",
852 (char *) NULL);
853 goto error;
854 }
855 if (Tk_GetAnchor(interp, options[index+1],
856 &packPtr->anchorPoint) != TCL_OK) {
857 goto error;
858 }
859 index++;
860 } else {
861 Tcl_AppendResult(interp, "bad option \"", curOpt,
862 "\": should be top, bottom, left, right, ",
863 "expand, fill, fillx, filly, padx, pady, or frame",
864 (char *) NULL);
865 goto error;
866 }
867 }
868
869 if (packPtr != prevPtr) {
870
871 /*
872 * Unpack this window if it's currently packed.
873 */
874
875 if (packPtr->parentPtr != NULL) {
876 Unlink(packPtr);
877 }
878
879 /*
880 * Add the window in the correct place in its parent's
881 * packing order, then make sure that the window is
882 * managed by us.
883 */
884
885 packPtr->parentPtr = parentPtr;
886 if (prevPtr == NULL) {
887 packPtr->nextPtr = parentPtr->childPtr;
888 parentPtr->childPtr = packPtr;
889 } else {
890 packPtr->nextPtr = prevPtr->nextPtr;
891 prevPtr->nextPtr = packPtr;
892 }
893 Tk_ManageGeometry(tkwin, PackReqProc, (ClientData) packPtr);
894 }
895 ckfree((char *) options);
896 }
897
898 /*
899 * Arrange for the parent to be re-packed at the first
900 * idle moment.
901 */
902
903 if (parentPtr->abortPtr != NULL) {
904 *parentPtr->abortPtr = 1;
905 }
906 if (!(parentPtr->flags & REQUESTED_REPACK)) {
907 parentPtr->flags |= REQUESTED_REPACK;
908 Tk_DoWhenIdle(ArrangePacking, (ClientData) parentPtr);
909 }
910 return TCL_OK;
911
912 error:
913 ckfree((char *) options);
914 return TCL_ERROR;
915 }
916 \f
917 /*
918 *----------------------------------------------------------------------
919 *
920 * Unlink --
921 *
922 * Remove a packer from its parent's list of children.
923 *
924 * Results:
925 * None.
926 *
927 * Side effects:
928 * The parent will be scheduled for repacking.
929 *
930 *----------------------------------------------------------------------
931 */
932
933 static void
934 Unlink(packPtr)
935 register Packer *packPtr; /* Window to unlink. */
936 {
937 register Packer *parentPtr, *packPtr2;
938
939 parentPtr = packPtr->parentPtr;
940 if (parentPtr == NULL) {
941 return;
942 }
943 if (parentPtr->childPtr == packPtr) {
944 parentPtr->childPtr = packPtr->nextPtr;
945 } else {
946 for (packPtr2 = parentPtr->childPtr; ; packPtr2 = packPtr2->nextPtr) {
947 if (packPtr2 == NULL) {
948 panic("Unlink couldn't find previous window");
949 }
950 if (packPtr2->nextPtr == packPtr) {
951 packPtr2->nextPtr = packPtr->nextPtr;
952 break;
953 }
954 }
955 }
956 if (!(parentPtr->flags & REQUESTED_REPACK)) {
957 parentPtr->flags |= REQUESTED_REPACK;
958 Tk_DoWhenIdle(ArrangePacking, (ClientData) parentPtr);
959 }
960 if (parentPtr->abortPtr != NULL) {
961 *parentPtr->abortPtr = 1;
962 }
963
964 packPtr->parentPtr = NULL;
965 }
966 \f
967 /*
968 *----------------------------------------------------------------------
969 *
970 * DestroyPacker --
971 *
972 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
973 * to clean up the internal structure of a packer at a safe time
974 * (when no-one is using it anymore).
975 *
976 * Results:
977 * None.
978 *
979 * Side effects:
980 * Everything associated with the packer is freed up.
981 *
982 *----------------------------------------------------------------------
983 */
984
985 static void
986 DestroyPacker(clientData)
987 ClientData clientData; /* Info about packed window that
988 * is now dead. */
989 {
990 register Packer *packPtr = (Packer *) clientData;
991 ckfree((char *) packPtr);
992 }
993 \f
994 /*
995 *----------------------------------------------------------------------
996 *
997 * PackStructureProc --
998 *
999 * This procedure is invoked by the Tk event dispatcher in response
1000 * to StructureNotify events.
1001 *
1002 * Results:
1003 * None.
1004 *
1005 * Side effects:
1006 * If a window was just deleted, clean up all its packer-related
1007 * information. If it was just resized, repack its children, if
1008 * any.
1009 *
1010 *----------------------------------------------------------------------
1011 */
1012
1013 static void
1014 PackStructureProc(clientData, eventPtr)
1015 ClientData clientData; /* Our information about window
1016 * referred to by eventPtr. */
1017 XEvent *eventPtr; /* Describes what just happened. */
1018 {
1019 register Packer *packPtr = (Packer *) clientData;
1020 if (eventPtr->type == ConfigureNotify) {
1021 if ((packPtr->childPtr != NULL)
1022 && !(packPtr->flags & REQUESTED_REPACK)) {
1023 packPtr->flags |= REQUESTED_REPACK;
1024 Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
1025 }
1026 if (packPtr->doubleBw != 2*Tk_Changes(packPtr->tkwin)->border_width) {
1027 if ((packPtr->parentPtr != NULL)
1028 && !(packPtr->parentPtr->flags & REQUESTED_REPACK)) {
1029 packPtr->doubleBw = 2*Tk_Changes(packPtr->tkwin)->border_width;
1030 packPtr->parentPtr->flags |= REQUESTED_REPACK;
1031 Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr->parentPtr);
1032 }
1033 }
1034 } else if (eventPtr->type == DestroyNotify) {
1035 register Packer *packPtr2;
1036
1037 if (packPtr->parentPtr != NULL) {
1038 Unlink(packPtr);
1039 }
1040 for (packPtr2 = packPtr->childPtr; packPtr2 != NULL;
1041 packPtr2 = packPtr2->nextPtr) {
1042 packPtr2->parentPtr = NULL;
1043 packPtr2->nextPtr = NULL;
1044 }
1045 Tcl_DeleteHashEntry(Tcl_FindHashEntry(&packerHashTable,
1046 (char *) packPtr->tkwin));
1047 if (packPtr->flags & REQUESTED_REPACK) {
1048 Tk_CancelIdleCall(ArrangePacking, (ClientData) packPtr);
1049 }
1050 packPtr->tkwin = NULL;
1051 Tk_EventuallyFree((ClientData) packPtr, DestroyPacker);
1052 }
1053 }
Impressum, Datenschutz