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