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