|
|
1.1 root 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:
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:
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:
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:
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:
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:
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:
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:
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.