1/* 2 * tkPlace.c -- 3 * 4 * This file contains code to implement a simple geometry manager for Tk 5 * based on absolute placement or "rubber-sheet" placement. 6 * 7 * Copyright (c) 1992-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 18/* 19 * Border modes for relative placement: 20 * 21 * BM_INSIDE: relative distances computed using area inside all 22 * borders of master window. 23 * BM_OUTSIDE: relative distances computed using outside area that 24 * includes all borders of master. 25 * BM_IGNORE: border issues are ignored: place relative to master's 26 * actual window size. 27 */ 28 29static char *borderModeStrings[] = { 30 "inside", "outside", "ignore", NULL 31}; 32 33typedef enum {BM_INSIDE, BM_OUTSIDE, BM_IGNORE} BorderMode; 34 35/* 36 * For each window whose geometry is managed by the placer there is a 37 * structure of the following type: 38 */ 39 40typedef struct Slave { 41 Tk_Window tkwin; /* Tk's token for window. */ 42 Tk_Window inTkwin; /* Token for the -in window. */ 43 struct Master *masterPtr; /* Pointer to information for window relative 44 * to which tkwin is placed. This isn't 45 * necessarily the logical parent of tkwin. 46 * NULL means the master was deleted or never 47 * assigned. */ 48 struct Slave *nextPtr; /* Next in list of windows placed relative to 49 * same master (NULL for end of list). */ 50 Tk_OptionTable optionTable; /* Table that defines configuration options 51 * available for this command. */ 52 /* 53 * Geometry information for window; where there are both relative and 54 * absolute values for the same attribute (e.g. x and relX) only one of 55 * them is actually used, depending on flags. 56 */ 57 58 int x, y; /* X and Y pixel coordinates for tkwin. */ 59 Tcl_Obj *xPtr, *yPtr; /* Tcl_Obj rep's of x, y coords, to keep pixel 60 * spec. information. */ 61 double relX, relY; /* X and Y coordinates relative to size of 62 * master. */ 63 int width, height; /* Absolute dimensions for tkwin. */ 64 Tcl_Obj *widthPtr; /* Tcl_Obj rep of width, to keep pixel 65 * spec. */ 66 Tcl_Obj *heightPtr; /* Tcl_Obj rep of height, to keep pixel 67 * spec. */ 68 double relWidth, relHeight; /* Dimensions for tkwin relative to size of 69 * master. */ 70 Tcl_Obj *relWidthPtr; 71 Tcl_Obj *relHeightPtr; 72 Tk_Anchor anchor; /* Which point on tkwin is placed at the given 73 * position. */ 74 BorderMode borderMode; /* How to treat borders of master window. */ 75 int flags; /* Various flags; see below for bit 76 * definitions. */ 77} Slave; 78 79/* 80 * Type masks for options: 81 */ 82 83#define IN_MASK 1 84 85static const Tk_OptionSpec optionSpecs[] = { 86 {TK_OPTION_ANCHOR, "-anchor", NULL, NULL, "nw", -1, 87 Tk_Offset(Slave, anchor), 0, 0, 0}, 88 {TK_OPTION_STRING_TABLE, "-bordermode", NULL, NULL, "inside", -1, 89 Tk_Offset(Slave, borderMode), 0, (ClientData) borderModeStrings, 0}, 90 {TK_OPTION_PIXELS, "-height", NULL, NULL, "", Tk_Offset(Slave, heightPtr), 91 Tk_Offset(Slave, height), TK_OPTION_NULL_OK, 0, 0}, 92 {TK_OPTION_WINDOW, "-in", NULL, NULL, "", -1, Tk_Offset(Slave, inTkwin), 93 0, 0, IN_MASK}, 94 {TK_OPTION_DOUBLE, "-relheight", NULL, NULL, "", 95 Tk_Offset(Slave, relHeightPtr), Tk_Offset(Slave, relHeight), 96 TK_OPTION_NULL_OK, 0, 0}, 97 {TK_OPTION_DOUBLE, "-relwidth", NULL, NULL, "", 98 Tk_Offset(Slave, relWidthPtr), Tk_Offset(Slave, relWidth), 99 TK_OPTION_NULL_OK, 0, 0}, 100 {TK_OPTION_DOUBLE, "-relx", NULL, NULL, "0", -1, 101 Tk_Offset(Slave, relX), 0, 0, 0}, 102 {TK_OPTION_DOUBLE, "-rely", NULL, NULL, "0", -1, 103 Tk_Offset(Slave, relY), 0, 0, 0}, 104 {TK_OPTION_PIXELS, "-width", NULL, NULL, "", Tk_Offset(Slave, widthPtr), 105 Tk_Offset(Slave, width), TK_OPTION_NULL_OK, 0, 0}, 106 {TK_OPTION_PIXELS, "-x", NULL, NULL, "0", Tk_Offset(Slave, xPtr), 107 Tk_Offset(Slave, x), TK_OPTION_NULL_OK, 0, 0}, 108 {TK_OPTION_PIXELS, "-y", NULL, NULL, "0", Tk_Offset(Slave, yPtr), 109 Tk_Offset(Slave, y), TK_OPTION_NULL_OK, 0, 0}, 110 {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} 111}; 112 113/* 114 * Flag definitions for Slave structures: 115 * 116 * CHILD_WIDTH - 1 means -width was specified; 117 * CHILD_REL_WIDTH - 1 means -relwidth was specified. 118 * CHILD_HEIGHT - 1 means -height was specified; 119 * CHILD_REL_HEIGHT - 1 means -relheight was specified. 120 */ 121 122#define CHILD_WIDTH 1 123#define CHILD_REL_WIDTH 2 124#define CHILD_HEIGHT 4 125#define CHILD_REL_HEIGHT 8 126 127/* 128 * For each master window that has a slave managed by the placer there is a 129 * structure of the following form: 130 */ 131 132typedef struct Master { 133 Tk_Window tkwin; /* Tk's token for master window. */ 134 struct Slave *slavePtr; /* First in linked list of slaves placed 135 * relative to this master. */ 136 int *abortPtr; /* If non-NULL, it means that there is a nested 137 * call to RecomputePlacement already working on 138 * this window. *abortPtr may be set to 1 to 139 * abort that nested call. This happens, for 140 * example, if tkwin or any of its slaves 141 * is deleted. */ 142 int flags; /* See below for bit definitions. */ 143} Master; 144 145/* 146 * Flag definitions for masters: 147 * 148 * PARENT_RECONFIG_PENDING - 1 means that a call to RecomputePlacement is 149 * already pending via a Do_When_Idle handler. 150 */ 151 152#define PARENT_RECONFIG_PENDING 1 153 154/* 155 * The following structure is the official type record for the placer: 156 */ 157 158static void PlaceRequestProc(ClientData clientData, 159 Tk_Window tkwin); 160static void PlaceLostSlaveProc(ClientData clientData, 161 Tk_Window tkwin); 162 163static const Tk_GeomMgr placerType = { 164 "place", /* name */ 165 PlaceRequestProc, /* requestProc */ 166 PlaceLostSlaveProc, /* lostSlaveProc */ 167}; 168 169/* 170 * Forward declarations for functions defined later in this file: 171 */ 172 173static void SlaveStructureProc(ClientData clientData, 174 XEvent *eventPtr); 175static int ConfigureSlave(Tcl_Interp *interp, Tk_Window tkwin, 176 Tk_OptionTable table, int objc, 177 Tcl_Obj *CONST objv[]); 178static int PlaceInfoCommand(Tcl_Interp *interp, Tk_Window tkwin); 179static Slave * CreateSlave(Tk_Window tkwin, Tk_OptionTable table); 180static void FreeSlave(Slave *slavePtr); 181static Slave * FindSlave(Tk_Window tkwin); 182static Master * CreateMaster(Tk_Window tkwin); 183static Master * FindMaster(Tk_Window tkwin); 184static void MasterStructureProc(ClientData clientData, 185 XEvent *eventPtr); 186static void RecomputePlacement(ClientData clientData); 187static void UnlinkSlave(Slave *slavePtr); 188 189/* 190 *-------------------------------------------------------------- 191 * 192 * Tk_PlaceObjCmd -- 193 * 194 * This function is invoked to process the "place" Tcl commands. See the 195 * user documentation for details on what it does. 196 * 197 * Results: 198 * A standard Tcl result. 199 * 200 * Side effects: 201 * See the user documentation. 202 * 203 *-------------------------------------------------------------- 204 */ 205 206int 207Tk_PlaceObjCmd( 208 ClientData clientData, /* NULL. */ 209 Tcl_Interp *interp, /* Current interpreter. */ 210 int objc, /* Number of arguments. */ 211 Tcl_Obj *CONST objv[]) /* Argument objects. */ 212{ 213 Tk_Window tkwin; 214 Slave *slavePtr; 215 char *string; 216 TkDisplay *dispPtr; 217 Tk_OptionTable optionTable; 218 static CONST char *optionStrings[] = { 219 "configure", "forget", "info", "slaves", NULL 220 }; 221 enum options { PLACE_CONFIGURE, PLACE_FORGET, PLACE_INFO, PLACE_SLAVES }; 222 int index; 223 224 if (objc < 3) { 225 Tcl_WrongNumArgs(interp, 1, objv, "option|pathName args"); 226 return TCL_ERROR; 227 } 228 229 /* 230 * Create the option table for this widget class. If it has already been 231 * created, the cached pointer will be returned. 232 */ 233 234 optionTable = Tk_CreateOptionTable(interp, optionSpecs); 235 236 /* 237 * Handle special shortcut where window name is first argument. 238 */ 239 240 string = Tcl_GetString(objv[1]); 241 if (string[0] == '.') { 242 tkwin = Tk_NameToWindow(interp, string, Tk_MainWindow(interp)); 243 if (tkwin == NULL) { 244 return TCL_ERROR; 245 } 246 247 /* 248 * Initialize, if that hasn't been done yet. 249 */ 250 251 dispPtr = ((TkWindow *) tkwin)->dispPtr; 252 if (!dispPtr->placeInit) { 253 Tcl_InitHashTable(&dispPtr->masterTable, TCL_ONE_WORD_KEYS); 254 Tcl_InitHashTable(&dispPtr->slaveTable, TCL_ONE_WORD_KEYS); 255 dispPtr->placeInit = 1; 256 } 257 258 return ConfigureSlave(interp, tkwin, optionTable, objc-2, objv+2); 259 } 260 261 /* 262 * Handle more general case of option followed by window name followed by 263 * possible additional arguments. 264 */ 265 266 tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), 267 Tk_MainWindow(interp)); 268 if (tkwin == NULL) { 269 return TCL_ERROR; 270 } 271 272 /* 273 * Initialize, if that hasn't been done yet. 274 */ 275 276 dispPtr = ((TkWindow *) tkwin)->dispPtr; 277 if (!dispPtr->placeInit) { 278 Tcl_InitHashTable(&dispPtr->masterTable, TCL_ONE_WORD_KEYS); 279 Tcl_InitHashTable(&dispPtr->slaveTable, TCL_ONE_WORD_KEYS); 280 dispPtr->placeInit = 1; 281 } 282 283 if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0, 284 &index) != TCL_OK) { 285 return TCL_ERROR; 286 } 287 288 switch ((enum options) index) { 289 case PLACE_CONFIGURE: 290 if (objc == 3 || objc == 4) { 291 Tcl_Obj *objPtr; 292 293 slavePtr = FindSlave(tkwin); 294 if (slavePtr == NULL) { 295 return TCL_OK; 296 } 297 objPtr = Tk_GetOptionInfo(interp, (char *) slavePtr, optionTable, 298 (objc == 4) ? objv[3] : NULL, tkwin); 299 if (objPtr == NULL) { 300 return TCL_ERROR; 301 } 302 Tcl_SetObjResult(interp, objPtr); 303 return TCL_OK; 304 } 305 return ConfigureSlave(interp, tkwin, optionTable, objc-3, objv+3); 306 307 case PLACE_FORGET: 308 if (objc != 3) { 309 Tcl_WrongNumArgs(interp, 2, objv, "pathName"); 310 return TCL_ERROR; 311 } 312 slavePtr = FindSlave(tkwin); 313 if (slavePtr == NULL) { 314 return TCL_OK; 315 } 316 if ((slavePtr->masterPtr != NULL) && 317 (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin))) { 318 Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin); 319 } 320 UnlinkSlave(slavePtr); 321 Tcl_DeleteHashEntry(Tcl_FindHashEntry(&dispPtr->slaveTable, 322 (char *) tkwin)); 323 Tk_DeleteEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc, 324 (ClientData) slavePtr); 325 Tk_ManageGeometry(tkwin, NULL, (ClientData) NULL); 326 Tk_UnmapWindow(tkwin); 327 FreeSlave(slavePtr); 328 break; 329 330 case PLACE_INFO: 331 if (objc != 3) { 332 Tcl_WrongNumArgs(interp, 2, objv, "pathName"); 333 return TCL_ERROR; 334 } 335 return PlaceInfoCommand(interp, tkwin); 336 337 case PLACE_SLAVES: { 338 Master *masterPtr; 339 340 if (objc != 3) { 341 Tcl_WrongNumArgs(interp, 2, objv, "pathName"); 342 return TCL_ERROR; 343 } 344 masterPtr = FindMaster(tkwin); 345 if (masterPtr != NULL) { 346 Tcl_Obj *listPtr = Tcl_NewObj(); 347 348 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; 349 slavePtr = slavePtr->nextPtr) { 350 Tcl_ListObjAppendElement(interp, listPtr, 351 Tcl_NewStringObj(Tk_PathName(slavePtr->tkwin),-1)); 352 } 353 Tcl_SetObjResult(interp, listPtr); 354 } 355 break; 356 } 357 } 358 359 return TCL_OK; 360} 361 362/* 363 *---------------------------------------------------------------------- 364 * 365 * CreateSlave -- 366 * 367 * Given a Tk_Window token, find the Slave structure corresponding to 368 * that token, creating a new one if necessary. 369 * 370 * Results: 371 * Pointer to the Slave structure. 372 * 373 * Side effects: 374 * A new Slave structure may be created. 375 * 376 *---------------------------------------------------------------------- 377 */ 378 379static Slave * 380CreateSlave( 381 Tk_Window tkwin, /* Token for desired slave. */ 382 Tk_OptionTable table) 383{ 384 Tcl_HashEntry *hPtr; 385 register Slave *slavePtr; 386 int isNew; 387 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr; 388 389 hPtr = Tcl_CreateHashEntry(&dispPtr->slaveTable, (char *) tkwin, &isNew); 390 if (!isNew) { 391 return (Slave *) Tcl_GetHashValue(hPtr); 392 } 393 394 /* 395 * No preexisting slave structure for that window, so make a new one and 396 * populate it with some default values. 397 */ 398 399 slavePtr = (Slave *) ckalloc(sizeof(Slave)); 400 memset(slavePtr, 0, sizeof(Slave)); 401 slavePtr->tkwin = tkwin; 402 slavePtr->inTkwin = None; 403 slavePtr->anchor = TK_ANCHOR_NW; 404 slavePtr->borderMode = BM_INSIDE; 405 slavePtr->optionTable = table; 406 Tcl_SetHashValue(hPtr, slavePtr); 407 Tk_CreateEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc, 408 (ClientData) slavePtr); 409 return slavePtr; 410} 411 412/* 413 *---------------------------------------------------------------------- 414 * 415 * FreeSlave -- 416 * 417 * Frees the resources held by a Slave structure. 418 * 419 * Results: 420 * None 421 * 422 * Side effects: 423 * Memory are freed. 424 * 425 *---------------------------------------------------------------------- 426 */ 427 428static void 429FreeSlave( 430 Slave *slavePtr) 431{ 432 Tk_FreeConfigOptions((char *) slavePtr, slavePtr->optionTable, 433 slavePtr->tkwin); 434 ckfree((char *) slavePtr); 435} 436 437/* 438 *---------------------------------------------------------------------- 439 * 440 * FindSlave -- 441 * 442 * Given a Tk_Window token, find the Slave structure corresponding to 443 * that token. This is purely a lookup function; it will not create a 444 * record if one does not yet exist. 445 * 446 * Results: 447 * Pointer to Slave structure; NULL if none exists. 448 * 449 * Side effects: 450 * None. 451 * 452 *---------------------------------------------------------------------- 453 */ 454 455static Slave * 456FindSlave( 457 Tk_Window tkwin) /* Token for desired slave. */ 458{ 459 Tcl_HashEntry *hPtr; 460 register Slave *slavePtr; 461 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr; 462 463 hPtr = Tcl_FindHashEntry(&dispPtr->slaveTable, (char *) tkwin); 464 if (hPtr == NULL) { 465 return NULL; 466 } 467 slavePtr = (Slave *) Tcl_GetHashValue(hPtr); 468 return slavePtr; 469} 470 471/* 472 *---------------------------------------------------------------------- 473 * 474 * UnlinkSlave -- 475 * 476 * This function removes a slave window from the chain of slaves in its 477 * master. 478 * 479 * Results: 480 * None. 481 * 482 * Side effects: 483 * The slave list of slavePtr's master changes. 484 * 485 *---------------------------------------------------------------------- 486 */ 487 488static void 489UnlinkSlave( 490 Slave *slavePtr) /* Slave structure to be unlinked. */ 491{ 492 register Master *masterPtr; 493 register Slave *prevPtr; 494 495 masterPtr = slavePtr->masterPtr; 496 if (masterPtr == NULL) { 497 return; 498 } 499 if (masterPtr->slavePtr == slavePtr) { 500 masterPtr->slavePtr = slavePtr->nextPtr; 501 } else { 502 for (prevPtr = masterPtr->slavePtr; ; prevPtr = prevPtr->nextPtr) { 503 if (prevPtr == NULL) { 504 Tcl_Panic("UnlinkSlave couldn't find slave to unlink"); 505 } 506 if (prevPtr->nextPtr == slavePtr) { 507 prevPtr->nextPtr = slavePtr->nextPtr; 508 break; 509 } 510 } 511 } 512 513 if (masterPtr->abortPtr != NULL) { 514 *masterPtr->abortPtr = 1; 515 } 516 slavePtr->masterPtr = NULL; 517} 518 519/* 520 *---------------------------------------------------------------------- 521 * 522 * CreateMaster -- 523 * 524 * Given a Tk_Window token, find the Master structure corresponding to 525 * that token, creating a new one if necessary. 526 * 527 * Results: 528 * Pointer to the Master structure. 529 * 530 * Side effects: 531 * A new Master structure may be created. 532 * 533 *---------------------------------------------------------------------- 534 */ 535 536static Master * 537CreateMaster( 538 Tk_Window tkwin) /* Token for desired master. */ 539{ 540 Tcl_HashEntry *hPtr; 541 register Master *masterPtr; 542 int isNew; 543 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr; 544 545 hPtr = Tcl_CreateHashEntry(&dispPtr->masterTable, (char *) tkwin, &isNew); 546 if (isNew) { 547 masterPtr = (Master *) ckalloc(sizeof(Master)); 548 masterPtr->tkwin = tkwin; 549 masterPtr->slavePtr = NULL; 550 masterPtr->abortPtr = NULL; 551 masterPtr->flags = 0; 552 Tcl_SetHashValue(hPtr, masterPtr); 553 Tk_CreateEventHandler(masterPtr->tkwin, StructureNotifyMask, 554 MasterStructureProc, (ClientData) masterPtr); 555 } else { 556 masterPtr = (Master *) Tcl_GetHashValue(hPtr); 557 } 558 return masterPtr; 559} 560 561/* 562 *---------------------------------------------------------------------- 563 * 564 * FindMaster -- 565 * 566 * Given a Tk_Window token, find the Master structure corresponding to 567 * that token. This is simply a lookup function; a new record will not be 568 * created if one does not already exist. 569 * 570 * Results: 571 * Pointer to the Master structure; NULL if one does not exist for the 572 * given Tk_Window token. 573 * 574 * Side effects: 575 * None. 576 * 577 *---------------------------------------------------------------------- 578 */ 579 580static Master * 581FindMaster( 582 Tk_Window tkwin) /* Token for desired master. */ 583{ 584 Tcl_HashEntry *hPtr; 585 register Master *masterPtr; 586 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr; 587 588 hPtr = Tcl_FindHashEntry(&dispPtr->masterTable, (char *) tkwin); 589 if (hPtr == NULL) { 590 return NULL; 591 } 592 masterPtr = (Master *) Tcl_GetHashValue(hPtr); 593 return masterPtr; 594} 595 596/* 597 *---------------------------------------------------------------------- 598 * 599 * ConfigureSlave -- 600 * 601 * This function is called to process an argv/argc list to reconfigure 602 * the placement of a window. 603 * 604 * Results: 605 * A standard Tcl result. If an error occurs then a message is left in 606 * the interp's result. 607 * 608 * Side effects: 609 * Information in slavePtr may change, and slavePtr's master is scheduled 610 * for reconfiguration. 611 * 612 *---------------------------------------------------------------------- 613 */ 614 615static int 616ConfigureSlave( 617 Tcl_Interp *interp, /* Used for error reporting. */ 618 Tk_Window tkwin, /* Token for the window to manipulate. */ 619 Tk_OptionTable table, /* Token for option table. */ 620 int objc, /* Number of config arguments. */ 621 Tcl_Obj *CONST objv[]) /* Object values for arguments. */ 622{ 623 register Master *masterPtr; 624 Tk_SavedOptions savedOptions; 625 int mask; 626 Slave *slavePtr; 627 Tk_Window masterWin = (Tk_Window) NULL; 628 629 if (Tk_TopWinHierarchy(tkwin)) { 630 Tcl_AppendResult(interp, "can't use placer on top-level window \"", 631 Tk_PathName(tkwin), "\"; use wm command instead", NULL); 632 return TCL_ERROR; 633 } 634 635 slavePtr = CreateSlave(tkwin, table); 636 637 if (Tk_SetOptions(interp, (char *) slavePtr, table, objc, objv, 638 slavePtr->tkwin, &savedOptions, &mask) != TCL_OK) { 639 goto error; 640 } 641 642 /* 643 * Set slave flags. First clear the field, then add bits as needed. 644 */ 645 646 slavePtr->flags = 0; 647 if (slavePtr->heightPtr) { 648 slavePtr->flags |= CHILD_HEIGHT; 649 } 650 651 if (slavePtr->relHeightPtr) { 652 slavePtr->flags |= CHILD_REL_HEIGHT; 653 } 654 655 if (slavePtr->relWidthPtr) { 656 slavePtr->flags |= CHILD_REL_WIDTH; 657 } 658 659 if (slavePtr->widthPtr) { 660 slavePtr->flags |= CHILD_WIDTH; 661 } 662 663 if (((mask & IN_MASK) == 0) && (slavePtr->masterPtr != NULL)) { 664 /* 665 * If no -in option was passed and the slave is already placed then 666 * just recompute the placement. 667 */ 668 669 masterPtr = slavePtr->masterPtr; 670 goto scheduleLayout; 671 } else if (mask & IN_MASK) { 672 /* -in changed */ 673 Tk_Window tkwin; 674 Tk_Window ancestor; 675 676 tkwin = slavePtr->inTkwin; 677 678 /* 679 * Make sure that the new master is either the logical parent of the 680 * slave or a descendant of that window, and that the master and slave 681 * aren't the same. 682 */ 683 684 for (ancestor = tkwin; ; ancestor = Tk_Parent(ancestor)) { 685 if (ancestor == Tk_Parent(slavePtr->tkwin)) { 686 break; 687 } 688 if (Tk_TopWinHierarchy(ancestor)) { 689 Tcl_AppendResult(interp, "can't place ", 690 Tk_PathName(slavePtr->tkwin), " relative to ", 691 Tk_PathName(tkwin), NULL); 692 goto error; 693 } 694 } 695 if (slavePtr->tkwin == tkwin) { 696 Tcl_AppendResult(interp, "can't place ", 697 Tk_PathName(slavePtr->tkwin), " relative to itself", 698 NULL); 699 goto error; 700 } 701 if ((slavePtr->masterPtr != NULL) 702 && (slavePtr->masterPtr->tkwin == tkwin)) { 703 /* 704 * Re-using same old master. Nothing to do. 705 */ 706 707 masterPtr = slavePtr->masterPtr; 708 goto scheduleLayout; 709 } 710 if ((slavePtr->masterPtr != NULL) && 711 (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin))) { 712 Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin); 713 } 714 UnlinkSlave(slavePtr); 715 masterWin = tkwin; 716 } 717 718 /* 719 * If there's no master specified for this slave, use its Tk_Parent. 720 */ 721 722 if (masterWin == NULL) { 723 masterWin = Tk_Parent(slavePtr->tkwin); 724 slavePtr->inTkwin = masterWin; 725 } 726 727 /* 728 * Manage the slave window in this master. 729 */ 730 731 masterPtr = CreateMaster(masterWin); 732 slavePtr->masterPtr = masterPtr; 733 slavePtr->nextPtr = masterPtr->slavePtr; 734 masterPtr->slavePtr = slavePtr; 735 Tk_ManageGeometry(slavePtr->tkwin, &placerType, (ClientData) slavePtr); 736 737 /* 738 * Arrange for the master to be re-arranged at the first idle moment. 739 */ 740 741 scheduleLayout: 742 Tk_FreeSavedOptions(&savedOptions); 743 744 if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) { 745 masterPtr->flags |= PARENT_RECONFIG_PENDING; 746 Tcl_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr); 747 } 748 return TCL_OK; 749 750 /* 751 * Error while processing some option, cleanup and return. 752 */ 753 754 error: 755 Tk_RestoreSavedOptions(&savedOptions); 756 return TCL_ERROR; 757} 758 759/* 760 *---------------------------------------------------------------------- 761 * 762 * PlaceInfoCommand -- 763 * 764 * Implementation of the [place info] subcommand. See the user 765 * documentation for information on what it does. 766 * 767 * Results: 768 * Standard Tcl result. 769 * 770 * Side effects: 771 * If the given tkwin is managed by the placer, this function will put 772 * information about that placement in the interp's result. 773 * 774 *---------------------------------------------------------------------- 775 */ 776 777static int 778PlaceInfoCommand( 779 Tcl_Interp *interp, /* Interp into which to place result. */ 780 Tk_Window tkwin) /* Token for the window to get info on. */ 781{ 782 char buffer[32 + TCL_INTEGER_SPACE]; 783 Slave *slavePtr; 784 785 slavePtr = FindSlave(tkwin); 786 if (slavePtr == NULL) { 787 return TCL_OK; 788 } 789 if (slavePtr->masterPtr != NULL) { 790 Tcl_AppendElement(interp, "-in"); 791 Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin)); 792 } 793 sprintf(buffer, " -x %d", slavePtr->x); 794 Tcl_AppendResult(interp, buffer, NULL); 795 sprintf(buffer, " -relx %.4g", slavePtr->relX); 796 Tcl_AppendResult(interp, buffer, NULL); 797 sprintf(buffer, " -y %d", slavePtr->y); 798 Tcl_AppendResult(interp, buffer, NULL); 799 sprintf(buffer, " -rely %.4g", slavePtr->relY); 800 Tcl_AppendResult(interp, buffer, NULL); 801 if (slavePtr->flags & CHILD_WIDTH) { 802 sprintf(buffer, " -width %d", slavePtr->width); 803 Tcl_AppendResult(interp, buffer, NULL); 804 } else { 805 Tcl_AppendResult(interp, " -width {}", NULL); 806 } 807 if (slavePtr->flags & CHILD_REL_WIDTH) { 808 sprintf(buffer, " -relwidth %.4g", slavePtr->relWidth); 809 Tcl_AppendResult(interp, buffer, NULL); 810 } else { 811 Tcl_AppendResult(interp, " -relwidth {}", NULL); 812 } 813 if (slavePtr->flags & CHILD_HEIGHT) { 814 sprintf(buffer, " -height %d", slavePtr->height); 815 Tcl_AppendResult(interp, buffer, NULL); 816 } else { 817 Tcl_AppendResult(interp, " -height {}", NULL); 818 } 819 if (slavePtr->flags & CHILD_REL_HEIGHT) { 820 sprintf(buffer, " -relheight %.4g", slavePtr->relHeight); 821 Tcl_AppendResult(interp, buffer, NULL); 822 } else { 823 Tcl_AppendResult(interp, " -relheight {}", NULL); 824 } 825 826 Tcl_AppendElement(interp, "-anchor"); 827 Tcl_AppendElement(interp, Tk_NameOfAnchor(slavePtr->anchor)); 828 Tcl_AppendElement(interp, "-bordermode"); 829 Tcl_AppendElement(interp, borderModeStrings[slavePtr->borderMode]); 830 return TCL_OK; 831} 832 833/* 834 *---------------------------------------------------------------------- 835 * 836 * RecomputePlacement -- 837 * 838 * This function is called as a when-idle handler. It recomputes the 839 * geometries of all the slaves of a given master. 840 * 841 * Results: 842 * None. 843 * 844 * Side effects: 845 * Windows may change size or shape. 846 * 847 *---------------------------------------------------------------------- 848 */ 849 850static void 851RecomputePlacement( 852 ClientData clientData) /* Pointer to Master record. */ 853{ 854 register Master *masterPtr = (Master *) clientData; 855 register Slave *slavePtr; 856 int x, y, width, height, tmp; 857 int masterWidth, masterHeight, masterX, masterY; 858 double x1, y1, x2, y2; 859 860 int abort; /* May get set to non-zero to abort this 861 * placement operation. */ 862 863 masterPtr->flags &= ~PARENT_RECONFIG_PENDING; 864 865 /* 866 * Abort any nested call to RecomputePlacement for this window, since 867 * we'll do everything necessary here, and set up so this call 868 * can be aborted if necessary. 869 */ 870 871 if (masterPtr->abortPtr != NULL) { 872 *masterPtr->abortPtr = 1; 873 } 874 masterPtr->abortPtr = &abort; 875 abort = 0; 876 Tcl_Preserve((ClientData) masterPtr); 877 878 /* 879 * Iterate over all the slaves for the master. Each slave's geometry can 880 * be computed independently of the other slaves. Changes to the window's 881 * structure could cause almost anything to happen, including deleting the 882 * parent or child. If this happens, we'll be told to abort. 883 */ 884 885 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL && !abort; 886 slavePtr = slavePtr->nextPtr) { 887 /* 888 * Step 1: compute size and borderwidth of master, taking into account 889 * desired border mode. 890 */ 891 892 masterX = masterY = 0; 893 masterWidth = Tk_Width(masterPtr->tkwin); 894 masterHeight = Tk_Height(masterPtr->tkwin); 895 if (slavePtr->borderMode == BM_INSIDE) { 896 masterX = Tk_InternalBorderLeft(masterPtr->tkwin); 897 masterY = Tk_InternalBorderTop(masterPtr->tkwin); 898 masterWidth -= masterX + Tk_InternalBorderRight(masterPtr->tkwin); 899 masterHeight -= masterY + 900 Tk_InternalBorderBottom(masterPtr->tkwin); 901 } else if (slavePtr->borderMode == BM_OUTSIDE) { 902 masterX = masterY = -Tk_Changes(masterPtr->tkwin)->border_width; 903 masterWidth -= 2 * masterX; 904 masterHeight -= 2 * masterY; 905 } 906 907 /* 908 * Step 2: compute size of slave (outside dimensions including border) 909 * and location of anchor point within master. 910 */ 911 912 x1 = slavePtr->x + masterX + (slavePtr->relX*masterWidth); 913 x = (int) (x1 + ((x1 > 0) ? 0.5 : -0.5)); 914 y1 = slavePtr->y + masterY + (slavePtr->relY*masterHeight); 915 y = (int) (y1 + ((y1 > 0) ? 0.5 : -0.5)); 916 if (slavePtr->flags & (CHILD_WIDTH|CHILD_REL_WIDTH)) { 917 width = 0; 918 if (slavePtr->flags & CHILD_WIDTH) { 919 width += slavePtr->width; 920 } 921 if (slavePtr->flags & CHILD_REL_WIDTH) { 922 /* 923 * The code below is a bit tricky. In order to round correctly 924 * when both relX and relWidth are specified, compute the 925 * location of the right edge and round that, then compute 926 * width. If we compute the width and round it, rounding 927 * errors in relX and relWidth accumulate. 928 */ 929 930 x2 = x1 + (slavePtr->relWidth*masterWidth); 931 tmp = (int) (x2 + ((x2 > 0) ? 0.5 : -0.5)); 932 width += tmp - x; 933 } 934 } else { 935 width = Tk_ReqWidth(slavePtr->tkwin) 936 + 2*Tk_Changes(slavePtr->tkwin)->border_width; 937 } 938 if (slavePtr->flags & (CHILD_HEIGHT|CHILD_REL_HEIGHT)) { 939 height = 0; 940 if (slavePtr->flags & CHILD_HEIGHT) { 941 height += slavePtr->height; 942 } 943 if (slavePtr->flags & CHILD_REL_HEIGHT) { 944 /* 945 * See note above for rounding errors in width computation. 946 */ 947 948 y2 = y1 + (slavePtr->relHeight*masterHeight); 949 tmp = (int) (y2 + ((y2 > 0) ? 0.5 : -0.5)); 950 height += tmp - y; 951 } 952 } else { 953 height = Tk_ReqHeight(slavePtr->tkwin) 954 + 2*Tk_Changes(slavePtr->tkwin)->border_width; 955 } 956 957 /* 958 * Step 3: adjust the x and y positions so that the desired anchor 959 * point on the slave appears at that position. Also adjust for the 960 * border mode and master's border. 961 */ 962 963 switch (slavePtr->anchor) { 964 case TK_ANCHOR_N: 965 x -= width/2; 966 break; 967 case TK_ANCHOR_NE: 968 x -= width; 969 break; 970 case TK_ANCHOR_E: 971 x -= width; 972 y -= height/2; 973 break; 974 case TK_ANCHOR_SE: 975 x -= width; 976 y -= height; 977 break; 978 case TK_ANCHOR_S: 979 x -= width/2; 980 y -= height; 981 break; 982 case TK_ANCHOR_SW: 983 y -= height; 984 break; 985 case TK_ANCHOR_W: 986 y -= height/2; 987 break; 988 case TK_ANCHOR_NW: 989 break; 990 case TK_ANCHOR_CENTER: 991 x -= width/2; 992 y -= height/2; 993 break; 994 } 995 996 /* 997 * Step 4: adjust width and height again to reflect inside dimensions 998 * of window rather than outside. Also make sure that the width and 999 * height aren't zero. 1000 */ 1001 1002 width -= 2*Tk_Changes(slavePtr->tkwin)->border_width; 1003 height -= 2*Tk_Changes(slavePtr->tkwin)->border_width; 1004 if (width <= 0) { 1005 width = 1; 1006 } 1007 if (height <= 0) { 1008 height = 1; 1009 } 1010 1011 /* 1012 * Step 5: reconfigure the window and map it if needed. If the slave 1013 * is a child of the master, we do this ourselves. If the slave isn't 1014 * a child of the master, let Tk_MaintainGeometry do the work (it will 1015 * re-adjust things as relevant windows map, unmap, and move). 1016 */ 1017 1018 if (masterPtr->tkwin == Tk_Parent(slavePtr->tkwin)) { 1019 if ((x != Tk_X(slavePtr->tkwin)) 1020 || (y != Tk_Y(slavePtr->tkwin)) 1021 || (width != Tk_Width(slavePtr->tkwin)) 1022 || (height != Tk_Height(slavePtr->tkwin))) { 1023 Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height); 1024 } 1025 if (abort) { 1026 break; 1027 } 1028 1029 /* 1030 * Don't map the slave unless the master is mapped: the slave will 1031 * get mapped later, when the master is mapped. 1032 */ 1033 1034 if (Tk_IsMapped(masterPtr->tkwin)) { 1035 Tk_MapWindow(slavePtr->tkwin); 1036 } 1037 } else { 1038 if ((width <= 0) || (height <= 0)) { 1039 Tk_UnmaintainGeometry(slavePtr->tkwin, masterPtr->tkwin); 1040 Tk_UnmapWindow(slavePtr->tkwin); 1041 } else { 1042 Tk_MaintainGeometry(slavePtr->tkwin, masterPtr->tkwin, 1043 x, y, width, height); 1044 } 1045 } 1046 } 1047 1048 masterPtr->abortPtr = NULL; 1049 Tcl_Release((ClientData) masterPtr); 1050} 1051 1052/* 1053 *---------------------------------------------------------------------- 1054 * 1055 * MasterStructureProc -- 1056 * 1057 * This function is invoked by the Tk event handler when StructureNotify 1058 * events occur for a master window. 1059 * 1060 * Results: 1061 * None. 1062 * 1063 * Side effects: 1064 * Structures get cleaned up if the window was deleted. If the window was 1065 * resized then slave geometries get recomputed. 1066 * 1067 *---------------------------------------------------------------------- 1068 */ 1069 1070static void 1071MasterStructureProc( 1072 ClientData clientData, /* Pointer to Master structure for window 1073 * referred to by eventPtr. */ 1074 XEvent *eventPtr) /* Describes what just happened. */ 1075{ 1076 register Master *masterPtr = (Master *) clientData; 1077 register Slave *slavePtr, *nextPtr; 1078 TkDisplay *dispPtr = ((TkWindow *) masterPtr->tkwin)->dispPtr; 1079 1080 if (eventPtr->type == ConfigureNotify) { 1081 if ((masterPtr->slavePtr != NULL) 1082 && !(masterPtr->flags & PARENT_RECONFIG_PENDING)) { 1083 masterPtr->flags |= PARENT_RECONFIG_PENDING; 1084 Tcl_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr); 1085 } 1086 } else if (eventPtr->type == DestroyNotify) { 1087 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; 1088 slavePtr = nextPtr) { 1089 slavePtr->masterPtr = NULL; 1090 nextPtr = slavePtr->nextPtr; 1091 slavePtr->nextPtr = NULL; 1092 } 1093 Tcl_DeleteHashEntry(Tcl_FindHashEntry(&dispPtr->masterTable, 1094 (char *) masterPtr->tkwin)); 1095 if (masterPtr->flags & PARENT_RECONFIG_PENDING) { 1096 Tcl_CancelIdleCall(RecomputePlacement, (ClientData) masterPtr); 1097 } 1098 masterPtr->tkwin = NULL; 1099 if (masterPtr->abortPtr != NULL) { 1100 *masterPtr->abortPtr = 1; 1101 } 1102 Tcl_EventuallyFree((ClientData) masterPtr, TCL_DYNAMIC); 1103 } else if (eventPtr->type == MapNotify) { 1104 /* 1105 * When a master gets mapped, must redo the geometry computation so 1106 * that all of its slaves get remapped. 1107 */ 1108 1109 if ((masterPtr->slavePtr != NULL) 1110 && !(masterPtr->flags & PARENT_RECONFIG_PENDING)) { 1111 masterPtr->flags |= PARENT_RECONFIG_PENDING; 1112 Tcl_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr); 1113 } 1114 } else if (eventPtr->type == UnmapNotify) { 1115 /* 1116 * Unmap all of the slaves when the master gets unmapped, so that they 1117 * don't keep redisplaying themselves. 1118 */ 1119 1120 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; 1121 slavePtr = slavePtr->nextPtr) { 1122 Tk_UnmapWindow(slavePtr->tkwin); 1123 } 1124 } 1125} 1126 1127/* 1128 *---------------------------------------------------------------------- 1129 * 1130 * SlaveStructureProc -- 1131 * 1132 * This function is invoked by the Tk event handler when StructureNotify 1133 * events occur for a slave window. 1134 * 1135 * Results: 1136 * None. 1137 * 1138 * Side effects: 1139 * Structures get cleaned up if the window was deleted. 1140 * 1141 *---------------------------------------------------------------------- 1142 */ 1143 1144static void 1145SlaveStructureProc( 1146 ClientData clientData, /* Pointer to Slave structure for window 1147 * referred to by eventPtr. */ 1148 XEvent *eventPtr) /* Describes what just happened. */ 1149{ 1150 register Slave *slavePtr = (Slave *) clientData; 1151 TkDisplay *dispPtr = ((TkWindow *) slavePtr->tkwin)->dispPtr; 1152 1153 if (eventPtr->type == DestroyNotify) { 1154 if (slavePtr->masterPtr != NULL) { 1155 UnlinkSlave(slavePtr); 1156 } 1157 Tcl_DeleteHashEntry(Tcl_FindHashEntry(&dispPtr->slaveTable, 1158 (char *) slavePtr->tkwin)); 1159 FreeSlave(slavePtr); 1160 } 1161} 1162 1163/* 1164 *---------------------------------------------------------------------- 1165 * 1166 * PlaceRequestProc -- 1167 * 1168 * This function is invoked by Tk whenever a slave managed by us changes 1169 * its requested geometry. 1170 * 1171 * Results: 1172 * None. 1173 * 1174 * Side effects: 1175 * The window will get relayed out, if its requested size has anything to 1176 * do with its actual size. 1177 * 1178 *---------------------------------------------------------------------- 1179 */ 1180 1181 /* ARGSUSED */ 1182static void 1183PlaceRequestProc( 1184 ClientData clientData, /* Pointer to our record for slave. */ 1185 Tk_Window tkwin) /* Window that changed its desired size. */ 1186{ 1187 Slave *slavePtr = (Slave *) clientData; 1188 Master *masterPtr; 1189 1190 if (((slavePtr->flags & (CHILD_WIDTH|CHILD_REL_WIDTH)) != 0) 1191 && ((slavePtr->flags & (CHILD_HEIGHT|CHILD_REL_HEIGHT)) != 0)) { 1192 return; 1193 } 1194 masterPtr = slavePtr->masterPtr; 1195 if (masterPtr == NULL) { 1196 return; 1197 } 1198 if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) { 1199 masterPtr->flags |= PARENT_RECONFIG_PENDING; 1200 Tcl_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr); 1201 } 1202} 1203 1204/* 1205 *-------------------------------------------------------------- 1206 * 1207 * PlaceLostSlaveProc -- 1208 * 1209 * This function is invoked by Tk whenever some other geometry claims 1210 * control over a slave that used to be managed by us. 1211 * 1212 * Results: 1213 * None. 1214 * 1215 * Side effects: 1216 * Forgets all placer-related information about the slave. 1217 * 1218 *-------------------------------------------------------------- 1219 */ 1220 1221 /* ARGSUSED */ 1222static void 1223PlaceLostSlaveProc( 1224 ClientData clientData, /* Slave structure for slave window that was 1225 * stolen away. */ 1226 Tk_Window tkwin) /* Tk's handle for the slave window. */ 1227{ 1228 register Slave *slavePtr = (Slave *) clientData; 1229 TkDisplay *dispPtr = ((TkWindow *) slavePtr->tkwin)->dispPtr; 1230 1231 if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) { 1232 Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin); 1233 } 1234 Tk_UnmapWindow(tkwin); 1235 UnlinkSlave(slavePtr); 1236 Tcl_DeleteHashEntry(Tcl_FindHashEntry(&dispPtr->slaveTable, 1237 (char *) tkwin)); 1238 Tk_DeleteEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc, 1239 (ClientData) slavePtr); 1240 FreeSlave(slavePtr); 1241} 1242 1243/* 1244 * Local Variables: 1245 * mode: c 1246 * c-basic-offset: 4 1247 * fill-column: 78 1248 * End: 1249 */ 1250