1/* 2 * tkGrid.c -- 3 * 4 * Grid based geometry manager. 5 * 6 * Copyright (c) 1996-1997 by Sun Microsystems, Inc. 7 * 8 * See the file "license.terms" for information on usage and redistribution of 9 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 10 * 11 * RCS: @(#) $Id$ 12 */ 13 14#include "tkInt.h" 15 16/* 17 * Convenience Macros 18 */ 19 20#ifdef MAX 21# undef MAX 22#endif 23#define MAX(x,y) ((x) > (y) ? (x) : (y)) 24 25#define COLUMN (1) /* Working on column offsets. */ 26#define ROW (2) /* Working on row offsets. */ 27 28#define CHECK_ONLY (1) /* Check max slot constraint. */ 29#define CHECK_SPACE (2) /* Alloc more space, don't change max. */ 30 31/* 32 * Pre-allocate enough row and column slots for "typical" sized tables this 33 * value should be chosen so by the time the extra malloc's are required, the 34 * layout calculations overwehlm them. [A "slot" contains information for 35 * either a row or column, depending upon the context.] 36 */ 37 38#define TYPICAL_SIZE 25 /* (Arbitrary guess) */ 39#define PREALLOC 10 /* Extra slots to allocate. */ 40 41/* 42 * Pre-allocate room for uniform groups during layout. 43 */ 44 45#define UNIFORM_PREALLOC 10 46 47/* 48 * Data structures are allocated dynamically to support arbitrary sized 49 * tables. However, the space is proportional to the highest numbered slot 50 * with some non-default property. This limit is used to head off mistakes and 51 * denial of service attacks by limiting the amount of storage required. 52 */ 53 54#define MAX_ELEMENT 10000 55 56/* 57 * Special characters to support relative layouts. 58 */ 59 60#define REL_SKIP 'x' /* Skip this column. */ 61#define REL_HORIZ '-' /* Extend previous widget horizontally. */ 62#define REL_VERT '^' /* Extend widget from row above. */ 63 64/* 65 * Default value for 'grid anchor'. 66 */ 67 68#define GRID_DEFAULT_ANCHOR TK_ANCHOR_NW 69 70/* 71 * Structure to hold information for grid masters. A slot is either a row or 72 * column. 73 */ 74 75typedef struct SlotInfo { 76 int minSize; /* The minimum size of this slot (in pixels). 77 * It is set via the rowconfigure or 78 * columnconfigure commands. */ 79 int weight; /* The resize weight of this slot. (0) means 80 * this slot doesn't resize. Extra space in 81 * the layout is given distributed among slots 82 * inproportion to their weights. */ 83 int pad; /* Extra padding, in pixels, required for this 84 * slot. This amount is "added" to the largest 85 * slave in the slot. */ 86 Tk_Uid uniform; /* Value of -uniform option. It is used to 87 * group slots that should have the same 88 * size. */ 89 int offset; /* This is a cached value used for 90 * introspection. It is the pixel offset of 91 * the right or bottom edge of this slot from 92 * the beginning of the layout. */ 93 int temp; /* This is a temporary value used for 94 * calculating adjusted weights when shrinking 95 * the layout below its nominal size. */ 96} SlotInfo; 97 98/* 99 * Structure to hold information during layout calculations. There is one of 100 * these for each slot, an array for each of the rows or columns. 101 */ 102 103typedef struct GridLayout { 104 struct Gridder *binNextPtr; /* The next slave window in this bin. Each bin 105 * contains a list of all slaves whose spans 106 * are >1 and whose right edges fall in this 107 * slot. */ 108 int minSize; /* Minimum size needed for this slot, in 109 * pixels. This is the space required to hold 110 * any slaves contained entirely in this slot, 111 * adjusted for any slot constrants, such as 112 * size or padding. */ 113 int pad; /* Padding needed for this slot */ 114 int weight; /* Slot weight, controls resizing. */ 115 Tk_Uid uniform; /* Value of -uniform option. It is used to 116 * group slots that should have the same 117 * size. */ 118 int minOffset; /* The minimum offset, in pixels, from the 119 * beginning of the layout to the bottom/right 120 * edge of the slot calculated from top/left 121 * to bottom/right. */ 122 int maxOffset; /* The maximum offset, in pixels, from the 123 * beginning of the layout to the bottom/right 124 * edge of the slot calculated from 125 * bottom/right to top/left. */ 126} GridLayout; 127 128/* 129 * Keep one of these for each geometry master. 130 */ 131 132typedef struct { 133 SlotInfo *columnPtr; /* Pointer to array of column constraints. */ 134 SlotInfo *rowPtr; /* Pointer to array of row constraints. */ 135 int columnEnd; /* The last column occupied by any slave. */ 136 int columnMax; /* The number of columns with constraints. */ 137 int columnSpace; /* The number of slots currently allocated for 138 * column constraints. */ 139 int rowEnd; /* The last row occupied by any slave. */ 140 int rowMax; /* The number of rows with constraints. */ 141 int rowSpace; /* The number of slots currently allocated for 142 * row constraints. */ 143 int startX; /* Pixel offset of this layout within its 144 * master. */ 145 int startY; /* Pixel offset of this layout within its 146 * master. */ 147 Tk_Anchor anchor; /* Value of anchor option: specifies where a 148 * grid without weight should be placed. */ 149} GridMaster; 150 151/* 152 * For each window that the grid cares about (either because the window is 153 * managed by the grid or because the window has slaves that are managed by 154 * the grid), there is a structure of the following type: 155 */ 156 157typedef struct Gridder { 158 Tk_Window tkwin; /* Tk token for window. NULL means that the 159 * window has been deleted, but the gridder 160 * hasn't had a chance to clean up yet because 161 * the structure is still in use. */ 162 struct Gridder *masterPtr; /* Master window within which this window is 163 * managed (NULL means this window isn't 164 * managed by the gridder). */ 165 struct Gridder *nextPtr; /* Next window managed within same master. 166 * List order doesn't matter. */ 167 struct Gridder *slavePtr; /* First in list of slaves managed inside this 168 * window (NULL means no grid slaves). */ 169 GridMaster *masterDataPtr; /* Additional data for geometry master. */ 170 int column, row; /* Location in the grid (starting from 171 * zero). */ 172 int numCols, numRows; /* Number of columns or rows this slave spans. 173 * Should be at least 1. */ 174 int padX, padY; /* Total additional pixels to leave around the 175 * window. Some is of this space is on each 176 * side. This is space *outside* the window: 177 * we'll allocate extra space in frame but 178 * won't enlarge window). */ 179 int padLeft, padTop; /* The part of padX or padY to use on the left 180 * or top of the widget, respectively. By 181 * default, this is half of padX or padY. */ 182 int iPadX, iPadY; /* Total extra pixels to allocate inside the 183 * window (half this amount will appear on 184 * each side). */ 185 int sticky; /* which sides of its cavity this window 186 * sticks to. See below for definitions */ 187 int doubleBw; /* Twice the window's last known border width. 188 * If this changes, the window must be 189 * re-arranged within its master. */ 190 int *abortPtr; /* If non-NULL, it means that there is a 191 * nested call to ArrangeGrid already working 192 * on this window. *abortPtr may be set to 1 193 * to abort that nested call. This happens, 194 * for example, if tkwin or any of its slaves 195 * is deleted. */ 196 int flags; /* Miscellaneous flags; see below for 197 * definitions. */ 198 199 /* 200 * These fields are used temporarily for layout calculations only. 201 */ 202 203 struct Gridder *binNextPtr; /* Link to next span>1 slave in this bin. */ 204 int size; /* Nominal size (width or height) in pixels of 205 * the slave. This includes the padding. */ 206} Gridder; 207 208/* 209 * Flag values for "sticky"ness. The 16 combinations subsume the packer's 210 * notion of anchor and fill. 211 * 212 * STICK_NORTH This window sticks to the top of its cavity. 213 * STICK_EAST This window sticks to the right edge of its 214 * cavity. 215 * STICK_SOUTH This window sticks to the bottom of its cavity. 216 * STICK_WEST This window sticks to the left edge of its 217 * cavity. 218 */ 219 220#define STICK_NORTH 1 221#define STICK_EAST 2 222#define STICK_SOUTH 4 223#define STICK_WEST 8 224 225 226/* 227 * Structure to gather information about uniform groups during layout. 228 */ 229 230typedef struct UniformGroup { 231 Tk_Uid group; 232 int minSize; 233} UniformGroup; 234 235/* 236 * Flag values for Grid structures: 237 * 238 * REQUESTED_RELAYOUT 1 means a Tcl_DoWhenIdle request has already 239 * been made to re-arrange all the slaves of this 240 * window. 241 * DONT_PROPAGATE 1 means don't set this window's requested 242 * size. 0 means if this window is a master then 243 * Tk will set its requested size to fit the 244 * needs of its slaves. 245 */ 246 247#define REQUESTED_RELAYOUT 1 248#define DONT_PROPAGATE 2 249 250/* 251 * Prototypes for procedures used only in this file: 252 */ 253 254static void AdjustForSticky(Gridder *slavePtr, int *xPtr, 255 int *yPtr, int *widthPtr, int *heightPtr); 256static int AdjustOffsets(int width, int elements, 257 SlotInfo *slotPtr); 258static void ArrangeGrid(ClientData clientData); 259static int CheckSlotData(Gridder *masterPtr, int slot, 260 int slotType, int checkOnly); 261static int ConfigureSlaves(Tcl_Interp *interp, Tk_Window tkwin, 262 int objc, Tcl_Obj *CONST objv[]); 263static void DestroyGrid(char *memPtr); 264static Gridder * GetGrid(Tk_Window tkwin); 265static int GridAnchorCommand(Tk_Window tkwin, Tcl_Interp *interp, 266 int objc, Tcl_Obj *CONST objv[]); 267static int GridBboxCommand(Tk_Window tkwin, Tcl_Interp *interp, 268 int objc, Tcl_Obj *CONST objv[]); 269static int GridForgetRemoveCommand(Tk_Window tkwin, 270 Tcl_Interp *interp, int objc, 271 Tcl_Obj *CONST objv[]); 272static int GridInfoCommand(Tk_Window tkwin, Tcl_Interp *interp, 273 int objc, Tcl_Obj *CONST objv[]); 274static int GridLocationCommand(Tk_Window tkwin, 275 Tcl_Interp *interp, int objc, 276 Tcl_Obj *CONST objv[]); 277static int GridPropagateCommand(Tk_Window tkwin, 278 Tcl_Interp *interp, int objc, 279 Tcl_Obj *CONST objv[]); 280static int GridRowColumnConfigureCommand(Tk_Window tkwin, 281 Tcl_Interp *interp, int objc, 282 Tcl_Obj *CONST objv[]); 283static int GridSizeCommand(Tk_Window tkwin, Tcl_Interp *interp, 284 int objc, Tcl_Obj *CONST objv[]); 285static int GridSlavesCommand(Tk_Window tkwin, Tcl_Interp *interp, 286 int objc, Tcl_Obj *CONST objv[]); 287static void GridStructureProc(ClientData clientData, 288 XEvent *eventPtr); 289static void GridLostSlaveProc(ClientData clientData, 290 Tk_Window tkwin); 291static void GridReqProc(ClientData clientData, Tk_Window tkwin); 292static void InitMasterData(Gridder *masterPtr); 293static Tcl_Obj * NewPairObj(int, int); 294static Tcl_Obj * NewQuadObj(int, int, int, int); 295static int ResolveConstraints(Gridder *gridPtr, int rowOrColumn, 296 int maxOffset); 297static void SetGridSize(Gridder *gridPtr); 298static int SetSlaveColumn(Tcl_Interp *interp, Gridder *slavePtr, 299 int column, int numCols); 300static int SetSlaveRow(Tcl_Interp *interp, Gridder *slavePtr, 301 int row, int numRows); 302static void StickyToString(int flags, char *result); 303static int StringToSticky(char *string); 304static void Unlink(Gridder *gridPtr); 305 306static const Tk_GeomMgr gridMgrType = { 307 "grid", /* name */ 308 GridReqProc, /* requestProc */ 309 GridLostSlaveProc, /* lostSlaveProc */ 310}; 311 312/* 313 *---------------------------------------------------------------------- 314 * 315 * Tk_GridCmd -- 316 * 317 * This procedure is invoked to process the "grid" Tcl command. See the 318 * user documentation for details on what it does. 319 * 320 * Results: 321 * A standard Tcl result. 322 * 323 * Side effects: 324 * See the user documentation. 325 * 326 *---------------------------------------------------------------------- 327 */ 328 329int 330Tk_GridObjCmd( 331 ClientData clientData, /* Main window associated with interpreter. */ 332 Tcl_Interp *interp, /* Current interpreter. */ 333 int objc, /* Number of arguments. */ 334 Tcl_Obj *CONST objv[]) /* Argument objects. */ 335{ 336 Tk_Window tkwin = (Tk_Window) clientData; 337 static CONST char *optionStrings[] = { 338 "anchor", "bbox", "columnconfigure", "configure", 339 "forget", "info", "location", "propagate", "remove", 340 "rowconfigure", "size", "slaves", NULL 341 }; 342 enum options { 343 GRID_ANCHOR, GRID_BBOX, GRID_COLUMNCONFIGURE, GRID_CONFIGURE, 344 GRID_FORGET, GRID_INFO, GRID_LOCATION, GRID_PROPAGATE, GRID_REMOVE, 345 GRID_ROWCONFIGURE, GRID_SIZE, GRID_SLAVES 346 }; 347 int index; 348 349 if (objc >= 2) { 350 char *argv1 = Tcl_GetString(objv[1]); 351 352 if ((argv1[0] == '.') || (argv1[0] == REL_SKIP) || 353 (argv1[0] == REL_VERT)) { 354 return ConfigureSlaves(interp, tkwin, objc-1, objv+1); 355 } 356 } 357 if (objc < 3) { 358 Tcl_WrongNumArgs(interp, 1, objv, "option arg ?arg ...?"); 359 return TCL_ERROR; 360 } 361 362 if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0, 363 &index) != TCL_OK) { 364 return TCL_ERROR; 365 } 366 367 switch ((enum options) index) { 368 case GRID_ANCHOR: 369 return GridAnchorCommand(tkwin, interp, objc, objv); 370 case GRID_BBOX: 371 return GridBboxCommand(tkwin, interp, objc, objv); 372 case GRID_CONFIGURE: 373 return ConfigureSlaves(interp, tkwin, objc-2, objv+2); 374 case GRID_FORGET: 375 case GRID_REMOVE: 376 return GridForgetRemoveCommand(tkwin, interp, objc, objv); 377 case GRID_INFO: 378 return GridInfoCommand(tkwin, interp, objc, objv); 379 case GRID_LOCATION: 380 return GridLocationCommand(tkwin, interp, objc, objv); 381 case GRID_PROPAGATE: 382 return GridPropagateCommand(tkwin, interp, objc, objv); 383 case GRID_SIZE: 384 return GridSizeCommand(tkwin, interp, objc, objv); 385 case GRID_SLAVES: 386 return GridSlavesCommand(tkwin, interp, objc, objv); 387 388 /* 389 * Sample argument combinations: 390 * grid columnconfigure <master> <index> -option 391 * grid columnconfigure <master> <index> -option value -option value 392 * grid rowconfigure <master> <index> 393 * grid rowconfigure <master> <index> -option 394 * grid rowconfigure <master> <index> -option value -option value. 395 */ 396 397 case GRID_COLUMNCONFIGURE: 398 case GRID_ROWCONFIGURE: 399 return GridRowColumnConfigureCommand(tkwin, interp, objc, objv); 400 } 401 402 /* This should not happen */ 403 Tcl_SetResult(interp, "Internal error in grid.", TCL_STATIC); 404 return TCL_ERROR; 405} 406 407/* 408 *---------------------------------------------------------------------- 409 * 410 * GridAnchorCommand -- 411 * 412 * Implementation of the [grid anchor] subcommand. See the user 413 * documentation for details on what it does. 414 * 415 * Results: 416 * Standard Tcl result. 417 * 418 * Side effects: 419 * May recompute grid geometry. 420 * 421 *---------------------------------------------------------------------- 422 */ 423 424static int 425GridAnchorCommand( 426 Tk_Window tkwin, /* Main window of the application. */ 427 Tcl_Interp *interp, /* Current interpreter. */ 428 int objc, /* Number of arguments. */ 429 Tcl_Obj *CONST objv[]) /* Argument objects. */ 430{ 431 Tk_Window master; 432 Gridder *masterPtr; 433 GridMaster *gridPtr; 434 Tk_Anchor old; 435 436 if (objc > 4) { 437 Tcl_WrongNumArgs(interp, 2, objv, "window ?anchor?"); 438 return TCL_ERROR; 439 } 440 441 if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) { 442 return TCL_ERROR; 443 } 444 masterPtr = GetGrid(master); 445 446 if (objc == 3) { 447 gridPtr = masterPtr->masterDataPtr; 448 Tcl_SetResult(interp, (char *) Tk_NameOfAnchor(gridPtr == NULL ? 449 GRID_DEFAULT_ANCHOR : gridPtr->anchor), TCL_VOLATILE); 450 return TCL_OK; 451 } 452 453 InitMasterData(masterPtr); 454 gridPtr = masterPtr->masterDataPtr; 455 old = gridPtr->anchor; 456 if (Tk_GetAnchorFromObj(interp, objv[3], &gridPtr->anchor) != TCL_OK) { 457 return TCL_ERROR; 458 } 459 460 /* 461 * Only request a relayout if the anchor changes. 462 */ 463 464 if (old != gridPtr->anchor) { 465 if (masterPtr->abortPtr != NULL) { 466 *masterPtr->abortPtr = 1; 467 } 468 if (!(masterPtr->flags & REQUESTED_RELAYOUT)) { 469 masterPtr->flags |= REQUESTED_RELAYOUT; 470 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr); 471 } 472 } 473 return TCL_OK; 474} 475 476/* 477 *---------------------------------------------------------------------- 478 * 479 * GridBboxCommand -- 480 * 481 * Implementation of the [grid bbox] subcommand. 482 * 483 * Results: 484 * Standard Tcl result. 485 * 486 * Side effects: 487 * Places bounding box information in the interp's result field. 488 * 489 *---------------------------------------------------------------------- 490 */ 491 492static int 493GridBboxCommand( 494 Tk_Window tkwin, /* Main window of the application. */ 495 Tcl_Interp *interp, /* Current interpreter. */ 496 int objc, /* Number of arguments. */ 497 Tcl_Obj *CONST objv[]) /* Argument objects. */ 498{ 499 Tk_Window master; 500 Gridder *masterPtr; /* master grid record */ 501 GridMaster *gridPtr; /* pointer to grid data */ 502 int row, column; /* origin for bounding box */ 503 int row2, column2; /* end of bounding box */ 504 int endX, endY; /* last column/row in the layout */ 505 int x=0, y=0; /* starting pixels for this bounding box */ 506 int width, height; /* size of the bounding box */ 507 508 if (objc!=3 && objc != 5 && objc != 7) { 509 Tcl_WrongNumArgs(interp, 2, objv, "master ?column row ?column row??"); 510 return TCL_ERROR; 511 } 512 513 if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) { 514 return TCL_ERROR; 515 } 516 masterPtr = GetGrid(master); 517 518 if (objc >= 5) { 519 if (Tcl_GetIntFromObj(interp, objv[3], &column) != TCL_OK) { 520 return TCL_ERROR; 521 } 522 if (Tcl_GetIntFromObj(interp, objv[4], &row) != TCL_OK) { 523 return TCL_ERROR; 524 } 525 column2 = column; 526 row2 = row; 527 } 528 529 if (objc == 7) { 530 if (Tcl_GetIntFromObj(interp, objv[5], &column2) != TCL_OK) { 531 return TCL_ERROR; 532 } 533 if (Tcl_GetIntFromObj(interp, objv[6], &row2) != TCL_OK) { 534 return TCL_ERROR; 535 } 536 } 537 538 gridPtr = masterPtr->masterDataPtr; 539 if (gridPtr == NULL) { 540 Tcl_SetObjResult(interp, NewQuadObj(0, 0, 0, 0)); 541 return TCL_OK; 542 } 543 544 SetGridSize(masterPtr); 545 endX = MAX(gridPtr->columnEnd, gridPtr->columnMax); 546 endY = MAX(gridPtr->rowEnd, gridPtr->rowMax); 547 548 if ((endX == 0) || (endY == 0)) { 549 Tcl_SetObjResult(interp, NewQuadObj(0, 0, 0, 0)); 550 return TCL_OK; 551 } 552 if (objc == 3) { 553 row = 0; 554 column = 0; 555 row2 = endY; 556 column2 = endX; 557 } 558 559 if (column > column2) { 560 int temp = column; 561 562 column = column2; 563 column2 = temp; 564 } 565 if (row > row2) { 566 int temp = row; 567 568 row = row2; 569 row2 = temp; 570 } 571 572 if (column > 0 && column < endX) { 573 x = gridPtr->columnPtr[column-1].offset; 574 } else if (column > 0) { 575 x = gridPtr->columnPtr[endX-1].offset; 576 } 577 578 if (row > 0 && row < endY) { 579 y = gridPtr->rowPtr[row-1].offset; 580 } else if (row > 0) { 581 y = gridPtr->rowPtr[endY-1].offset; 582 } 583 584 if (column2 < 0) { 585 width = 0; 586 } else if (column2 >= endX) { 587 width = gridPtr->columnPtr[endX-1].offset - x; 588 } else { 589 width = gridPtr->columnPtr[column2].offset - x; 590 } 591 592 if (row2 < 0) { 593 height = 0; 594 } else if (row2 >= endY) { 595 height = gridPtr->rowPtr[endY-1].offset - y; 596 } else { 597 height = gridPtr->rowPtr[row2].offset - y; 598 } 599 600 Tcl_SetObjResult(interp, NewQuadObj( 601 x + gridPtr->startX, y + gridPtr->startY, width, height)); 602 return TCL_OK; 603} 604 605/* 606 *---------------------------------------------------------------------- 607 * 608 * GridForgetRemoveCommand -- 609 * 610 * Implementation of the [grid forget]/[grid remove] subcommands. See the 611 * user documentation for details on what these do. 612 * 613 * Results: 614 * Standard Tcl result. 615 * 616 * Side effects: 617 * Removes a window from a grid layout. 618 * 619 *---------------------------------------------------------------------- 620 */ 621 622static int 623GridForgetRemoveCommand( 624 Tk_Window tkwin, /* Main window of the application. */ 625 Tcl_Interp *interp, /* Current interpreter. */ 626 int objc, /* Number of arguments. */ 627 Tcl_Obj *CONST objv[]) /* Argument objects. */ 628{ 629 Tk_Window slave; 630 Gridder *slavePtr; 631 int i; 632 char *string = Tcl_GetString(objv[1]); 633 char c = string[0]; 634 635 for (i = 2; i < objc; i++) { 636 if (TkGetWindowFromObj(interp, tkwin, objv[i], &slave) != TCL_OK) { 637 return TCL_ERROR; 638 } 639 640 slavePtr = GetGrid(slave); 641 if (slavePtr->masterPtr != NULL) { 642 /* 643 * For "forget", reset all the settings to their defaults 644 */ 645 646 if (c == 'f') { 647 slavePtr->column = -1; 648 slavePtr->row = -1; 649 slavePtr->numCols = 1; 650 slavePtr->numRows = 1; 651 slavePtr->padX = 0; 652 slavePtr->padY = 0; 653 slavePtr->padLeft = 0; 654 slavePtr->padTop = 0; 655 slavePtr->iPadX = 0; 656 slavePtr->iPadY = 0; 657 slavePtr->doubleBw = 2*Tk_Changes(tkwin)->border_width; 658 if (slavePtr->flags & REQUESTED_RELAYOUT) { 659 Tcl_CancelIdleCall(ArrangeGrid, (ClientData) slavePtr); 660 } 661 slavePtr->flags = 0; 662 slavePtr->sticky = 0; 663 } 664 Tk_ManageGeometry(slave, NULL, (ClientData) NULL); 665 if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) { 666 Tk_UnmaintainGeometry(slavePtr->tkwin, 667 slavePtr->masterPtr->tkwin); 668 } 669 Unlink(slavePtr); 670 Tk_UnmapWindow(slavePtr->tkwin); 671 } 672 } 673 return TCL_OK; 674} 675 676/* 677 *---------------------------------------------------------------------- 678 * 679 * GridInfoCommand -- 680 * 681 * Implementation of the [grid info] subcommand. See the user 682 * documentation for details on what it does. 683 * 684 * Results: 685 * Standard Tcl result. 686 * 687 * Side effects: 688 * Puts gridding information in the interpreter's result. 689 * 690 *---------------------------------------------------------------------- 691 */ 692 693static int 694GridInfoCommand( 695 Tk_Window tkwin, /* Main window of the application. */ 696 Tcl_Interp *interp, /* Current interpreter. */ 697 int objc, /* Number of arguments. */ 698 Tcl_Obj *CONST objv[]) /* Argument objects. */ 699{ 700 register Gridder *slavePtr; 701 Tk_Window slave; 702 char buffer[64 + TCL_INTEGER_SPACE * 4]; 703 704 if (objc != 3) { 705 Tcl_WrongNumArgs(interp, 2, objv, "window"); 706 return TCL_ERROR; 707 } 708 if (TkGetWindowFromObj(interp, tkwin, objv[2], &slave) != TCL_OK) { 709 return TCL_ERROR; 710 } 711 slavePtr = GetGrid(slave); 712 if (slavePtr->masterPtr == NULL) { 713 Tcl_ResetResult(interp); 714 return TCL_OK; 715 } 716 717 Tcl_AppendElement(interp, "-in"); 718 Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin)); 719 sprintf(buffer, " -column %d -row %d -columnspan %d -rowspan %d", 720 slavePtr->column, slavePtr->row, 721 slavePtr->numCols, slavePtr->numRows); 722 Tcl_AppendResult(interp, buffer, NULL); 723 TkPrintPadAmount(interp, "ipadx", slavePtr->iPadX/2, slavePtr->iPadX); 724 TkPrintPadAmount(interp, "ipady", slavePtr->iPadY/2, slavePtr->iPadY); 725 TkPrintPadAmount(interp, "padx", slavePtr->padLeft, slavePtr->padX); 726 TkPrintPadAmount(interp, "pady", slavePtr->padTop, slavePtr->padY); 727 StickyToString(slavePtr->sticky, buffer); 728 Tcl_AppendResult(interp, " -sticky ", buffer, NULL); 729 return TCL_OK; 730} 731 732/* 733 *---------------------------------------------------------------------- 734 * 735 * GridLocationCommand -- 736 * 737 * Implementation of the [grid location] subcommand. See the user 738 * documentation for details on what it does. 739 * 740 * Results: 741 * Standard Tcl result. 742 * 743 * Side effects: 744 * Puts location information in the interpreter's result field. 745 * 746 *---------------------------------------------------------------------- 747 */ 748 749static int 750GridLocationCommand( 751 Tk_Window tkwin, /* Main window of the application. */ 752 Tcl_Interp *interp, /* Current interpreter. */ 753 int objc, /* Number of arguments. */ 754 Tcl_Obj *CONST objv[]) /* Argument objects. */ 755{ 756 Tk_Window master; 757 Gridder *masterPtr; /* Master grid record. */ 758 GridMaster *gridPtr; /* Pointer to grid data. */ 759 register SlotInfo *slotPtr; 760 int x, y; /* Offset in pixels, from edge of master. */ 761 int i, j; /* Corresponding column and row indeces. */ 762 int endX, endY; /* End of grid. */ 763 764 if (objc != 5) { 765 Tcl_WrongNumArgs(interp, 2, objv, "master x y"); 766 return TCL_ERROR; 767 } 768 769 if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) { 770 return TCL_ERROR; 771 } 772 773 if (Tk_GetPixelsFromObj(interp, master, objv[3], &x) != TCL_OK) { 774 return TCL_ERROR; 775 } 776 if (Tk_GetPixelsFromObj(interp, master, objv[4], &y) != TCL_OK) { 777 return TCL_ERROR; 778 } 779 780 masterPtr = GetGrid(master); 781 if (masterPtr->masterDataPtr == NULL) { 782 Tcl_SetObjResult(interp, NewPairObj(-1, -1)); 783 return TCL_OK; 784 } 785 gridPtr = masterPtr->masterDataPtr; 786 787 /* 788 * Update any pending requests. This is not always the steady state value, 789 * as more configure events could be in the pipeline, but its as close as 790 * its easy to get. 791 */ 792 793 while (masterPtr->flags & REQUESTED_RELAYOUT) { 794 Tcl_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr); 795 ArrangeGrid((ClientData) masterPtr); 796 } 797 SetGridSize(masterPtr); 798 endX = MAX(gridPtr->columnEnd, gridPtr->columnMax); 799 endY = MAX(gridPtr->rowEnd, gridPtr->rowMax); 800 801 slotPtr = masterPtr->masterDataPtr->columnPtr; 802 if (x < masterPtr->masterDataPtr->startX) { 803 i = -1; 804 } else { 805 x -= masterPtr->masterDataPtr->startX; 806 for (i = 0; slotPtr[i].offset < x && i < endX; i++) { 807 /* null body */ 808 } 809 } 810 811 slotPtr = masterPtr->masterDataPtr->rowPtr; 812 if (y < masterPtr->masterDataPtr->startY) { 813 j = -1; 814 } else { 815 y -= masterPtr->masterDataPtr->startY; 816 for (j = 0; slotPtr[j].offset < y && j < endY; j++) { 817 /* null body */ 818 } 819 } 820 821 Tcl_SetObjResult(interp, NewPairObj(i, j)); 822 return TCL_OK; 823} 824 825/* 826 *---------------------------------------------------------------------- 827 * 828 * GridPropagateCommand -- 829 * 830 * Implementation of the [grid propagate] subcommand. See the user 831 * documentation for details on what it does. 832 * 833 * Results: 834 * Standard Tcl result. 835 * 836 * Side effects: 837 * May alter geometry propagation for a widget. 838 * 839 *---------------------------------------------------------------------- 840 */ 841 842static int 843GridPropagateCommand( 844 Tk_Window tkwin, /* Main window of the application. */ 845 Tcl_Interp *interp, /* Current interpreter. */ 846 int objc, /* Number of arguments. */ 847 Tcl_Obj *CONST objv[]) /* Argument objects. */ 848{ 849 Tk_Window master; 850 Gridder *masterPtr; 851 int propagate, old; 852 853 if (objc > 4) { 854 Tcl_WrongNumArgs(interp, 2, objv, "window ?boolean?"); 855 return TCL_ERROR; 856 } 857 858 if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) { 859 return TCL_ERROR; 860 } 861 masterPtr = GetGrid(master); 862 if (objc == 3) { 863 Tcl_SetObjResult(interp, 864 Tcl_NewBooleanObj(!(masterPtr->flags & DONT_PROPAGATE))); 865 return TCL_OK; 866 } 867 if (Tcl_GetBooleanFromObj(interp, objv[3], &propagate) != TCL_OK) { 868 return TCL_ERROR; 869 } 870 871 /* 872 * Only request a relayout if the propagation bit changes. 873 */ 874 875 old = !(masterPtr->flags & DONT_PROPAGATE); 876 if (propagate != old) { 877 if (propagate) { 878 masterPtr->flags &= ~DONT_PROPAGATE; 879 } else { 880 masterPtr->flags |= DONT_PROPAGATE; 881 } 882 883 /* 884 * Re-arrange the master to allow new geometry information to 885 * propagate upwards to the master's master. 886 */ 887 888 if (masterPtr->abortPtr != NULL) { 889 *masterPtr->abortPtr = 1; 890 } 891 if (!(masterPtr->flags & REQUESTED_RELAYOUT)) { 892 masterPtr->flags |= REQUESTED_RELAYOUT; 893 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr); 894 } 895 } 896 return TCL_OK; 897} 898 899/* 900 *---------------------------------------------------------------------- 901 * 902 * GridRowColumnConfigureCommand -- 903 * 904 * Implementation of the [grid rowconfigure] and [grid columnconfigure] 905 * subcommands. See the user documentation for details on what these do. 906 * 907 * Results: 908 * Standard Tcl result. 909 * 910 * Side effects: 911 * Depends on arguments; see user documentation. 912 * 913 *---------------------------------------------------------------------- 914 */ 915 916static int 917GridRowColumnConfigureCommand( 918 Tk_Window tkwin, /* Main window of the application. */ 919 Tcl_Interp *interp, /* Current interpreter. */ 920 int objc, /* Number of arguments. */ 921 Tcl_Obj *CONST objv[]) /* Argument objects. */ 922{ 923 Tk_Window master, slave; 924 Gridder *masterPtr, *slavePtr; 925 SlotInfo *slotPtr = NULL; 926 int slot; /* the column or row number */ 927 int slotType; /* COLUMN or ROW */ 928 int size; /* the configuration value */ 929 int lObjc; /* Number of items in index list */ 930 Tcl_Obj **lObjv; /* array of indices */ 931 int ok; /* temporary TCL result code */ 932 int i, j, first, last; 933 char *string; 934 static CONST char *optionStrings[] = { 935 "-minsize", "-pad", "-uniform", "-weight", NULL 936 }; 937 enum options { 938 ROWCOL_MINSIZE, ROWCOL_PAD, ROWCOL_UNIFORM, ROWCOL_WEIGHT 939 }; 940 int index; 941 Tcl_Obj *listCopy; 942 943 if (((objc % 2 != 0) && (objc > 6)) || (objc < 4)) { 944 Tcl_WrongNumArgs(interp, 2, objv, "master index ?-option value...?"); 945 return TCL_ERROR; 946 } 947 948 if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) { 949 return TCL_ERROR; 950 } 951 952 listCopy = Tcl_DuplicateObj(objv[3]); 953 Tcl_IncrRefCount(listCopy); 954 if (Tcl_ListObjGetElements(interp, listCopy, &lObjc, &lObjv) != TCL_OK) { 955 Tcl_DecrRefCount(listCopy); 956 return TCL_ERROR; 957 } 958 959 string = Tcl_GetString(objv[1]); 960 slotType = (*string == 'c') ? COLUMN : ROW; 961 if (lObjc == 0) { 962 Tcl_AppendResult(interp, "no ", 963 (slotType == COLUMN) ? "column" : "row", 964 " indices specified", NULL); 965 Tcl_DecrRefCount(listCopy); 966 return TCL_ERROR; 967 } 968 969 masterPtr = GetGrid(master); 970 first = 0; /* lint */ 971 last = 0; /* lint */ 972 973 if ((objc == 4) || (objc == 5)) { 974 if (lObjc != 1) { 975 Tcl_AppendResult(interp, Tcl_GetString(objv[0]), " ", 976 Tcl_GetString(objv[1]), 977 ": must specify a single element on retrieval", NULL); 978 Tcl_DecrRefCount(listCopy); 979 return TCL_ERROR; 980 } 981 if (Tcl_GetIntFromObj(interp, lObjv[0], &slot) != TCL_OK) { 982 Tcl_AppendResult(interp, 983 " (when retreiving options only integer indices are " 984 "allowed)", NULL); 985 Tcl_DecrRefCount(listCopy); 986 return TCL_ERROR; 987 } 988 ok = CheckSlotData(masterPtr, slot, slotType, /* checkOnly */ 1); 989 if (ok == TCL_OK) { 990 slotPtr = (slotType == COLUMN) ? 991 masterPtr->masterDataPtr->columnPtr : 992 masterPtr->masterDataPtr->rowPtr; 993 } 994 995 /* 996 * Return all of the options for this row or column. If the request is 997 * out of range, return all 0's. 998 */ 999 1000 if (objc == 4) { 1001 int minsize = 0, pad = 0, weight = 0; 1002 Tk_Uid uniform = NULL; 1003 Tcl_Obj *res = Tcl_NewListObj(0, NULL); 1004 1005 if (ok == TCL_OK) { 1006 minsize = slotPtr[slot].minSize; 1007 pad = slotPtr[slot].pad; 1008 weight = slotPtr[slot].weight; 1009 uniform = slotPtr[slot].uniform; 1010 } 1011 1012 Tcl_ListObjAppendElement(interp, res, 1013 Tcl_NewStringObj("-minsize", -1)); 1014 Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(minsize)); 1015 Tcl_ListObjAppendElement(interp, res, 1016 Tcl_NewStringObj("-pad", -1)); 1017 Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(pad)); 1018 Tcl_ListObjAppendElement(interp, res, 1019 Tcl_NewStringObj("-uniform", -1)); 1020 Tcl_ListObjAppendElement(interp, res, 1021 Tcl_NewStringObj(uniform == NULL ? "" : uniform, -1)); 1022 Tcl_ListObjAppendElement(interp, res, 1023 Tcl_NewStringObj("-weight", -1)); 1024 Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(weight)); 1025 Tcl_SetObjResult(interp, res); 1026 Tcl_DecrRefCount(listCopy); 1027 return TCL_OK; 1028 } 1029 1030 /* 1031 * If only one option is given, with no value, the current value is 1032 * returned. 1033 */ 1034 1035 if (Tcl_GetIndexFromObj(interp, objv[4], optionStrings, "option", 0, 1036 &index) != TCL_OK) { 1037 Tcl_DecrRefCount(listCopy); 1038 return TCL_ERROR; 1039 } 1040 if (index == ROWCOL_MINSIZE) { 1041 Tcl_SetObjResult(interp, 1042 Tcl_NewIntObj((ok == TCL_OK) ? slotPtr[slot].minSize : 0)); 1043 } else if (index == ROWCOL_WEIGHT) { 1044 Tcl_SetObjResult(interp, 1045 Tcl_NewIntObj((ok == TCL_OK) ? slotPtr[slot].weight : 0)); 1046 } else if (index == ROWCOL_UNIFORM) { 1047 Tk_Uid value = (ok == TCL_OK) ? slotPtr[slot].uniform : ""; 1048 1049 Tcl_SetObjResult(interp, 1050 Tcl_NewStringObj(value == NULL ? "" : value, -1)); 1051 } else if (index == ROWCOL_PAD) { 1052 Tcl_SetObjResult(interp, 1053 Tcl_NewIntObj((ok == TCL_OK) ? slotPtr[slot].pad : 0)); 1054 } 1055 Tcl_DecrRefCount(listCopy); 1056 return TCL_OK; 1057 } 1058 1059 for (j = 0; j < lObjc; j++) { 1060 int allSlaves = 0; 1061 1062 if (Tcl_GetIntFromObj(NULL, lObjv[j], &slot) == TCL_OK) { 1063 first = slot; 1064 last = slot; 1065 slavePtr = NULL; 1066 } else if (strcmp(Tcl_GetString(lObjv[j]), "all") == 0) { 1067 /* 1068 * Make sure master is initialised. 1069 */ 1070 1071 InitMasterData(masterPtr); 1072 1073 slavePtr = masterPtr->slavePtr; 1074 if (slavePtr == NULL) { 1075 continue; 1076 } 1077 allSlaves = 1; 1078 } else if (TkGetWindowFromObj(NULL, tkwin, lObjv[j], &slave) 1079 == TCL_OK) { 1080 /* 1081 * Is it gridded in this master? 1082 */ 1083 1084 slavePtr = GetGrid(slave); 1085 if (slavePtr->masterPtr != masterPtr) { 1086 Tcl_AppendResult(interp, Tcl_GetString(objv[0]), " ", 1087 Tcl_GetString(objv[1]), ": the window \"", 1088 Tcl_GetString(lObjv[j]), "\" is not managed by \"", 1089 Tcl_GetString(objv[2]), "\"", NULL); 1090 Tcl_DecrRefCount(listCopy); 1091 return TCL_ERROR; 1092 } 1093 } else { 1094 Tcl_AppendResult(interp, Tcl_GetString(objv[0]), " ", 1095 Tcl_GetString(objv[1]), ": illegal index \"", 1096 Tcl_GetString(lObjv[j]), "\"", NULL); 1097 Tcl_DecrRefCount(listCopy); 1098 return TCL_ERROR; 1099 } 1100 1101 /* 1102 * The outer loop is only to handle "all". 1103 */ 1104 1105 do { 1106 if (slavePtr != NULL) { 1107 first = (slotType == COLUMN) ? 1108 slavePtr->column : slavePtr->row; 1109 last = first - 1 + ((slotType == COLUMN) ? 1110 slavePtr->numCols : slavePtr->numRows); 1111 } 1112 1113 for (slot = first; slot <= last; slot++) { 1114 ok = CheckSlotData(masterPtr, slot, slotType, /*checkOnly*/ 0); 1115 if (ok != TCL_OK) { 1116 Tcl_AppendResult(interp, Tcl_GetString(objv[0]), " ", 1117 Tcl_GetString(objv[1]), ": \"", 1118 Tcl_GetString(lObjv[j]), 1119 "\" is out of range", NULL); 1120 Tcl_DecrRefCount(listCopy); 1121 return TCL_ERROR; 1122 } 1123 slotPtr = (slotType == COLUMN) ? 1124 masterPtr->masterDataPtr->columnPtr : 1125 masterPtr->masterDataPtr->rowPtr; 1126 1127 /* 1128 * Loop through each option value pair, setting the values as 1129 * required. 1130 */ 1131 1132 for (i = 4; i < objc; i += 2) { 1133 if (Tcl_GetIndexFromObj(interp, objv[i], optionStrings, 1134 "option", 0, &index) != TCL_OK) { 1135 Tcl_DecrRefCount(listCopy); 1136 return TCL_ERROR; 1137 } 1138 if (index == ROWCOL_MINSIZE) { 1139 if (Tk_GetPixelsFromObj(interp, master, objv[i+1], 1140 &size) != TCL_OK) { 1141 Tcl_DecrRefCount(listCopy); 1142 return TCL_ERROR; 1143 } else { 1144 slotPtr[slot].minSize = size; 1145 } 1146 } else if (index == ROWCOL_WEIGHT) { 1147 int wt; 1148 1149 if (Tcl_GetIntFromObj(interp,objv[i+1],&wt)!=TCL_OK) { 1150 Tcl_DecrRefCount(listCopy); 1151 return TCL_ERROR; 1152 } else if (wt < 0) { 1153 Tcl_AppendResult(interp, "invalid arg \"", 1154 Tcl_GetString(objv[i]), 1155 "\": should be non-negative", NULL); 1156 Tcl_DecrRefCount(listCopy); 1157 return TCL_ERROR; 1158 } else { 1159 slotPtr[slot].weight = wt; 1160 } 1161 } else if (index == ROWCOL_UNIFORM) { 1162 slotPtr[slot].uniform = 1163 Tk_GetUid(Tcl_GetString(objv[i+1])); 1164 if (slotPtr[slot].uniform != NULL && 1165 slotPtr[slot].uniform[0] == 0) { 1166 slotPtr[slot].uniform = NULL; 1167 } 1168 } else if (index == ROWCOL_PAD) { 1169 if (Tk_GetPixelsFromObj(interp, master, objv[i+1], 1170 &size) != TCL_OK) { 1171 Tcl_DecrRefCount(listCopy); 1172 return TCL_ERROR; 1173 } else if (size < 0) { 1174 Tcl_AppendResult(interp, "invalid arg \"", 1175 Tcl_GetString(objv[i]), 1176 "\": should be non-negative", NULL); 1177 Tcl_DecrRefCount(listCopy); 1178 return TCL_ERROR; 1179 } else { 1180 slotPtr[slot].pad = size; 1181 } 1182 } 1183 } 1184 } 1185 if (slavePtr != NULL) { 1186 slavePtr = slavePtr->nextPtr; 1187 } 1188 } while ((allSlaves == 1) && (slavePtr != NULL)); 1189 } 1190 Tcl_DecrRefCount(listCopy); 1191 1192 /* 1193 * We changed a property, re-arrange the table, and check for constraint 1194 * shrinkage. A null slotPtr will occur for 'all' checks. 1195 */ 1196 1197 if (slotPtr != NULL) { 1198 if (slotType == ROW) { 1199 int last = masterPtr->masterDataPtr->rowMax - 1; 1200 1201 while ((last >= 0) && (slotPtr[last].weight == 0) 1202 && (slotPtr[last].pad == 0) && (slotPtr[last].minSize == 0) 1203 && (slotPtr[last].uniform == NULL)) { 1204 last--; 1205 } 1206 masterPtr->masterDataPtr->rowMax = last+1; 1207 } else { 1208 int last = masterPtr->masterDataPtr->columnMax - 1; 1209 1210 while ((last >= 0) && (slotPtr[last].weight == 0) 1211 && (slotPtr[last].pad == 0) && (slotPtr[last].minSize == 0) 1212 && (slotPtr[last].uniform == NULL)) { 1213 last--; 1214 } 1215 masterPtr->masterDataPtr->columnMax = last + 1; 1216 } 1217 } 1218 1219 if (masterPtr->abortPtr != NULL) { 1220 *masterPtr->abortPtr = 1; 1221 } 1222 if (!(masterPtr->flags & REQUESTED_RELAYOUT)) { 1223 masterPtr->flags |= REQUESTED_RELAYOUT; 1224 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr); 1225 } 1226 return TCL_OK; 1227} 1228 1229/* 1230 *---------------------------------------------------------------------- 1231 * 1232 * GridSizeCommand -- 1233 * 1234 * Implementation of the [grid size] subcommand. See the user 1235 * documentation for details on what it does. 1236 * 1237 * Results: 1238 * Standard Tcl result. 1239 * 1240 * Side effects: 1241 * Puts grid size information in the interpreter's result. 1242 * 1243 *---------------------------------------------------------------------- 1244 */ 1245 1246static int 1247GridSizeCommand( 1248 Tk_Window tkwin, /* Main window of the application. */ 1249 Tcl_Interp *interp, /* Current interpreter. */ 1250 int objc, /* Number of arguments. */ 1251 Tcl_Obj *CONST objv[]) /* Argument objects. */ 1252{ 1253 Tk_Window master; 1254 Gridder *masterPtr; 1255 GridMaster *gridPtr; /* pointer to grid data */ 1256 1257 if (objc != 3) { 1258 Tcl_WrongNumArgs(interp, 2, objv, "window"); 1259 return TCL_ERROR; 1260 } 1261 1262 if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) { 1263 return TCL_ERROR; 1264 } 1265 masterPtr = GetGrid(master); 1266 1267 if (masterPtr->masterDataPtr != NULL) { 1268 SetGridSize(masterPtr); 1269 gridPtr = masterPtr->masterDataPtr; 1270 Tcl_SetObjResult(interp, NewPairObj( 1271 MAX(gridPtr->columnEnd, gridPtr->columnMax), 1272 MAX(gridPtr->rowEnd, gridPtr->rowMax))); 1273 } else { 1274 Tcl_SetObjResult(interp, NewPairObj(0, 0)); 1275 } 1276 return TCL_OK; 1277} 1278 1279/* 1280 *---------------------------------------------------------------------- 1281 * 1282 * GridSlavesCommand -- 1283 * 1284 * Implementation of the [grid slaves] subcommand. See the user 1285 * documentation for details on what it does. 1286 * 1287 * Results: 1288 * Standard Tcl result. 1289 * 1290 * Side effects: 1291 * Places a list of slaves of the specified window in the interpreter's 1292 * result field. 1293 * 1294 *---------------------------------------------------------------------- 1295 */ 1296 1297static int 1298GridSlavesCommand( 1299 Tk_Window tkwin, /* Main window of the application. */ 1300 Tcl_Interp *interp, /* Current interpreter. */ 1301 int objc, /* Number of arguments. */ 1302 Tcl_Obj *CONST objv[]) /* Argument objects. */ 1303{ 1304 Tk_Window master; 1305 Gridder *masterPtr; /* master grid record */ 1306 Gridder *slavePtr; 1307 int i, value, index; 1308 int row = -1, column = -1; 1309 static CONST char *optionStrings[] = { 1310 "-column", "-row", NULL 1311 }; 1312 enum options { SLAVES_COLUMN, SLAVES_ROW }; 1313 Tcl_Obj *res; 1314 1315 if ((objc < 3) || ((objc % 2) == 0)) { 1316 Tcl_WrongNumArgs(interp, 2, objv, "window ?-option value...?"); 1317 return TCL_ERROR; 1318 } 1319 1320 for (i = 3; i < objc; i += 2) { 1321 if (Tcl_GetIndexFromObj(interp, objv[i], optionStrings, "option", 0, 1322 &index) != TCL_OK) { 1323 return TCL_ERROR; 1324 } 1325 if (Tcl_GetIntFromObj(interp, objv[i+1], &value) != TCL_OK) { 1326 return TCL_ERROR; 1327 } 1328 if (value < 0) { 1329 Tcl_AppendResult(interp, Tcl_GetString(objv[i]), 1330 " is an invalid value: should NOT be < 0", NULL); 1331 return TCL_ERROR; 1332 } 1333 if (index == SLAVES_COLUMN) { 1334 column = value; 1335 } else { 1336 row = value; 1337 } 1338 } 1339 1340 if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) { 1341 return TCL_ERROR; 1342 } 1343 masterPtr = GetGrid(master); 1344 1345 res = Tcl_NewListObj(0, NULL); 1346 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; 1347 slavePtr = slavePtr->nextPtr) { 1348 if (column>=0 && (slavePtr->column > column 1349 || slavePtr->column+slavePtr->numCols-1 < column)) { 1350 continue; 1351 } 1352 if (row>=0 && (slavePtr->row > row || 1353 slavePtr->row+slavePtr->numRows-1 < row)) { 1354 continue; 1355 } 1356 Tcl_ListObjAppendElement(interp, res, 1357 Tcl_NewStringObj(Tk_PathName(slavePtr->tkwin), -1)); 1358 } 1359 Tcl_SetObjResult(interp, res); 1360 return TCL_OK; 1361} 1362 1363/* 1364 *---------------------------------------------------------------------- 1365 * 1366 * GridReqProc -- 1367 * 1368 * This procedure is invoked by Tk_GeometryRequest for windows managed by 1369 * the grid. 1370 * 1371 * Results: 1372 * None. 1373 * 1374 * Side effects: 1375 * Arranges for tkwin, and all its managed siblings, to be re-arranged at 1376 * the next idle point. 1377 * 1378 *---------------------------------------------------------------------- 1379 */ 1380 1381static void 1382GridReqProc( 1383 ClientData clientData, /* Grid's information about window that got 1384 * new preferred geometry. */ 1385 Tk_Window tkwin) /* Other Tk-related information about the 1386 * window. */ 1387{ 1388 register Gridder *gridPtr = (Gridder *) clientData; 1389 1390 gridPtr = gridPtr->masterPtr; 1391 if (gridPtr && !(gridPtr->flags & REQUESTED_RELAYOUT)) { 1392 gridPtr->flags |= REQUESTED_RELAYOUT; 1393 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr); 1394 } 1395} 1396 1397/* 1398 *---------------------------------------------------------------------- 1399 * 1400 * GridLostSlaveProc -- 1401 * 1402 * This procedure is invoked by Tk whenever some other geometry claims 1403 * control over a slave that used to be managed by us. 1404 * 1405 * Results: 1406 * None. 1407 * 1408 * Side effects: 1409 * Forgets all grid-related information about the slave. 1410 * 1411 *---------------------------------------------------------------------- 1412 */ 1413 1414static void 1415GridLostSlaveProc( 1416 ClientData clientData, /* Grid structure for slave window that was 1417 * stolen away. */ 1418 Tk_Window tkwin) /* Tk's handle for the slave window. */ 1419{ 1420 register Gridder *slavePtr = (Gridder *) clientData; 1421 1422 if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) { 1423 Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin); 1424 } 1425 Unlink(slavePtr); 1426 Tk_UnmapWindow(slavePtr->tkwin); 1427} 1428 1429/* 1430 *---------------------------------------------------------------------- 1431 * 1432 * AdjustOffsets -- 1433 * 1434 * This procedure adjusts the size of the layout to fit in the space 1435 * provided. If it needs more space, the extra is added according to the 1436 * weights. If it needs less, the space is removed according to the 1437 * weights, but at no time does the size drop below the minsize specified 1438 * for that slot. 1439 * 1440 * Results: 1441 * The size used by the layout. 1442 * 1443 * Side effects: 1444 * The slot offsets are modified to shrink the layout. 1445 * 1446 *---------------------------------------------------------------------- 1447 */ 1448 1449static int 1450AdjustOffsets( 1451 int size, /* The total layout size (in pixels). */ 1452 int slots, /* Number of slots. */ 1453 register SlotInfo *slotPtr) /* Pointer to slot array. */ 1454{ 1455 register int slot; /* Current slot. */ 1456 int diff; /* Extra pixels needed to add to the layout. */ 1457 int totalWeight; /* Sum of the weights for all the slots. */ 1458 int weight; /* Sum of the weights so far. */ 1459 int minSize; /* Minimum possible layout size. */ 1460 int newDiff; /* The most pixels that can be added on the 1461 * current pass. */ 1462 1463 diff = size - slotPtr[slots-1].offset; 1464 1465 /* 1466 * The layout is already the correct size; all done. 1467 */ 1468 1469 if (diff == 0) { 1470 return size; 1471 } 1472 1473 /* 1474 * If all the weights are zero, there is nothing more to do. 1475 */ 1476 1477 totalWeight = 0; 1478 for (slot = 0; slot < slots; slot++) { 1479 totalWeight += slotPtr[slot].weight; 1480 } 1481 1482 if (totalWeight == 0) { 1483 return slotPtr[slots-1].offset; 1484 } 1485 1486 /* 1487 * Add extra space according to the slot weights. This is done 1488 * cumulatively to prevent round-off error accumulation. 1489 */ 1490 1491 if (diff > 0) { 1492 weight = 0; 1493 for (slot = 0; slot < slots; slot++) { 1494 weight += slotPtr[slot].weight; 1495 slotPtr[slot].offset += diff * weight / totalWeight; 1496 } 1497 return size; 1498 } 1499 1500 /* 1501 * The layout must shrink below its requested size. Compute the minimum 1502 * possible size by looking at the slot minSizes. Store each slot's 1503 * minimum size in temp. 1504 */ 1505 1506 minSize = 0; 1507 for (slot = 0; slot < slots; slot++) { 1508 if (slotPtr[slot].weight > 0) { 1509 slotPtr[slot].temp = slotPtr[slot].minSize; 1510 } else if (slot > 0) { 1511 slotPtr[slot].temp = slotPtr[slot].offset - slotPtr[slot-1].offset; 1512 } else { 1513 slotPtr[slot].temp = slotPtr[slot].offset; 1514 } 1515 minSize += slotPtr[slot].temp; 1516 } 1517 1518 /* 1519 * If the requested size is less than the minimum required size, set the 1520 * slot sizes to their minimum values. 1521 */ 1522 1523 if (size <= minSize) { 1524 int offset = 0; 1525 1526 for (slot = 0; slot < slots; slot++) { 1527 offset += slotPtr[slot].temp; 1528 slotPtr[slot].offset = offset; 1529 } 1530 return minSize; 1531 } 1532 1533 /* 1534 * Remove space from slots according to their weights. The weights get 1535 * renormalized anytime a slot shrinks to its minimum size. 1536 */ 1537 1538 while (diff < 0) { 1539 /* 1540 * Find the total weight for the shrinkable slots. 1541 */ 1542 1543 totalWeight = 0; 1544 for (slot = 0; slot < slots; slot++) { 1545 int current = (slot == 0) ? slotPtr[slot].offset : 1546 slotPtr[slot].offset - slotPtr[slot-1].offset; 1547 1548 if (current > slotPtr[slot].minSize) { 1549 totalWeight += slotPtr[slot].weight; 1550 slotPtr[slot].temp = slotPtr[slot].weight; 1551 } else { 1552 slotPtr[slot].temp = 0; 1553 } 1554 } 1555 if (totalWeight == 0) { 1556 break; 1557 } 1558 1559 /* 1560 * Find the maximum amount of space we can distribute this pass. 1561 */ 1562 1563 newDiff = diff; 1564 for (slot = 0; slot < slots; slot++) { 1565 int current; /* Current size of this slot. */ 1566 int maxDiff; /* Maximum diff that would cause this slot to 1567 * equal its minsize. */ 1568 1569 if (slotPtr[slot].temp == 0) { 1570 continue; 1571 } 1572 current = (slot == 0) ? slotPtr[slot].offset : 1573 slotPtr[slot].offset - slotPtr[slot-1].offset; 1574 maxDiff = totalWeight * (slotPtr[slot].minSize - current) 1575 / slotPtr[slot].temp; 1576 if (maxDiff > newDiff) { 1577 newDiff = maxDiff; 1578 } 1579 } 1580 1581 /* 1582 * Now distribute the space. 1583 */ 1584 1585 weight = 0; 1586 for (slot = 0; slot < slots; slot++) { 1587 weight += slotPtr[slot].temp; 1588 slotPtr[slot].offset += newDiff * weight / totalWeight; 1589 } 1590 diff -= newDiff; 1591 } 1592 return size; 1593} 1594 1595/* 1596 *---------------------------------------------------------------------- 1597 * 1598 * AdjustForSticky -- 1599 * 1600 * This procedure adjusts the size of a slave in its cavity based on its 1601 * "sticky" flags. 1602 * 1603 * Results: 1604 * The input x, y, width, and height are changed to represent the desired 1605 * coordinates of the slave. 1606 * 1607 * Side effects: 1608 * None. 1609 * 1610 *---------------------------------------------------------------------- 1611 */ 1612 1613static void 1614AdjustForSticky( 1615 Gridder *slavePtr, /* Slave window to arrange in its cavity. */ 1616 int *xPtr, /* Pixel location of the left edge of the cavity. */ 1617 int *yPtr, /* Pixel location of the top edge of the cavity. */ 1618 int *widthPtr, /* Width of the cavity (in pixels). */ 1619 int *heightPtr) /* Height of the cavity (in pixels). */ 1620{ 1621 int diffx = 0; /* Cavity width - slave width. */ 1622 int diffy = 0; /* Cavity hight - slave height. */ 1623 int sticky = slavePtr->sticky; 1624 1625 *xPtr += slavePtr->padLeft; 1626 *widthPtr -= slavePtr->padX; 1627 *yPtr += slavePtr->padTop; 1628 *heightPtr -= slavePtr->padY; 1629 1630 if (*widthPtr > (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX)) { 1631 diffx = *widthPtr - (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX); 1632 *widthPtr = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX; 1633 } 1634 1635 if (*heightPtr > (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY)) { 1636 diffy = *heightPtr - (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY); 1637 *heightPtr = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY; 1638 } 1639 1640 if (sticky&STICK_EAST && sticky&STICK_WEST) { 1641 *widthPtr += diffx; 1642 } 1643 if (sticky&STICK_NORTH && sticky&STICK_SOUTH) { 1644 *heightPtr += diffy; 1645 } 1646 if (!(sticky&STICK_WEST)) { 1647 *xPtr += (sticky&STICK_EAST) ? diffx : diffx/2; 1648 } 1649 if (!(sticky&STICK_NORTH)) { 1650 *yPtr += (sticky&STICK_SOUTH) ? diffy : diffy/2; 1651 } 1652} 1653 1654/* 1655 *---------------------------------------------------------------------- 1656 * 1657 * ArrangeGrid -- 1658 * 1659 * This procedure is invoked (using the Tcl_DoWhenIdle mechanism) to 1660 * re-layout a set of windows managed by the grid. It is invoked at idle 1661 * time so that a series of grid requests can be merged into a single 1662 * layout operation. 1663 * 1664 * Results: 1665 * None. 1666 * 1667 * Side effects: 1668 * The slaves of masterPtr may get resized or moved. 1669 * 1670 *---------------------------------------------------------------------- 1671 */ 1672 1673static void 1674ArrangeGrid( 1675 ClientData clientData) /* Structure describing master whose slaves 1676 * are to be re-layed out. */ 1677{ 1678 register Gridder *masterPtr = (Gridder *) clientData; 1679 register Gridder *slavePtr; 1680 GridMaster *slotPtr = masterPtr->masterDataPtr; 1681 int abort; 1682 int width, height; /* Requested size of layout, in pixels. */ 1683 int realWidth, realHeight; /* Actual size layout should take-up. */ 1684 int usedX, usedY; 1685 1686 masterPtr->flags &= ~REQUESTED_RELAYOUT; 1687 1688 /* 1689 * If the master has no slaves anymore, then don't do anything at all: 1690 * just leave the master's size as-is. Otherwise there is no way to 1691 * "relinquish" control over the master so another geometry manager can 1692 * take over. 1693 */ 1694 1695 if (masterPtr->slavePtr == NULL) { 1696 return; 1697 } 1698 1699 if (masterPtr->masterDataPtr == NULL) { 1700 return; 1701 } 1702 1703 /* 1704 * Abort any nested call to ArrangeGrid for this window, since we'll do 1705 * everything necessary here, and set up so this call can be aborted if 1706 * necessary. 1707 */ 1708 1709 if (masterPtr->abortPtr != NULL) { 1710 *masterPtr->abortPtr = 1; 1711 } 1712 masterPtr->abortPtr = &abort; 1713 abort = 0; 1714 Tcl_Preserve((ClientData) masterPtr); 1715 1716 /* 1717 * Call the constraint engine to fill in the row and column offsets. 1718 */ 1719 1720 SetGridSize(masterPtr); 1721 width = ResolveConstraints(masterPtr, COLUMN, 0); 1722 height = ResolveConstraints(masterPtr, ROW, 0); 1723 width += Tk_InternalBorderLeft(masterPtr->tkwin) + 1724 Tk_InternalBorderRight(masterPtr->tkwin); 1725 height += Tk_InternalBorderTop(masterPtr->tkwin) + 1726 Tk_InternalBorderBottom(masterPtr->tkwin); 1727 1728 if (width < Tk_MinReqWidth(masterPtr->tkwin)) { 1729 width = Tk_MinReqWidth(masterPtr->tkwin); 1730 } 1731 if (height < Tk_MinReqHeight(masterPtr->tkwin)) { 1732 height = Tk_MinReqHeight(masterPtr->tkwin); 1733 } 1734 1735 if (((width != Tk_ReqWidth(masterPtr->tkwin)) 1736 || (height != Tk_ReqHeight(masterPtr->tkwin))) 1737 && !(masterPtr->flags & DONT_PROPAGATE)) { 1738 Tk_GeometryRequest(masterPtr->tkwin, width, height); 1739 if (width>1 && height>1) { 1740 masterPtr->flags |= REQUESTED_RELAYOUT; 1741 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr); 1742 } 1743 masterPtr->abortPtr = NULL; 1744 Tcl_Release((ClientData) masterPtr); 1745 return; 1746 } 1747 1748 /* 1749 * If the currently requested layout size doesn't match the master's 1750 * window size, then adjust the slot offsets according to the weights. If 1751 * all of the weights are zero, place the layout according to the anchor 1752 * value. 1753 */ 1754 1755 realWidth = Tk_Width(masterPtr->tkwin) - 1756 Tk_InternalBorderLeft(masterPtr->tkwin) - 1757 Tk_InternalBorderRight(masterPtr->tkwin); 1758 realHeight = Tk_Height(masterPtr->tkwin) - 1759 Tk_InternalBorderTop(masterPtr->tkwin) - 1760 Tk_InternalBorderBottom(masterPtr->tkwin); 1761 usedX = AdjustOffsets(realWidth, 1762 MAX(slotPtr->columnEnd, slotPtr->columnMax), slotPtr->columnPtr); 1763 usedY = AdjustOffsets(realHeight, MAX(slotPtr->rowEnd, slotPtr->rowMax), 1764 slotPtr->rowPtr); 1765 TkComputeAnchor(masterPtr->masterDataPtr->anchor, masterPtr->tkwin, 1766 0, 0, usedX, usedY, &slotPtr->startX, &slotPtr->startY); 1767 1768 /* 1769 * Now adjust the actual size of the slave to its cavity by computing the 1770 * cavity size, and adjusting the widget according to its stickyness. 1771 */ 1772 1773 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL && !abort; 1774 slavePtr = slavePtr->nextPtr) { 1775 int x, y; /* Top left coordinate */ 1776 int width, height; /* Slot or slave size */ 1777 int col = slavePtr->column; 1778 int row = slavePtr->row; 1779 1780 x = (col>0) ? slotPtr->columnPtr[col-1].offset : 0; 1781 y = (row>0) ? slotPtr->rowPtr[row-1].offset : 0; 1782 1783 width = slotPtr->columnPtr[slavePtr->numCols+col-1].offset - x; 1784 height = slotPtr->rowPtr[slavePtr->numRows+row-1].offset - y; 1785 1786 x += slotPtr->startX; 1787 y += slotPtr->startY; 1788 1789 AdjustForSticky(slavePtr, &x, &y, &width, &height); 1790 1791 /* 1792 * Now put the window in the proper spot. (This was taken directly 1793 * from tkPack.c.) If the slave is a child of the master, then do this 1794 * here. Otherwise let Tk_MaintainGeometry do the work. 1795 */ 1796 1797 if (masterPtr->tkwin == Tk_Parent(slavePtr->tkwin)) { 1798 if ((width <= 0) || (height <= 0)) { 1799 Tk_UnmapWindow(slavePtr->tkwin); 1800 } else { 1801 if ((x != Tk_X(slavePtr->tkwin)) 1802 || (y != Tk_Y(slavePtr->tkwin)) 1803 || (width != Tk_Width(slavePtr->tkwin)) 1804 || (height != Tk_Height(slavePtr->tkwin))) { 1805 Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height); 1806 } 1807 if (abort) { 1808 break; 1809 } 1810 1811 /* 1812 * Don't map the slave if the master isn't mapped: wait until 1813 * the master gets mapped later. 1814 */ 1815 1816 if (Tk_IsMapped(masterPtr->tkwin)) { 1817 Tk_MapWindow(slavePtr->tkwin); 1818 } 1819 } 1820 } else if ((width <= 0) || (height <= 0)) { 1821 Tk_UnmaintainGeometry(slavePtr->tkwin, masterPtr->tkwin); 1822 Tk_UnmapWindow(slavePtr->tkwin); 1823 } else { 1824 Tk_MaintainGeometry(slavePtr->tkwin, masterPtr->tkwin, x, y, 1825 width, height); 1826 } 1827 } 1828 1829 masterPtr->abortPtr = NULL; 1830 Tcl_Release((ClientData) masterPtr); 1831} 1832 1833/* 1834 *---------------------------------------------------------------------- 1835 * 1836 * ResolveConstraints -- 1837 * 1838 * Resolve all of the column and row boundaries. Most of the calculations 1839 * are identical for rows and columns, so this procedure is called twice, 1840 * once for rows, and again for columns. 1841 * 1842 * Results: 1843 * The offset (in pixels) from the left/top edge of this layout is 1844 * returned. 1845 * 1846 * Side effects: 1847 * The slot offsets are copied into the SlotInfo structure for the 1848 * geometry master. 1849 * 1850 *---------------------------------------------------------------------- 1851 */ 1852 1853static int 1854ResolveConstraints( 1855 Gridder *masterPtr, /* The geometry master for this grid. */ 1856 int slotType, /* Either ROW or COLUMN. */ 1857 int maxOffset) /* The actual maximum size of this layout in 1858 * pixels, or 0 (not currently used). */ 1859{ 1860 register SlotInfo *slotPtr; /* Pointer to row/col constraints. */ 1861 register Gridder *slavePtr; /* List of slave windows in this grid. */ 1862 int constraintCount; /* Count of rows or columns that have 1863 * constraints. */ 1864 int slotCount; /* Last occupied row or column. */ 1865 int gridCount; /* The larger of slotCount and 1866 * constraintCount. */ 1867 GridLayout *layoutPtr; /* Temporary layout structure. */ 1868 int requiredSize; /* The natural size of the grid (pixels). 1869 * This is the minimum size needed to 1870 * accomodate all of the slaves at their 1871 * requested sizes. */ 1872 int offset; /* The pixel offset of the right edge of the 1873 * current slot from the beginning of the 1874 * layout. */ 1875 int slot; /* The current slot. */ 1876 int start; /* The first slot of a contiguous set whose 1877 * constraints are not yet fully resolved. */ 1878 int end; /* The Last slot of a contiguous set whose 1879 * constraints are not yet fully resolved. */ 1880 UniformGroup uniformPre[UNIFORM_PREALLOC]; 1881 /* Pre-allocated space for uniform groups. */ 1882 UniformGroup *uniformGroupPtr; 1883 /* Uniform groups data. */ 1884 int uniformGroups; /* Number of currently used uniform groups. */ 1885 int uniformGroupsAlloced; /* Size of allocated space for uniform 1886 * groups. */ 1887 int weight, minSize; 1888 int prevGrow, accWeight, grow; 1889 1890 /* 1891 * For typical sized tables, we'll use stack space for the layout data to 1892 * avoid the overhead of a malloc and free for every layout. 1893 */ 1894 1895 GridLayout layoutData[TYPICAL_SIZE + 1]; 1896 1897 if (slotType == COLUMN) { 1898 constraintCount = masterPtr->masterDataPtr->columnMax; 1899 slotCount = masterPtr->masterDataPtr->columnEnd; 1900 slotPtr = masterPtr->masterDataPtr->columnPtr; 1901 } else { 1902 constraintCount = masterPtr->masterDataPtr->rowMax; 1903 slotCount = masterPtr->masterDataPtr->rowEnd; 1904 slotPtr = masterPtr->masterDataPtr->rowPtr; 1905 } 1906 1907 /* 1908 * Make sure there is enough memory for the layout. 1909 */ 1910 1911 gridCount = MAX(constraintCount, slotCount); 1912 if (gridCount >= TYPICAL_SIZE) { 1913 layoutPtr = (GridLayout *) 1914 ckalloc(sizeof(GridLayout) * (1+gridCount)); 1915 } else { 1916 layoutPtr = layoutData; 1917 } 1918 1919 /* 1920 * Allocate an extra layout slot to represent the left/top edge of the 0th 1921 * slot to make it easier to calculate slot widths from offsets without 1922 * special case code. 1923 * 1924 * Initialize the "dummy" slot to the left/top of the table. This slot 1925 * avoids special casing the first slot. 1926 */ 1927 1928 layoutPtr->minOffset = 0; 1929 layoutPtr->maxOffset = 0; 1930 layoutPtr++; 1931 1932 /* 1933 * Step 1. 1934 * Copy the slot constraints into the layout structure, and initialize the 1935 * rest of the fields. 1936 */ 1937 1938 for (slot=0; slot < constraintCount; slot++) { 1939 layoutPtr[slot].minSize = slotPtr[slot].minSize; 1940 layoutPtr[slot].weight = slotPtr[slot].weight; 1941 layoutPtr[slot].uniform = slotPtr[slot].uniform; 1942 layoutPtr[slot].pad = slotPtr[slot].pad; 1943 layoutPtr[slot].binNextPtr = NULL; 1944 } 1945 for (; slot<gridCount; slot++) { 1946 layoutPtr[slot].minSize = 0; 1947 layoutPtr[slot].weight = 0; 1948 layoutPtr[slot].uniform = NULL; 1949 layoutPtr[slot].pad = 0; 1950 layoutPtr[slot].binNextPtr = NULL; 1951 } 1952 1953 /* 1954 * Step 2. 1955 * Slaves with a span of 1 are used to determine the minimum size of each 1956 * slot. Slaves whose span is two or more slots don't contribute to the 1957 * minimum size of each slot directly, but can cause slots to grow if 1958 * their size exceeds the the sizes of the slots they span. 1959 * 1960 * Bin all slaves whose spans are > 1 by their right edges. This allows 1961 * the computation on minimum and maximum possible layout sizes at each 1962 * slot boundary, without the need to re-sort the slaves. 1963 */ 1964 1965 switch (slotType) { 1966 case COLUMN: 1967 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; 1968 slavePtr = slavePtr->nextPtr) { 1969 int rightEdge = slavePtr->column + slavePtr->numCols - 1; 1970 1971 slavePtr->size = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->padX 1972 + slavePtr->iPadX + slavePtr->doubleBw; 1973 if (slavePtr->numCols > 1) { 1974 slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr; 1975 layoutPtr[rightEdge].binNextPtr = slavePtr; 1976 } else { 1977 int size = slavePtr->size + layoutPtr[rightEdge].pad; 1978 1979 if (size > layoutPtr[rightEdge].minSize) { 1980 layoutPtr[rightEdge].minSize = size; 1981 } 1982 } 1983 } 1984 break; 1985 case ROW: 1986 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; 1987 slavePtr = slavePtr->nextPtr) { 1988 int rightEdge = slavePtr->row + slavePtr->numRows - 1; 1989 1990 slavePtr->size = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->padY 1991 + slavePtr->iPadY + slavePtr->doubleBw; 1992 if (slavePtr->numRows > 1) { 1993 slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr; 1994 layoutPtr[rightEdge].binNextPtr = slavePtr; 1995 } else { 1996 int size = slavePtr->size + layoutPtr[rightEdge].pad; 1997 1998 if (size > layoutPtr[rightEdge].minSize) { 1999 layoutPtr[rightEdge].minSize = size; 2000 } 2001 } 2002 } 2003 break; 2004 } 2005 2006 /* 2007 * Step 2b. 2008 * Consider demands on uniform sizes. 2009 */ 2010 2011 uniformGroupPtr = uniformPre; 2012 uniformGroupsAlloced = UNIFORM_PREALLOC; 2013 uniformGroups = 0; 2014 2015 for (slot = 0; slot < gridCount; slot++) { 2016 if (layoutPtr[slot].uniform != NULL) { 2017 for (start = 0; start < uniformGroups; start++) { 2018 if (uniformGroupPtr[start].group == layoutPtr[slot].uniform) { 2019 break; 2020 } 2021 } 2022 if (start >= uniformGroups) { 2023 /* 2024 * Have not seen that group before, set up data for it. 2025 */ 2026 2027 if (uniformGroups >= uniformGroupsAlloced) { 2028 /* 2029 * We need to allocate more space. 2030 */ 2031 2032 size_t oldSize = uniformGroupsAlloced 2033 * sizeof(UniformGroup); 2034 size_t newSize = (uniformGroupsAlloced + UNIFORM_PREALLOC) 2035 * sizeof(UniformGroup); 2036 UniformGroup *newUG = (UniformGroup *) ckalloc(newSize); 2037 UniformGroup *oldUG = uniformGroupPtr; 2038 2039 memcpy(newUG, oldUG, oldSize); 2040 if (oldUG != uniformPre) { 2041 ckfree((char *) oldUG); 2042 } 2043 uniformGroupPtr = newUG; 2044 uniformGroupsAlloced += UNIFORM_PREALLOC; 2045 } 2046 uniformGroups++; 2047 uniformGroupPtr[start].group = layoutPtr[slot].uniform; 2048 uniformGroupPtr[start].minSize = 0; 2049 } 2050 weight = layoutPtr[slot].weight; 2051 weight = weight > 0 ? weight : 1; 2052 minSize = (layoutPtr[slot].minSize + weight - 1) / weight; 2053 if (minSize > uniformGroupPtr[start].minSize) { 2054 uniformGroupPtr[start].minSize = minSize; 2055 } 2056 } 2057 } 2058 2059 /* 2060 * Data has been gathered about uniform groups. Now relayout accordingly. 2061 */ 2062 2063 if (uniformGroups > 0) { 2064 for (slot = 0; slot < gridCount; slot++) { 2065 if (layoutPtr[slot].uniform != NULL) { 2066 for (start = 0; start < uniformGroups; start++) { 2067 if (uniformGroupPtr[start].group == 2068 layoutPtr[slot].uniform) { 2069 weight = layoutPtr[slot].weight; 2070 weight = weight > 0 ? weight : 1; 2071 layoutPtr[slot].minSize = 2072 uniformGroupPtr[start].minSize * weight; 2073 break; 2074 } 2075 } 2076 } 2077 } 2078 } 2079 2080 if (uniformGroupPtr != uniformPre) { 2081 ckfree((char *) uniformGroupPtr); 2082 } 2083 2084 /* 2085 * Step 3. 2086 * Determine the minimum slot offsets going from left to right that would 2087 * fit all of the slaves. This determines the minimum 2088 */ 2089 2090 for (offset=0,slot=0; slot < gridCount; slot++) { 2091 layoutPtr[slot].minOffset = layoutPtr[slot].minSize + offset; 2092 for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL; 2093 slavePtr = slavePtr->binNextPtr) { 2094 int span = (slotType == COLUMN) ? 2095 slavePtr->numCols : slavePtr->numRows; 2096 int required = slavePtr->size + layoutPtr[slot - span].minOffset; 2097 2098 if (required > layoutPtr[slot].minOffset) { 2099 layoutPtr[slot].minOffset = required; 2100 } 2101 } 2102 offset = layoutPtr[slot].minOffset; 2103 } 2104 2105 /* 2106 * At this point, we know the minimum required size of the entire layout. 2107 * It might be prudent to stop here if our "master" will resize itself to 2108 * this size. 2109 */ 2110 2111 requiredSize = offset; 2112 if (maxOffset > offset) { 2113 offset=maxOffset; 2114 } 2115 2116 /* 2117 * Step 4. 2118 * Determine the minimum slot offsets going from right to left, bounding 2119 * the pixel range of each slot boundary. Pre-fill all of the right 2120 * offsets with the actual size of the table; they will be reduced as 2121 * required. 2122 */ 2123 2124 for (slot=0; slot < gridCount; slot++) { 2125 layoutPtr[slot].maxOffset = offset; 2126 } 2127 for (slot=gridCount-1; slot > 0;) { 2128 for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL; 2129 slavePtr = slavePtr->binNextPtr) { 2130 int span = (slotType == COLUMN) ? 2131 slavePtr->numCols : slavePtr->numRows; 2132 int require = offset - slavePtr->size; 2133 int startSlot = slot - span; 2134 2135 if (startSlot >=0 && require < layoutPtr[startSlot].maxOffset) { 2136 layoutPtr[startSlot].maxOffset = require; 2137 } 2138 } 2139 offset -= layoutPtr[slot].minSize; 2140 slot--; 2141 if (layoutPtr[slot].maxOffset < offset) { 2142 offset = layoutPtr[slot].maxOffset; 2143 } else { 2144 layoutPtr[slot].maxOffset = offset; 2145 } 2146 } 2147 2148 /* 2149 * Step 5. 2150 * At this point, each slot boundary has a range of values that will 2151 * satisfy the overall layout size. Make repeated passes over the layout 2152 * structure looking for spans of slot boundaries where the minOffsets are 2153 * less than the maxOffsets, and adjust the offsets according to the slot 2154 * weights. At each pass, at least one slot boundary will have its range 2155 * of possible values fixed at a single value. 2156 */ 2157 2158 for (start = 0; start < gridCount;) { 2159 int totalWeight = 0; /* Sum of the weights for all of the slots in 2160 * this span. */ 2161 int need = 0; /* The minimum space needed to layout this 2162 * span. */ 2163 int have; /* The actual amount of space that will be 2164 * taken up by this span. */ 2165 int weight; /* Cumulative weights of the columns in this 2166 * span. */ 2167 int noWeights = 0; /* True if the span has no weights. */ 2168 2169 /* 2170 * Find a span by identifying ranges of slots whose edges are already 2171 * constrained at fixed offsets, but whose internal slot boundaries 2172 * have a range of possible positions. 2173 */ 2174 2175 if (layoutPtr[start].minOffset == layoutPtr[start].maxOffset) { 2176 start++; 2177 continue; 2178 } 2179 2180 for (end = start + 1; end < gridCount; end++) { 2181 if (layoutPtr[end].minOffset == layoutPtr[end].maxOffset) { 2182 break; 2183 } 2184 } 2185 2186 /* 2187 * We found a span. Compute the total weight, minumum space required, 2188 * for this span, and the actual amount of space the span should use. 2189 */ 2190 2191 for (slot = start; slot <= end; slot++) { 2192 totalWeight += layoutPtr[slot].weight; 2193 need += layoutPtr[slot].minSize; 2194 } 2195 have = layoutPtr[end].maxOffset - layoutPtr[start-1].minOffset; 2196 2197 /* 2198 * If all the weights in the span are zero, then distribute the extra 2199 * space evenly. 2200 */ 2201 2202 if (totalWeight == 0) { 2203 noWeights++; 2204 totalWeight = end - start + 1; 2205 } 2206 2207 /* 2208 * It might not be possible to give the span all of the space 2209 * available on this pass without violating the size constraints of 2210 * one or more of the internal slot boundaries. Try to determine the 2211 * maximum amount of space that when added to the entire span, would 2212 * cause a slot boundary to have its possible range reduced to one 2213 * value, and reduce the amount of extra space allocated on this pass 2214 * accordingly. 2215 * 2216 * The calculation is done cumulatively to avoid accumulating roundoff 2217 * errors. 2218 */ 2219 2220 do { 2221 int prevMinOffset = layoutPtr[start - 1].minOffset; 2222 2223 prevGrow = 0; 2224 accWeight = 0; 2225 for (slot = start; slot <= end; slot++) { 2226 weight = noWeights ? 1 : layoutPtr[slot].weight; 2227 accWeight += weight; 2228 grow = (have - need) * accWeight / totalWeight - prevGrow; 2229 prevGrow += grow; 2230 2231 if ((weight > 0) && 2232 ((prevMinOffset + layoutPtr[slot].minSize + grow) 2233 > layoutPtr[slot].maxOffset)) { 2234 int newHave; 2235 2236 /* 2237 * There is not enough room to grow that much. Calculate 2238 * how much this slot can grow and how much "have" that 2239 * corresponds to. 2240 */ 2241 2242 grow = layoutPtr[slot].maxOffset - 2243 layoutPtr[slot].minSize - prevMinOffset; 2244 newHave = grow * totalWeight / weight; 2245 if (newHave > totalWeight) { 2246 /* 2247 * By distributing multiples of totalWeight we 2248 * minimize rounding errors since they will only 2249 * happen in the last loop(s). 2250 */ 2251 2252 newHave = newHave / totalWeight * totalWeight; 2253 } 2254 if (newHave <= 0) { 2255 /* 2256 * We can end up with a "have" of 0 here if the 2257 * previous slots have taken all the space. In that 2258 * case we cannot guess an appropriate "have" so we 2259 * just try some lower "have" that is >= 1, to make 2260 * sure this terminates. 2261 */ 2262 2263 newHave = (have - need) - 1; 2264 if (newHave > (3 * totalWeight)) { 2265 /* 2266 * Go down 25% for large values. 2267 */ 2268 newHave = newHave * 3 / 4; 2269 } 2270 2271 if (newHave > totalWeight) { 2272 /* 2273 * Round down to a multiple of totalWeight. 2274 */ 2275 newHave = newHave / totalWeight * totalWeight; 2276 } 2277 2278 if (newHave <= 0) { 2279 newHave = 1; 2280 } 2281 } 2282 have = newHave + need; 2283 2284 /* 2285 * Restart loop to check if the new "have" will fit. 2286 */ 2287 2288 break; 2289 } 2290 prevMinOffset += layoutPtr[slot].minSize + grow; 2291 if (prevMinOffset < layoutPtr[slot].minOffset) { 2292 prevMinOffset = layoutPtr[slot].minOffset; 2293 } 2294 } 2295 2296 /* 2297 * Quit the outer loop if the inner loop ran all the way. 2298 */ 2299 } while (slot <= end); 2300 2301 /* 2302 * Now distribute the extra space among the slots by adjusting the 2303 * minSizes and minOffsets. 2304 */ 2305 2306 prevGrow = 0; 2307 accWeight = 0; 2308 for (slot = start; slot <= end; slot++) { 2309 accWeight += noWeights ? 1 : layoutPtr[slot].weight; 2310 grow = (have - need) * accWeight / totalWeight - prevGrow; 2311 prevGrow += grow; 2312 layoutPtr[slot].minSize += grow; 2313 if ((layoutPtr[slot-1].minOffset + layoutPtr[slot].minSize) 2314 > layoutPtr[slot].minOffset) { 2315 layoutPtr[slot].minOffset = layoutPtr[slot-1].minOffset + 2316 layoutPtr[slot].minSize; 2317 } 2318 } 2319 2320 /* 2321 * Having pushed the top/left boundaries of the slots to take up extra 2322 * space, the bottom/right space is recalculated to propagate the new 2323 * space allocation. 2324 */ 2325 2326 for (slot = end; slot > start; slot--) { 2327 /* 2328 * maxOffset may not go up. 2329 */ 2330 2331 if ((layoutPtr[slot].maxOffset-layoutPtr[slot].minSize) 2332 < layoutPtr[slot-1].maxOffset) { 2333 layoutPtr[slot-1].maxOffset = 2334 layoutPtr[slot].maxOffset-layoutPtr[slot].minSize; 2335 } 2336 } 2337 } 2338 2339 /* 2340 * Step 6. 2341 * All of the space has been apportioned; copy the layout information back 2342 * into the master. 2343 */ 2344 2345 for (slot=0; slot < gridCount; slot++) { 2346 slotPtr[slot].offset = layoutPtr[slot].minOffset; 2347 } 2348 2349 --layoutPtr; 2350 if (layoutPtr != layoutData) { 2351 ckfree((char *) layoutPtr); 2352 } 2353 return requiredSize; 2354} 2355 2356/* 2357 *---------------------------------------------------------------------- 2358 * 2359 * GetGrid -- 2360 * 2361 * This internal procedure is used to locate a Grid structure for a given 2362 * window, creating one if one doesn't exist already. 2363 * 2364 * Results: 2365 * The return value is a pointer to the Grid structure corresponding to 2366 * tkwin. 2367 * 2368 * Side effects: 2369 * A new grid structure may be created. If so, then a callback is set up 2370 * to clean things up when the window is deleted. 2371 * 2372 *---------------------------------------------------------------------- 2373 */ 2374 2375static Gridder * 2376GetGrid( 2377 Tk_Window tkwin) /* Token for window for which grid structure 2378 * is desired. */ 2379{ 2380 register Gridder *gridPtr; 2381 Tcl_HashEntry *hPtr; 2382 int isNew; 2383 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr; 2384 2385 if (!dispPtr->gridInit) { 2386 Tcl_InitHashTable(&dispPtr->gridHashTable, TCL_ONE_WORD_KEYS); 2387 dispPtr->gridInit = 1; 2388 } 2389 2390 /* 2391 * See if there's already grid for this window. If not, then create a new 2392 * one. 2393 */ 2394 2395 hPtr = Tcl_CreateHashEntry(&dispPtr->gridHashTable, (char*) tkwin, &isNew); 2396 if (!isNew) { 2397 return (Gridder *) Tcl_GetHashValue(hPtr); 2398 } 2399 gridPtr = (Gridder *) ckalloc(sizeof(Gridder)); 2400 gridPtr->tkwin = tkwin; 2401 gridPtr->masterPtr = NULL; 2402 gridPtr->masterDataPtr = NULL; 2403 gridPtr->nextPtr = NULL; 2404 gridPtr->slavePtr = NULL; 2405 gridPtr->binNextPtr = NULL; 2406 2407 gridPtr->column = -1; 2408 gridPtr->row = -1; 2409 gridPtr->numCols = 1; 2410 gridPtr->numRows = 1; 2411 2412 gridPtr->padX = 0; 2413 gridPtr->padY = 0; 2414 gridPtr->padLeft = 0; 2415 gridPtr->padTop = 0; 2416 gridPtr->iPadX = 0; 2417 gridPtr->iPadY = 0; 2418 gridPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width; 2419 gridPtr->abortPtr = NULL; 2420 gridPtr->flags = 0; 2421 gridPtr->sticky = 0; 2422 gridPtr->size = 0; 2423 gridPtr->masterDataPtr = NULL; 2424 Tcl_SetHashValue(hPtr, gridPtr); 2425 Tk_CreateEventHandler(tkwin, StructureNotifyMask, 2426 GridStructureProc, (ClientData) gridPtr); 2427 return gridPtr; 2428} 2429 2430/* 2431 *---------------------------------------------------------------------- 2432 * 2433 * SetGridSize -- 2434 * 2435 * This internal procedure sets the size of the grid occupied by slaves. 2436 * 2437 * Results: 2438 * None 2439 * 2440 * Side effects: 2441 * The width and height arguments are filled in the master data 2442 * structure. Additional space is allocated for the constraints to 2443 * accomodate the offsets. 2444 * 2445 *---------------------------------------------------------------------- 2446 */ 2447 2448static void 2449SetGridSize( 2450 Gridder *masterPtr) /* The geometry master for this grid. */ 2451{ 2452 register Gridder *slavePtr; /* Current slave window. */ 2453 int maxX = 0, maxY = 0; 2454 2455 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; 2456 slavePtr = slavePtr->nextPtr) { 2457 maxX = MAX(maxX, slavePtr->numCols + slavePtr->column); 2458 maxY = MAX(maxY, slavePtr->numRows + slavePtr->row); 2459 } 2460 masterPtr->masterDataPtr->columnEnd = maxX; 2461 masterPtr->masterDataPtr->rowEnd = maxY; 2462 CheckSlotData(masterPtr, maxX, COLUMN, CHECK_SPACE); 2463 CheckSlotData(masterPtr, maxY, ROW, CHECK_SPACE); 2464} 2465 2466/* 2467 *---------------------------------------------------------------------- 2468 * 2469 * SetSlaveColumn -- 2470 * 2471 * Update column data for a slave, checking that MAX_ELEMENT bound 2472 * is not passed. 2473 * 2474 * Results: 2475 * TCL_ERROR if out of bounds, TCL_OK otherwise 2476 * 2477 * Side effects: 2478 * Slave fields are updated. 2479 * 2480 *---------------------------------------------------------------------- 2481 */ 2482 2483static int 2484SetSlaveColumn( 2485 Tcl_Interp *interp, /* Interp for error message. */ 2486 Gridder *slavePtr, /* Slave to be updated. */ 2487 int column, /* New column or -1 to be unchanged. */ 2488 int numCols) /* New columnspan or -1 to be unchanged. */ 2489{ 2490 int newColumn, newNumCols, lastCol; 2491 2492 newColumn = (column >= 0) ? column : slavePtr->column; 2493 newNumCols = (numCols >= 1) ? numCols : slavePtr->numCols; 2494 2495 lastCol = ((newColumn >= 0) ? newColumn : 0) + newNumCols; 2496 if (lastCol >= MAX_ELEMENT) { 2497 Tcl_SetResult(interp, "Column out of bounds", TCL_STATIC); 2498 return TCL_ERROR; 2499 } 2500 2501 slavePtr->column = newColumn; 2502 slavePtr->numCols = newNumCols; 2503 return TCL_OK; 2504} 2505 2506/* 2507 *---------------------------------------------------------------------- 2508 * 2509 * SetSlaveRow -- 2510 * 2511 * Update row data for a slave, checking that MAX_ELEMENT bound 2512 * is not passed. 2513 * 2514 * Results: 2515 * TCL_ERROR if out of bounds, TCL_OK otherwise 2516 * 2517 * Side effects: 2518 * Slave fields are updated. 2519 * 2520 *---------------------------------------------------------------------- 2521 */ 2522 2523static int 2524SetSlaveRow( 2525 Tcl_Interp *interp, /* Interp for error message. */ 2526 Gridder *slavePtr, /* Slave to be updated. */ 2527 int row, /* New row or -1 to be unchanged. */ 2528 int numRows) /* New rowspan or -1 to be unchanged. */ 2529{ 2530 int newRow, newNumRows, lastRow; 2531 2532 newRow = (row >= 0) ? row : slavePtr->row; 2533 newNumRows = (numRows >= 1) ? numRows : slavePtr->numRows; 2534 2535 lastRow = ((newRow >= 0) ? newRow : 0) + newNumRows; 2536 if (lastRow >= MAX_ELEMENT) { 2537 Tcl_SetResult(interp, "Row out of bounds", TCL_STATIC); 2538 return TCL_ERROR; 2539 } 2540 2541 slavePtr->row = newRow; 2542 slavePtr->numRows = newNumRows; 2543 return TCL_OK; 2544} 2545 2546/* 2547 *---------------------------------------------------------------------- 2548 * 2549 * CheckSlotData -- 2550 * 2551 * This internal procedure is used to manage the storage for row and 2552 * column (slot) constraints. 2553 * 2554 * Results: 2555 * TRUE if the index is OK, False otherwise. 2556 * 2557 * Side effects: 2558 * A new master grid structure may be created. If so, then it is 2559 * initialized. In addition, additional storage for a row or column 2560 * constraints may be allocated, and the constraint maximums are 2561 * adjusted. 2562 * 2563 *---------------------------------------------------------------------- 2564 */ 2565 2566static int 2567CheckSlotData( 2568 Gridder *masterPtr, /* The geometry master for this grid. */ 2569 int slot, /* Which slot to look at. */ 2570 int slotType, /* ROW or COLUMN. */ 2571 int checkOnly) /* Don't allocate new space if true. */ 2572{ 2573 int numSlot; /* Number of slots already allocated (Space) */ 2574 int end; /* Last used constraint. */ 2575 2576 /* 2577 * If slot is out of bounds, return immediately. 2578 */ 2579 2580 if (slot < 0 || slot >= MAX_ELEMENT) { 2581 return TCL_ERROR; 2582 } 2583 2584 if ((checkOnly == CHECK_ONLY) && (masterPtr->masterDataPtr == NULL)) { 2585 return TCL_ERROR; 2586 } 2587 2588 /* 2589 * If we need to allocate more space, allocate a little extra to avoid 2590 * repeated re-alloc's for large tables. We need enough space to hold all 2591 * of the offsets as well. 2592 */ 2593 2594 InitMasterData(masterPtr); 2595 end = (slotType == ROW) ? masterPtr->masterDataPtr->rowMax : 2596 masterPtr->masterDataPtr->columnMax; 2597 if (checkOnly == CHECK_ONLY) { 2598 return ((end < slot) ? TCL_ERROR : TCL_OK); 2599 } else { 2600 numSlot = (slotType == ROW) ? masterPtr->masterDataPtr->rowSpace 2601 : masterPtr->masterDataPtr->columnSpace; 2602 if (slot >= numSlot) { 2603 int newNumSlot = slot + PREALLOC; 2604 size_t oldSize = numSlot * sizeof(SlotInfo); 2605 size_t newSize = newNumSlot * sizeof(SlotInfo); 2606 SlotInfo *newSI = (SlotInfo *) ckalloc(newSize); 2607 SlotInfo *oldSI = (slotType == ROW) 2608 ? masterPtr->masterDataPtr->rowPtr 2609 : masterPtr->masterDataPtr->columnPtr; 2610 2611 memcpy(newSI, oldSI, oldSize); 2612 memset(newSI+numSlot, 0, newSize - oldSize); 2613 ckfree((char *) oldSI); 2614 if (slotType == ROW) { 2615 masterPtr->masterDataPtr->rowPtr = newSI; 2616 masterPtr->masterDataPtr->rowSpace = newNumSlot; 2617 } else { 2618 masterPtr->masterDataPtr->columnPtr = newSI; 2619 masterPtr->masterDataPtr->columnSpace = newNumSlot; 2620 } 2621 } 2622 if (slot >= end && checkOnly != CHECK_SPACE) { 2623 if (slotType == ROW) { 2624 masterPtr->masterDataPtr->rowMax = slot+1; 2625 } else { 2626 masterPtr->masterDataPtr->columnMax = slot+1; 2627 } 2628 } 2629 return TCL_OK; 2630 } 2631} 2632 2633/* 2634 *---------------------------------------------------------------------- 2635 * 2636 * InitMasterData -- 2637 * 2638 * This internal procedure is used to allocate and initialize the data 2639 * for a geometry master, if the data doesn't exist already. 2640 * 2641 * Results: 2642 * none 2643 * 2644 * Side effects: 2645 * A new master grid structure may be created. If so, then it is 2646 * initialized. 2647 * 2648 *---------------------------------------------------------------------- 2649 */ 2650 2651static void 2652InitMasterData( 2653 Gridder *masterPtr) 2654{ 2655 if (masterPtr->masterDataPtr == NULL) { 2656 GridMaster *gridPtr = masterPtr->masterDataPtr = (GridMaster *) 2657 ckalloc(sizeof(GridMaster)); 2658 size_t size = sizeof(SlotInfo) * TYPICAL_SIZE; 2659 2660 gridPtr->columnEnd = 0; 2661 gridPtr->columnMax = 0; 2662 gridPtr->columnPtr = (SlotInfo *) ckalloc(size); 2663 gridPtr->columnSpace = TYPICAL_SIZE; 2664 gridPtr->rowEnd = 0; 2665 gridPtr->rowMax = 0; 2666 gridPtr->rowPtr = (SlotInfo *) ckalloc(size); 2667 gridPtr->rowSpace = TYPICAL_SIZE; 2668 gridPtr->startX = 0; 2669 gridPtr->startY = 0; 2670 gridPtr->anchor = GRID_DEFAULT_ANCHOR; 2671 2672 memset(gridPtr->columnPtr, 0, size); 2673 memset(gridPtr->rowPtr, 0, size); 2674 } 2675} 2676 2677/* 2678 *---------------------------------------------------------------------- 2679 * 2680 * Unlink -- 2681 * 2682 * Remove a grid from its master's list of slaves. 2683 * 2684 * Results: 2685 * None. 2686 * 2687 * Side effects: 2688 * The master will be scheduled for re-arranging, and the size of the 2689 * grid will be adjusted accordingly 2690 * 2691 *---------------------------------------------------------------------- 2692 */ 2693 2694static void 2695Unlink( 2696 register Gridder *slavePtr) /* Window to unlink. */ 2697{ 2698 register Gridder *masterPtr, *slavePtr2; 2699 2700 masterPtr = slavePtr->masterPtr; 2701 if (masterPtr == NULL) { 2702 return; 2703 } 2704 2705 if (masterPtr->slavePtr == slavePtr) { 2706 masterPtr->slavePtr = slavePtr->nextPtr; 2707 } else { 2708 for (slavePtr2=masterPtr->slavePtr ; ; slavePtr2=slavePtr2->nextPtr) { 2709 if (slavePtr2 == NULL) { 2710 Tcl_Panic("Unlink couldn't find previous window"); 2711 } 2712 if (slavePtr2->nextPtr == slavePtr) { 2713 slavePtr2->nextPtr = slavePtr->nextPtr; 2714 break; 2715 } 2716 } 2717 } 2718 if (!(masterPtr->flags & REQUESTED_RELAYOUT)) { 2719 masterPtr->flags |= REQUESTED_RELAYOUT; 2720 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr); 2721 } 2722 if (masterPtr->abortPtr != NULL) { 2723 *masterPtr->abortPtr = 1; 2724 } 2725 2726 SetGridSize(slavePtr->masterPtr); 2727 slavePtr->masterPtr = NULL; 2728} 2729 2730/* 2731 *---------------------------------------------------------------------- 2732 * 2733 * DestroyGrid -- 2734 * 2735 * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release to 2736 * clean up the internal structure of a grid at a safe time (when no-one 2737 * is using it anymore). Cleaning up the grid involves freeing the main 2738 * structure for all windows and the master structure for geometry 2739 * managers. 2740 * 2741 * Results: 2742 * None. 2743 * 2744 * Side effects: 2745 * Everything associated with the grid is freed up. 2746 * 2747 *---------------------------------------------------------------------- 2748 */ 2749 2750static void 2751DestroyGrid( 2752 char *memPtr) /* Info about window that is now dead. */ 2753{ 2754 register Gridder *gridPtr = (Gridder *) memPtr; 2755 2756 if (gridPtr->masterDataPtr != NULL) { 2757 if (gridPtr->masterDataPtr->rowPtr != NULL) { 2758 ckfree((char *) gridPtr->masterDataPtr -> rowPtr); 2759 } 2760 if (gridPtr->masterDataPtr->columnPtr != NULL) { 2761 ckfree((char *) gridPtr->masterDataPtr -> columnPtr); 2762 } 2763 ckfree((char *) gridPtr->masterDataPtr); 2764 } 2765 ckfree((char *) gridPtr); 2766} 2767 2768/* 2769 *---------------------------------------------------------------------- 2770 * 2771 * GridStructureProc -- 2772 * 2773 * This procedure is invoked by the Tk event dispatcher in response to 2774 * StructureNotify events. 2775 * 2776 * Results: 2777 * None. 2778 * 2779 * Side effects: 2780 * If a window was just deleted, clean up all its grid-related 2781 * information. If it was just resized, re-configure its slaves, if any. 2782 * 2783 *---------------------------------------------------------------------- 2784 */ 2785 2786static void 2787GridStructureProc( 2788 ClientData clientData, /* Our information about window referred to by 2789 * eventPtr. */ 2790 XEvent *eventPtr) /* Describes what just happened. */ 2791{ 2792 register Gridder *gridPtr = (Gridder *) clientData; 2793 TkDisplay *dispPtr = ((TkWindow *) gridPtr->tkwin)->dispPtr; 2794 2795 if (eventPtr->type == ConfigureNotify) { 2796 if ((gridPtr->slavePtr != NULL) 2797 && !(gridPtr->flags & REQUESTED_RELAYOUT)) { 2798 gridPtr->flags |= REQUESTED_RELAYOUT; 2799 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr); 2800 } 2801 if ((gridPtr->masterPtr != NULL) && 2802 (gridPtr->doubleBw != 2*Tk_Changes(gridPtr->tkwin)->border_width)) { 2803 if (!(gridPtr->masterPtr->flags & REQUESTED_RELAYOUT)) { 2804 gridPtr->doubleBw = 2*Tk_Changes(gridPtr->tkwin)->border_width; 2805 gridPtr->masterPtr->flags |= REQUESTED_RELAYOUT; 2806 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr->masterPtr); 2807 } 2808 } 2809 } else if (eventPtr->type == DestroyNotify) { 2810 register Gridder *gridPtr2, *nextPtr; 2811 2812 if (gridPtr->masterPtr != NULL) { 2813 Unlink(gridPtr); 2814 } 2815 for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL; 2816 gridPtr2 = nextPtr) { 2817 Tk_UnmapWindow(gridPtr2->tkwin); 2818 gridPtr2->masterPtr = NULL; 2819 nextPtr = gridPtr2->nextPtr; 2820 gridPtr2->nextPtr = NULL; 2821 } 2822 Tcl_DeleteHashEntry(Tcl_FindHashEntry(&dispPtr->gridHashTable, 2823 (char *) gridPtr->tkwin)); 2824 if (gridPtr->flags & REQUESTED_RELAYOUT) { 2825 Tcl_CancelIdleCall(ArrangeGrid, (ClientData) gridPtr); 2826 } 2827 gridPtr->tkwin = NULL; 2828 Tcl_EventuallyFree((ClientData) gridPtr, DestroyGrid); 2829 } else if (eventPtr->type == MapNotify) { 2830 if ((gridPtr->slavePtr != NULL) 2831 && !(gridPtr->flags & REQUESTED_RELAYOUT)) { 2832 gridPtr->flags |= REQUESTED_RELAYOUT; 2833 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr); 2834 } 2835 } else if (eventPtr->type == UnmapNotify) { 2836 register Gridder *gridPtr2; 2837 2838 for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL; 2839 gridPtr2 = gridPtr2->nextPtr) { 2840 Tk_UnmapWindow(gridPtr2->tkwin); 2841 } 2842 } 2843} 2844 2845/* 2846 *---------------------------------------------------------------------- 2847 * 2848 * ConfigureSlaves -- 2849 * 2850 * This implements the guts of the "grid configure" command. Given a list 2851 * of slaves and configuration options, it arranges for the grid to 2852 * manage the slaves and sets the specified options. Arguments consist 2853 * of windows or window shortcuts followed by "-option value" pairs. 2854 * 2855 * Results: 2856 * TCL_OK is returned if all went well. Otherwise, TCL_ERROR is returned 2857 * and the interp's result is set to contain an error message. 2858 * 2859 * Side effects: 2860 * Slave windows get taken over by the grid. 2861 * 2862 *---------------------------------------------------------------------- 2863 */ 2864 2865static int 2866ConfigureSlaves( 2867 Tcl_Interp *interp, /* Interpreter for error reporting. */ 2868 Tk_Window tkwin, /* Any window in application containing 2869 * slaves. Used to look up slave names. */ 2870 int objc, /* Number of elements in argv. */ 2871 Tcl_Obj *CONST objv[]) /* Argument objects: contains one or more 2872 * window names followed by any number of 2873 * "option value" pairs. Caller must make sure 2874 * that there is at least one window name. */ 2875{ 2876 Gridder *masterPtr = NULL; 2877 Gridder *slavePtr; 2878 Tk_Window other, slave, parent, ancestor; 2879 int i, j, tmp; 2880 int numWindows; 2881 int width; 2882 int defaultRow = -1; 2883 int defaultColumn = 0; /* Default column number */ 2884 int defaultColumnSpan = 1; /* Default number of columns */ 2885 char *lastWindow; /* Use this window to base current row/col 2886 * on */ 2887 int numSkip; /* Number of 'x' found */ 2888 static CONST char *optionStrings[] = { 2889 "-column", "-columnspan", "-in", "-ipadx", "-ipady", 2890 "-padx", "-pady", "-row", "-rowspan", "-sticky", NULL 2891 }; 2892 enum options { 2893 CONF_COLUMN, CONF_COLUMNSPAN, CONF_IN, CONF_IPADX, CONF_IPADY, 2894 CONF_PADX, CONF_PADY, CONF_ROW, CONF_ROWSPAN, CONF_STICKY }; 2895 int index; 2896 char *string; 2897 char firstChar; 2898 int positionGiven; 2899 2900 /* 2901 * Count the number of windows, or window short-cuts. 2902 */ 2903 2904 firstChar = 0; 2905 for (numWindows=0, i=0; i < objc; i++) { 2906 int length; 2907 char prevChar = firstChar; 2908 2909 string = Tcl_GetStringFromObj(objv[i], &length); 2910 firstChar = string[0]; 2911 2912 if (firstChar == '.') { 2913 /* 2914 * Check that windows are valid, and locate the first slave's 2915 * parent window (default for -in). 2916 */ 2917 2918 if (TkGetWindowFromObj(interp, tkwin, objv[i], &slave) != TCL_OK) { 2919 return TCL_ERROR; 2920 } 2921 if (masterPtr == NULL) { 2922 parent = Tk_Parent(slave); 2923 if (parent != NULL) { 2924 masterPtr = GetGrid(parent); 2925 InitMasterData(masterPtr); 2926 } 2927 } 2928 numWindows++; 2929 continue; 2930 } 2931 if (length > 1 && i == 0) { 2932 Tcl_AppendResult(interp, "bad argument \"", string, 2933 "\": must be name of window", NULL); 2934 return TCL_ERROR; 2935 } 2936 if (length > 1 && firstChar == '-') { 2937 break; 2938 } 2939 if (length > 1) { 2940 Tcl_AppendResult(interp, "unexpected parameter, \"", 2941 string, "\", in configure list. ", 2942 "Should be window name or option", NULL); 2943 return TCL_ERROR; 2944 } 2945 2946 if ((firstChar == REL_HORIZ) && ((numWindows == 0) || 2947 (prevChar == REL_SKIP) || (prevChar == REL_VERT))) { 2948 Tcl_AppendResult(interp, 2949 "Must specify window before shortcut '-'.", NULL); 2950 return TCL_ERROR; 2951 } 2952 2953 if ((firstChar == REL_VERT) || (firstChar == REL_SKIP) 2954 || (firstChar == REL_HORIZ)) { 2955 continue; 2956 } 2957 2958 Tcl_AppendResult(interp, "invalid window shortcut, \"", 2959 string, "\" should be '-', 'x', or '^'", NULL); 2960 return TCL_ERROR; 2961 } 2962 numWindows = i; 2963 2964 if ((objc - numWindows) & 1) { 2965 Tcl_AppendResult(interp, "extra option or option with no value", NULL); 2966 return TCL_ERROR; 2967 } 2968 2969 /* 2970 * Go through all options looking for -in and -row, which are needed to be 2971 * found first to handle the special case where ^ is used on a row without 2972 * windows names, but with an -in option. Since all options are checked 2973 * here, we do not need to handle the error case again later. 2974 */ 2975 2976 for (i = numWindows; i < objc; i += 2) { 2977 if (Tcl_GetIndexFromObj(interp, objv[i], optionStrings, "option", 0, 2978 &index) != TCL_OK) { 2979 return TCL_ERROR; 2980 } 2981 if (index == CONF_IN) { 2982 if (TkGetWindowFromObj(interp, tkwin, objv[i+1], &other) != 2983 TCL_OK) { 2984 return TCL_ERROR; 2985 } 2986 masterPtr = GetGrid(other); 2987 InitMasterData(masterPtr); 2988 } else if (index == CONF_ROW) { 2989 if (Tcl_GetIntFromObj(interp, objv[i+1], &tmp) != TCL_OK 2990 || tmp < 0) { 2991 Tcl_ResetResult(interp); 2992 Tcl_AppendResult(interp, "bad row value \"", 2993 Tcl_GetString(objv[i+1]), "\": must be ", 2994 "a non-negative integer", NULL); 2995 return TCL_ERROR; 2996 } 2997 defaultRow = tmp; 2998 } 2999 } 3000 3001 /* 3002 * If no -row is given, use the first unoccupied row of the master. 3003 */ 3004 3005 if (defaultRow < 0) { 3006 if (masterPtr != NULL && masterPtr->masterDataPtr != NULL) { 3007 SetGridSize(masterPtr); 3008 defaultRow = masterPtr->masterDataPtr->rowEnd; 3009 } else { 3010 defaultRow = 0; 3011 } 3012 } 3013 3014 /* 3015 * Iterate over all of the slave windows and short-cuts, parsing options 3016 * for each slave. It's a bit wasteful to re-parse the options for each 3017 * slave, but things get too messy if we try to parse the arguments just 3018 * once at the beginning. For example, if a slave already is managed we 3019 * want to just change a few existing values without resetting everything. 3020 * If there are multiple windows, the -in option only gets processed for 3021 * the first window. 3022 */ 3023 3024 positionGiven = 0; 3025 for (j = 0; j < numWindows; j++) { 3026 string = Tcl_GetString(objv[j]); 3027 firstChar = string[0]; 3028 3029 /* 3030 * '^' and 'x' cause us to skip a column. '-' is processed as part of 3031 * its preceeding slave. 3032 */ 3033 3034 if ((firstChar == REL_VERT) || (firstChar == REL_SKIP)) { 3035 defaultColumn++; 3036 continue; 3037 } 3038 if (firstChar == REL_HORIZ) { 3039 continue; 3040 } 3041 3042 for (defaultColumnSpan = 1; j + defaultColumnSpan < numWindows; 3043 defaultColumnSpan++) { 3044 char *string = Tcl_GetString(objv[j + defaultColumnSpan]); 3045 3046 if (*string != REL_HORIZ) { 3047 break; 3048 } 3049 } 3050 3051 if (TkGetWindowFromObj(interp, tkwin, objv[j], &slave) != TCL_OK) { 3052 return TCL_ERROR; 3053 } 3054 3055 if (Tk_TopWinHierarchy(slave)) { 3056 Tcl_AppendResult(interp, "can't manage \"", Tcl_GetString(objv[j]), 3057 "\": it's a top-level window", NULL); 3058 return TCL_ERROR; 3059 } 3060 slavePtr = GetGrid(slave); 3061 3062 /* 3063 * The following statement is taken from tkPack.c: 3064 * 3065 * "If the slave isn't currently managed, reset all of its 3066 * configuration information to default values (there could be old 3067 * values left from a previous packer)." 3068 * 3069 * I [D.S.] disagree with this statement. If a slave is disabled 3070 * (using "forget") and then re-enabled, I submit that 90% of the time 3071 * the programmer will want it to retain its old configuration 3072 * information. If the programmer doesn't want this behavior, then the 3073 * defaults can be reestablished by hand, without having to worry 3074 * about keeping track of the old state. 3075 */ 3076 3077 for (i = numWindows; i < objc; i += 2) { 3078 Tcl_GetIndexFromObj(interp, objv[i], optionStrings, "option", 0, 3079 &index); 3080 switch ((enum options) index) { 3081 case CONF_COLUMN: 3082 if (Tcl_GetIntFromObj(NULL, objv[i+1], &tmp) != TCL_OK 3083 || tmp < 0) { 3084 Tcl_AppendResult(interp, "bad column value \"", 3085 Tcl_GetString(objv[i+1]), "\": must be ", 3086 "a non-negative integer", NULL); 3087 return TCL_ERROR; 3088 } 3089 if (SetSlaveColumn(interp, slavePtr, tmp, -1) != TCL_OK) { 3090 return TCL_ERROR; 3091 } 3092 break; 3093 case CONF_COLUMNSPAN: 3094 if (Tcl_GetIntFromObj(NULL, objv[i+1], &tmp) != TCL_OK 3095 || tmp <= 0) { 3096 Tcl_AppendResult(interp, "bad columnspan value \"", 3097 Tcl_GetString(objv[i+1]), "\": must be ", 3098 "a positive integer", NULL); 3099 return TCL_ERROR; 3100 } 3101 if (SetSlaveColumn(interp, slavePtr, -1, tmp) != TCL_OK) { 3102 return TCL_ERROR; 3103 } 3104 break; 3105 case CONF_IN: 3106 if (TkGetWindowFromObj(interp, tkwin, objv[i+1], 3107 &other) != TCL_OK) { 3108 return TCL_ERROR; 3109 } 3110 if (other == slave) { 3111 Tcl_SetResult(interp, "Window can't be managed in itself", 3112 TCL_STATIC); 3113 return TCL_ERROR; 3114 } 3115 positionGiven = 1; 3116 masterPtr = GetGrid(other); 3117 InitMasterData(masterPtr); 3118 break; 3119 case CONF_STICKY: { 3120 int sticky = StringToSticky(Tcl_GetString(objv[i+1])); 3121 3122 if (sticky == -1) { 3123 Tcl_AppendResult(interp, "bad stickyness value \"", 3124 Tcl_GetString(objv[i+1]), "\": must be ", 3125 "a string containing n, e, s, and/or w", NULL); 3126 return TCL_ERROR; 3127 } 3128 slavePtr->sticky = sticky; 3129 break; 3130 } 3131 case CONF_IPADX: 3132 if ((Tk_GetPixelsFromObj(NULL, slave, objv[i+1], 3133 &tmp) != TCL_OK) || (tmp < 0)) { 3134 Tcl_AppendResult(interp, "bad ipadx value \"", 3135 Tcl_GetString(objv[i+1]), "\": must be ", 3136 "positive screen distance", NULL); 3137 return TCL_ERROR; 3138 } 3139 slavePtr->iPadX = tmp*2; 3140 break; 3141 case CONF_IPADY: 3142 if ((Tk_GetPixelsFromObj(NULL, slave, objv[i+1], 3143 &tmp) != TCL_OK) || (tmp < 0)) { 3144 Tcl_AppendResult(interp, "bad ipady value \"", 3145 Tcl_GetString(objv[i+1]), "\": must be ", 3146 "positive screen distance", NULL); 3147 return TCL_ERROR; 3148 } 3149 slavePtr->iPadY = tmp*2; 3150 break; 3151 case CONF_PADX: 3152 if (TkParsePadAmount(interp, tkwin, objv[i+1], 3153 &slavePtr->padLeft, &slavePtr->padX) != TCL_OK) { 3154 return TCL_ERROR; 3155 } 3156 break; 3157 case CONF_PADY: 3158 if (TkParsePadAmount(interp, tkwin, objv[i+1], 3159 &slavePtr->padTop, &slavePtr->padY) != TCL_OK) { 3160 return TCL_ERROR; 3161 } 3162 break; 3163 case CONF_ROW: 3164 if (Tcl_GetIntFromObj(NULL, objv[i+1], &tmp) != TCL_OK 3165 || tmp < 0) { 3166 Tcl_AppendResult(interp, "bad row value \"", 3167 Tcl_GetString(objv[i+1]), 3168 "\": must be a non-negative integer", NULL); 3169 return TCL_ERROR; 3170 } 3171 if (SetSlaveRow(interp, slavePtr, tmp, -1) != TCL_OK) { 3172 return TCL_ERROR; 3173 } 3174 break; 3175 case CONF_ROWSPAN: 3176 if ((Tcl_GetIntFromObj(NULL, objv[i+1], &tmp) != TCL_OK) 3177 || tmp <= 0) { 3178 Tcl_AppendResult(interp, "bad rowspan value \"", 3179 Tcl_GetString(objv[i+1]), 3180 "\": must be a positive integer", NULL); 3181 return TCL_ERROR; 3182 } 3183 if (SetSlaveRow(interp, slavePtr, -1, tmp) != TCL_OK) { 3184 return TCL_ERROR; 3185 } 3186 break; 3187 } 3188 } 3189 3190 /* 3191 * If no position was specified via -in and the slave is already 3192 * packed, then leave it in its current location. 3193 */ 3194 3195 if (!positionGiven && (slavePtr->masterPtr != NULL)) { 3196 masterPtr = slavePtr->masterPtr; 3197 goto scheduleLayout; 3198 } 3199 3200 /* 3201 * If the same -in window is passed in again, then just leave it in 3202 * its current location. 3203 */ 3204 3205 if (positionGiven && (masterPtr == slavePtr->masterPtr)) { 3206 goto scheduleLayout; 3207 } 3208 3209 /* 3210 * Make sure we have a geometry master. We look at: 3211 * 1) the -in flag 3212 * 2) the parent of the first slave. 3213 */ 3214 3215 parent = Tk_Parent(slave); 3216 if (masterPtr == NULL) { 3217 masterPtr = GetGrid(parent); 3218 InitMasterData(masterPtr); 3219 } 3220 3221 if (slavePtr->masterPtr != NULL && slavePtr->masterPtr != masterPtr) { 3222 Unlink(slavePtr); 3223 slavePtr->masterPtr = NULL; 3224 } 3225 3226 if (slavePtr->masterPtr == NULL) { 3227 Gridder *tempPtr = masterPtr->slavePtr; 3228 3229 slavePtr->masterPtr = masterPtr; 3230 masterPtr->slavePtr = slavePtr; 3231 slavePtr->nextPtr = tempPtr; 3232 } 3233 3234 /* 3235 * Make sure that the slave's parent is either the master or an 3236 * ancestor of the master, and that the master and slave aren't the 3237 * same. 3238 */ 3239 3240 for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) { 3241 if (ancestor == parent) { 3242 break; 3243 } 3244 if (Tk_TopWinHierarchy(ancestor)) { 3245 Tcl_AppendResult(interp, "can't put ", Tcl_GetString(objv[j]), 3246 " inside ", Tk_PathName(masterPtr->tkwin), NULL); 3247 Unlink(slavePtr); 3248 return TCL_ERROR; 3249 } 3250 } 3251 3252 /* 3253 * Try to make sure our master isn't managed by us. 3254 */ 3255 3256 if (masterPtr->masterPtr == slavePtr) { 3257 Tcl_AppendResult(interp, "can't put ", Tcl_GetString(objv[j]), 3258 " inside ", Tk_PathName(masterPtr->tkwin), 3259 ", would cause management loop.", NULL); 3260 Unlink(slavePtr); 3261 return TCL_ERROR; 3262 } 3263 3264 Tk_ManageGeometry(slave, &gridMgrType, (ClientData) slavePtr); 3265 3266 /* 3267 * Assign default position information. 3268 */ 3269 3270 if (slavePtr->column == -1) { 3271 if (SetSlaveColumn(interp, slavePtr, defaultColumn,-1) != TCL_OK){ 3272 return TCL_ERROR; 3273 } 3274 } 3275 if (SetSlaveColumn(interp, slavePtr, -1, 3276 slavePtr->numCols + defaultColumnSpan - 1) != TCL_OK) { 3277 return TCL_ERROR; 3278 } 3279 if (slavePtr->row == -1) { 3280 if (SetSlaveRow(interp, slavePtr, defaultRow, -1) != TCL_OK) { 3281 return TCL_ERROR; 3282 } 3283 } 3284 defaultColumn += slavePtr->numCols; 3285 defaultColumnSpan = 1; 3286 3287 /* 3288 * Arrange for the master to be re-arranged at the first idle moment. 3289 */ 3290 3291 scheduleLayout: 3292 if (masterPtr->abortPtr != NULL) { 3293 *masterPtr->abortPtr = 1; 3294 } 3295 if (!(masterPtr->flags & REQUESTED_RELAYOUT)) { 3296 masterPtr->flags |= REQUESTED_RELAYOUT; 3297 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr); 3298 } 3299 } 3300 3301 /* 3302 * Now look for all the "^"'s. 3303 */ 3304 3305 lastWindow = NULL; 3306 numSkip = 0; 3307 for (j = 0; j < numWindows; j++) { 3308 struct Gridder *otherPtr; 3309 int match; /* Found a match for the ^ */ 3310 int lastRow, lastColumn; /* Implied end of table. */ 3311 3312 string = Tcl_GetString(objv[j]); 3313 firstChar = string[0]; 3314 3315 if (firstChar == '.') { 3316 lastWindow = string; 3317 numSkip = 0; 3318 } 3319 if (firstChar == REL_SKIP) { 3320 numSkip++; 3321 } 3322 if (firstChar != REL_VERT) { 3323 continue; 3324 } 3325 3326 if (masterPtr == NULL) { 3327 Tcl_AppendResult(interp, "can't use '^', cant find master", NULL); 3328 return TCL_ERROR; 3329 } 3330 3331 /* 3332 * Count the number of consecutive ^'s starting from this position. 3333 */ 3334 3335 for (width = 1; width + j < numWindows; width++) { 3336 char *string = Tcl_GetString(objv[j+width]); 3337 3338 if (*string != REL_VERT) { 3339 break; 3340 } 3341 } 3342 3343 /* 3344 * Find the implied grid location of the ^ 3345 */ 3346 3347 if (lastWindow == NULL) { 3348 lastRow = defaultRow - 1; 3349 lastColumn = 0; 3350 } else { 3351 other = Tk_NameToWindow(interp, lastWindow, tkwin); 3352 otherPtr = GetGrid(other); 3353 lastRow = otherPtr->row + otherPtr->numRows - 2; 3354 lastColumn = otherPtr->column + otherPtr->numCols; 3355 } 3356 3357 lastColumn += numSkip; 3358 3359 match = 0; 3360 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; 3361 slavePtr = slavePtr->nextPtr) { 3362 3363 if (slavePtr->column == lastColumn 3364 && slavePtr->row + slavePtr->numRows - 1 == lastRow) { 3365 if (slavePtr->numCols <= width) { 3366 if (SetSlaveRow(interp, slavePtr, -1, 3367 slavePtr->numRows + 1) != TCL_OK) { 3368 return TCL_ERROR; 3369 } 3370 match++; 3371 j += slavePtr->numCols - 1; 3372 lastWindow = Tk_PathName(slavePtr->tkwin); 3373 numSkip = 0; 3374 break; 3375 } 3376 } 3377 } 3378 if (!match) { 3379 Tcl_AppendResult(interp, "can't find slave to extend with \"^\".", 3380 NULL); 3381 return TCL_ERROR; 3382 } 3383 } 3384 3385 if (masterPtr == NULL) { 3386 Tcl_AppendResult(interp, "can't determine master window", NULL); 3387 return TCL_ERROR; 3388 } 3389 SetGridSize(masterPtr); 3390 return TCL_OK; 3391} 3392 3393/* 3394 *---------------------------------------------------------------------- 3395 * 3396 * StickyToString 3397 * 3398 * Converts the internal boolean combination of "sticky" bits onto a Tcl 3399 * list element containing zero or more of n, s, e, or w. 3400 * 3401 * Results: 3402 * A string is placed into the "result" pointer. 3403 * 3404 * Side effects: 3405 * none. 3406 * 3407 *---------------------------------------------------------------------- 3408 */ 3409 3410static void 3411StickyToString( 3412 int flags, /* The sticky flags. */ 3413 char *result) /* Where to put the result. */ 3414{ 3415 int count = 0; 3416 if (flags&STICK_NORTH) { 3417 result[count++] = 'n'; 3418 } 3419 if (flags&STICK_EAST) { 3420 result[count++] = 'e'; 3421 } 3422 if (flags&STICK_SOUTH) { 3423 result[count++] = 's'; 3424 } 3425 if (flags&STICK_WEST) { 3426 result[count++] = 'w'; 3427 } 3428 if (count) { 3429 result[count] = '\0'; 3430 } else { 3431 sprintf(result, "{}"); 3432 } 3433} 3434 3435/* 3436 *---------------------------------------------------------------------- 3437 * 3438 * StringToSticky -- 3439 * 3440 * Converts an ascii string representing a widgets stickyness into the 3441 * boolean result. 3442 * 3443 * Results: 3444 * The boolean combination of the "sticky" bits is retuned. If an error 3445 * occurs, such as an invalid character, -1 is returned instead. 3446 * 3447 * Side effects: 3448 * none 3449 * 3450 *---------------------------------------------------------------------- 3451 */ 3452 3453static int 3454StringToSticky( 3455 char *string) 3456{ 3457 int sticky = 0; 3458 char c; 3459 3460 while ((c = *string++) != '\0') { 3461 switch (c) { 3462 case 'n': case 'N': 3463 sticky |= STICK_NORTH; 3464 break; 3465 case 'e': case 'E': 3466 sticky |= STICK_EAST; 3467 break; 3468 case 's': case 'S': 3469 sticky |= STICK_SOUTH; 3470 break; 3471 case 'w': case 'W': 3472 sticky |= STICK_WEST; 3473 break; 3474 case ' ': case ',': case '\t': case '\r': case '\n': 3475 break; 3476 default: 3477 return -1; 3478 } 3479 } 3480 return sticky; 3481} 3482 3483/* 3484 *---------------------------------------------------------------------- 3485 * 3486 * NewPairObj -- 3487 * 3488 * Creates a new list object and fills it with two integer objects. 3489 * 3490 * Results: 3491 * The newly created list object is returned. 3492 * 3493 * Side effects: 3494 * None. 3495 * 3496 *---------------------------------------------------------------------- 3497 */ 3498 3499static Tcl_Obj * 3500NewPairObj( 3501 int val1, int val2) 3502{ 3503 Tcl_Obj *ary[2]; 3504 3505 ary[0] = Tcl_NewIntObj(val1); 3506 ary[1] = Tcl_NewIntObj(val2); 3507 return Tcl_NewListObj(2, ary); 3508} 3509 3510/* 3511 *---------------------------------------------------------------------- 3512 * 3513 * NewQuadObj -- 3514 * 3515 * Creates a new list object and fills it with four integer objects. 3516 * 3517 * Results: 3518 * The newly created list object is returned. 3519 * 3520 * Side effects: 3521 * None. 3522 * 3523 *---------------------------------------------------------------------- 3524 */ 3525 3526static Tcl_Obj * 3527NewQuadObj( 3528 int val1, int val2, int val3, int val4) 3529{ 3530 Tcl_Obj *ary[4]; 3531 3532 ary[0] = Tcl_NewIntObj(val1); 3533 ary[1] = Tcl_NewIntObj(val2); 3534 ary[2] = Tcl_NewIntObj(val3); 3535 ary[3] = Tcl_NewIntObj(val4); 3536 return Tcl_NewListObj(4, ary); 3537} 3538 3539/* 3540 * Local Variables: 3541 * mode: c 3542 * c-basic-offset: 4 3543 * fill-column: 78 3544 * End: 3545 */ 3546