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