|
|
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.