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