1/* 2 * tkPanedWindow.c -- 3 * 4 * This module implements "paned window" widgets that are object based. A 5 * "paned window" is a widget that manages the geometry for some number 6 * of other widgets, placing a movable "sash" between them, which can be 7 * used to alter the relative sizes of adjacent widgets. 8 * 9 * Copyright (c) 1997 Sun Microsystems, Inc. 10 * Copyright (c) 2000 Ajuba Solutions. 11 * 12 * See the file "license.terms" for information on usage and redistribution of 13 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 14 * 15 * RCS: @(#) $Id$ 16 */ 17 18#include "default.h" 19#include "tkInt.h" 20 21/* 22 * Flag values for "sticky"ness. The 16 combinations subsume the packer's 23 * notion of anchor and fill. 24 * 25 * STICK_NORTH This window sticks to the top of its cavity. 26 * STICK_EAST This window sticks to the right edge of its cavity. 27 * STICK_SOUTH This window sticks to the bottom of its cavity. 28 * STICK_WEST This window sticks to the left edge of its cavity. 29 */ 30 31#define STICK_NORTH 1 32#define STICK_EAST 2 33#define STICK_SOUTH 4 34#define STICK_WEST 8 35 36/* 37 * The following table defines the legal values for the -orient option. 38 */ 39 40static char *orientStrings[] = { 41 "horizontal", "vertical", NULL 42}; 43 44enum orient { ORIENT_HORIZONTAL, ORIENT_VERTICAL }; 45 46/* 47 * The following table defines the legal values for the -stretch option. 48 */ 49 50static char *stretchStrings[] = { 51 "always", "first", "last", "middle", "never", NULL 52}; 53 54enum stretch { 55 STRETCH_ALWAYS, /* Always give extra space to this pane. */ 56 STRETCH_FIRST, /* Give extra space to pane if it is first. */ 57 STRETCH_LAST, /* Give extra space to pane if it is last. */ 58 STRETCH_MIDDLE, /* Give extra space to pane only if it is 59 * neither first nor last. */ 60 STRETCH_NEVER /* Never give extra space to this pane. */ 61}; 62 63/* 64 * Codify the stretchiness rule in one place. 65 */ 66 67#define IsStretchable(stretch,index,first,last) \ 68 (((stretch) == STRETCH_ALWAYS) || \ 69 ((stretch) == STRETCH_FIRST && (index) == (first)) || \ 70 ((stretch) == STRETCH_LAST && (index) == (last)) || \ 71 ((stretch) == STRETCH_MIDDLE && (index) != (first) && (index) != (last))) 72 73typedef struct { 74 Tk_OptionTable pwOptions; /* Token for paned window option table. */ 75 Tk_OptionTable slaveOpts; /* Token for slave cget option table. */ 76} OptionTables; 77 78/* 79 * One structure of the following type is kept for each window 80 * managed by a paned window widget. 81 */ 82 83typedef struct Slave { 84 Tk_Window tkwin; /* Window being managed. */ 85 int minSize; /* Minimum size of this pane, on the relevant 86 * axis, in pixels. */ 87 int padx; /* Additional padding requested for slave, in 88 * the x dimension. */ 89 int pady; /* Additional padding requested for slave, in 90 * the y dimension. */ 91 Tcl_Obj *widthPtr, *heightPtr; 92 /* Tcl_Obj rep's of slave width/height, to 93 * allow for null values. */ 94 int width; /* Slave width. */ 95 int height; /* Slave height. */ 96 int sticky; /* Sticky string. */ 97 int x, y; /* Coordinates of the widget. */ 98 int paneWidth, paneHeight; /* Pane dimensions (may be different from 99 * slave width/height). */ 100 int sashx, sashy; /* Coordinates of the sash of the right or 101 * bottom of this pane. */ 102 int markx, marky; /* Coordinates of the last mark set for the 103 * sash. */ 104 int handlex, handley; /* Coordinates of the sash handle. */ 105 enum stretch stretch; /* Controls how slave grows/shrinks */ 106 int hide; /* Controls visibility of pane */ 107 struct PanedWindow *masterPtr; 108 /* Paned window managing the window. */ 109 Tk_Window after; /* Placeholder for parsing options. */ 110 Tk_Window before; /* Placeholder for parsing options. */ 111} Slave; 112 113/* 114 * A data structure of the following type is kept for each paned window widget 115 * managed by this file: 116 */ 117 118typedef struct PanedWindow { 119 Tk_Window tkwin; /* Window that embodies the paned window. */ 120 Tk_Window proxywin; /* Window for the resizing proxy. */ 121 Display *display; /* X's token for the window's display. */ 122 Tcl_Interp *interp; /* Interpreter associated with widget. */ 123 Tcl_Command widgetCmd; /* Token for square's widget command. */ 124 Tk_OptionTable optionTable; /* Token representing the configuration 125 * specifications. */ 126 Tk_OptionTable slaveOpts; /* Token for slave cget table. */ 127 Tk_3DBorder background; /* Background color. */ 128 int borderWidth; /* Value of -borderwidth option. */ 129 int relief; /* 3D border effect (TK_RELIEF_RAISED, etc) */ 130 Tcl_Obj *widthPtr; /* Tcl_Obj rep for width. */ 131 Tcl_Obj *heightPtr; /* Tcl_Obj rep for height. */ 132 int width, height; /* Width and height of the widget. */ 133 enum orient orient; /* Orientation of the widget. */ 134 Tk_Cursor cursor; /* Current cursor for window, or None. */ 135 int resizeOpaque; /* Boolean indicating whether resize should be 136 * opaque or rubberband style. */ 137 int sashRelief; /* Relief used to draw sash. */ 138 int sashWidth; /* Width of each sash, in pixels. */ 139 Tcl_Obj *sashWidthPtr; /* Tcl_Obj rep for sash width. */ 140 int sashPad; /* Additional padding around each sash. */ 141 Tcl_Obj *sashPadPtr; /* Tcl_Obj rep for sash padding. */ 142 int showHandle; /* Boolean indicating whether sash handles 143 * should be drawn. */ 144 int handleSize; /* Size of one side of a sash handle (handles 145 * are square), in pixels. */ 146 int handlePad; /* Distance from border to draw handle. */ 147 Tcl_Obj *handleSizePtr; /* Tcl_Obj rep for handle size. */ 148 Tk_Cursor sashCursor; /* Cursor used when mouse is above a sash. */ 149 GC gc; /* Graphics context for copying from 150 * off-screen pixmap onto screen. */ 151 int proxyx, proxyy; /* Proxy x,y coordinates. */ 152 Slave **slaves; /* Pointer to array of Slaves. */ 153 int numSlaves; /* Number of slaves. */ 154 int sizeofSlaves; /* Number of elements in the slaves array. */ 155 int flags; /* Flags for widget; see below. */ 156} PanedWindow; 157 158/* 159 * Flags used for paned windows: 160 * 161 * REDRAW_PENDING: Non-zero means a DoWhenIdle handler has been 162 * queued to redraw this window. 163 * 164 * WIDGET_DELETED: Non-zero means that the paned window has been, 165 * or is in the process of being, deleted. 166 * 167 * RESIZE_PENDING: Non-zero means that the window might need to 168 * change its size (or the size of its panes) 169 * because of a change in the size of one of its 170 * children. 171 */ 172 173#define REDRAW_PENDING 0x0001 174#define WIDGET_DELETED 0x0002 175#define REQUESTED_RELAYOUT 0x0004 176#define RECOMPUTE_GEOMETRY 0x0008 177#define PROXY_REDRAW_PENDING 0x0010 178#define RESIZE_PENDING 0x0020 179 180/* 181 * Forward declarations for functions defined later in this file: 182 */ 183 184int Tk_PanedWindowObjCmd(ClientData clientData, 185 Tcl_Interp *interp, int objc, 186 Tcl_Obj *CONST objv[]); 187static void PanedWindowCmdDeletedProc(ClientData clientData); 188static int ConfigurePanedWindow(Tcl_Interp *interp, 189 PanedWindow *pwPtr, int objc, 190 Tcl_Obj *CONST objv[]); 191static void DestroyPanedWindow(PanedWindow *pwPtr); 192static void DisplayPanedWindow(ClientData clientData); 193static void PanedWindowEventProc(ClientData clientData, 194 XEvent *eventPtr); 195static void ProxyWindowEventProc(ClientData clientData, 196 XEvent *eventPtr); 197static void DisplayProxyWindow(ClientData clientData); 198static void PanedWindowWorldChanged(ClientData instanceData); 199static int PanedWindowWidgetObjCmd(ClientData clientData, 200 Tcl_Interp *, int objc, Tcl_Obj * CONST objv[]); 201static void PanedWindowLostSlaveProc(ClientData clientData, 202 Tk_Window tkwin); 203static void PanedWindowReqProc(ClientData clientData, 204 Tk_Window tkwin); 205static void ArrangePanes(ClientData clientData); 206static void Unlink(Slave *slavePtr); 207static Slave * GetPane(PanedWindow *pwPtr, Tk_Window tkwin); 208static void SlaveStructureProc(ClientData clientData, 209 XEvent *eventPtr); 210static int PanedWindowSashCommand(PanedWindow *pwPtr, 211 Tcl_Interp *interp, int objc, 212 Tcl_Obj * CONST objv[]); 213static int PanedWindowProxyCommand(PanedWindow *pwPtr, 214 Tcl_Interp *interp, int objc, 215 Tcl_Obj * CONST objv[]); 216static void ComputeGeometry(PanedWindow *pwPtr); 217static int ConfigureSlaves(PanedWindow *pwPtr, 218 Tcl_Interp *interp, int objc, 219 Tcl_Obj * CONST objv[]); 220static void DestroyOptionTables(ClientData clientData, 221 Tcl_Interp *interp); 222static int SetSticky(ClientData clientData, Tcl_Interp *interp, 223 Tk_Window tkwin, Tcl_Obj **value, char *recordPtr, 224 int internalOffset, char *oldInternalPtr, 225 int flags); 226static Tcl_Obj * GetSticky(ClientData clientData, Tk_Window tkwin, 227 char *recordPtr, int internalOffset); 228static void RestoreSticky(ClientData clientData, Tk_Window tkwin, 229 char *internalPtr, char *oldInternalPtr); 230static void AdjustForSticky(int sticky, int cavityWidth, 231 int cavityHeight, int *xPtr, int *yPtr, 232 int *slaveWidthPtr, int *slaveHeightPtr); 233static void MoveSash(PanedWindow *pwPtr, int sash, int diff); 234static int ObjectIsEmpty(Tcl_Obj *objPtr); 235static char * ComputeSlotAddress(char *recordPtr, int offset); 236static int PanedWindowIdentifyCoords(PanedWindow *pwPtr, 237 Tcl_Interp *interp, int x, int y); 238 239/* 240 * Sashes are between panes only, so there is one less sash than slaves 241 */ 242 243#define ValidSashIndex(pwPtr, sash) \ 244 (((sash) >= 0) && ((sash) < ((pwPtr)->numSlaves-1))) 245 246static const Tk_GeomMgr panedWindowMgrType = { 247 "panedwindow", /* name */ 248 PanedWindowReqProc, /* requestProc */ 249 PanedWindowLostSlaveProc, /* lostSlaveProc */ 250}; 251 252/* 253 * Information used for objv parsing. 254 */ 255 256#define GEOMETRY 0x0001 257 258/* 259 * The following structure contains pointers to functions used for processing 260 * the custom "-sticky" option for slave windows. 261 */ 262 263static Tk_ObjCustomOption stickyOption = { 264 "sticky", /* name */ 265 SetSticky, /* setProc */ 266 GetSticky, /* getProc */ 267 RestoreSticky, /* restoreProc */ 268 NULL, /* freeProc */ 269 0 270}; 271 272static const Tk_OptionSpec optionSpecs[] = { 273 {TK_OPTION_BORDER, "-background", "background", "Background", 274 DEF_PANEDWINDOW_BG_COLOR, -1, Tk_Offset(PanedWindow, background), 0, 275 (ClientData) DEF_PANEDWINDOW_BG_MONO}, 276 {TK_OPTION_SYNONYM, "-bd", NULL, NULL, 277 NULL, 0, -1, 0, (ClientData) "-borderwidth"}, 278 {TK_OPTION_SYNONYM, "-bg", NULL, NULL, 279 NULL, 0, -1, 0, (ClientData) "-background"}, 280 {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", 281 DEF_PANEDWINDOW_BORDERWIDTH, -1, Tk_Offset(PanedWindow, borderWidth), 282 0, 0, GEOMETRY}, 283 {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor", 284 DEF_PANEDWINDOW_CURSOR, -1, Tk_Offset(PanedWindow, cursor), 285 TK_OPTION_NULL_OK, 0, 0}, 286 {TK_OPTION_PIXELS, "-handlepad", "handlePad", "HandlePad", 287 DEF_PANEDWINDOW_HANDLEPAD, -1, Tk_Offset(PanedWindow, handlePad), 288 0, 0, GEOMETRY}, 289 {TK_OPTION_PIXELS, "-handlesize", "handleSize", "HandleSize", 290 DEF_PANEDWINDOW_HANDLESIZE, Tk_Offset(PanedWindow, handleSizePtr), 291 Tk_Offset(PanedWindow, handleSize), 0, 0, GEOMETRY}, 292 {TK_OPTION_PIXELS, "-height", "height", "Height", 293 DEF_PANEDWINDOW_HEIGHT, Tk_Offset(PanedWindow, heightPtr), 294 Tk_Offset(PanedWindow, height), TK_OPTION_NULL_OK, 0, GEOMETRY}, 295 {TK_OPTION_BOOLEAN, "-opaqueresize", "opaqueResize", "OpaqueResize", 296 DEF_PANEDWINDOW_OPAQUERESIZE, -1, 297 Tk_Offset(PanedWindow, resizeOpaque), 0, 0, 0}, 298 {TK_OPTION_STRING_TABLE, "-orient", "orient", "Orient", 299 DEF_PANEDWINDOW_ORIENT, -1, Tk_Offset(PanedWindow, orient), 300 0, (ClientData) orientStrings, GEOMETRY}, 301 {TK_OPTION_RELIEF, "-relief", "relief", "Relief", 302 DEF_PANEDWINDOW_RELIEF, -1, Tk_Offset(PanedWindow, relief), 0, 0, 0}, 303 {TK_OPTION_CURSOR, "-sashcursor", "sashCursor", "Cursor", 304 DEF_PANEDWINDOW_SASHCURSOR, -1, Tk_Offset(PanedWindow, sashCursor), 305 TK_OPTION_NULL_OK, 0, 0}, 306 {TK_OPTION_PIXELS, "-sashpad", "sashPad", "SashPad", 307 DEF_PANEDWINDOW_SASHPAD, -1, Tk_Offset(PanedWindow, sashPad), 308 0, 0, GEOMETRY}, 309 {TK_OPTION_RELIEF, "-sashrelief", "sashRelief", "Relief", 310 DEF_PANEDWINDOW_SASHRELIEF, -1, Tk_Offset(PanedWindow, sashRelief), 311 0, 0, 0}, 312 {TK_OPTION_PIXELS, "-sashwidth", "sashWidth", "Width", 313 DEF_PANEDWINDOW_SASHWIDTH, Tk_Offset(PanedWindow, sashWidthPtr), 314 Tk_Offset(PanedWindow, sashWidth), 0, 0, GEOMETRY}, 315 {TK_OPTION_BOOLEAN, "-showhandle", "showHandle", "ShowHandle", 316 DEF_PANEDWINDOW_SHOWHANDLE, -1, Tk_Offset(PanedWindow, showHandle), 317 0, 0, GEOMETRY}, 318 {TK_OPTION_PIXELS, "-width", "width", "Width", 319 DEF_PANEDWINDOW_WIDTH, Tk_Offset(PanedWindow, widthPtr), 320 Tk_Offset(PanedWindow, width), TK_OPTION_NULL_OK, 0, GEOMETRY}, 321 {TK_OPTION_END} 322}; 323 324static const Tk_OptionSpec slaveOptionSpecs[] = { 325 {TK_OPTION_WINDOW, "-after", NULL, NULL, 326 DEF_PANEDWINDOW_PANE_AFTER, -1, Tk_Offset(Slave, after), 327 TK_OPTION_NULL_OK, 0, 0}, 328 {TK_OPTION_WINDOW, "-before", NULL, NULL, 329 DEF_PANEDWINDOW_PANE_BEFORE, -1, Tk_Offset(Slave, before), 330 TK_OPTION_NULL_OK, 0, 0}, 331 {TK_OPTION_PIXELS, "-height", NULL, NULL, 332 DEF_PANEDWINDOW_PANE_HEIGHT, Tk_Offset(Slave, heightPtr), 333 Tk_Offset(Slave, height), TK_OPTION_NULL_OK, 0, 0}, 334 {TK_OPTION_BOOLEAN, "-hide", "hide", "Hide", 335 DEF_PANEDWINDOW_PANE_HIDE, -1, Tk_Offset(Slave, hide), 0,0,GEOMETRY}, 336 {TK_OPTION_PIXELS, "-minsize", NULL, NULL, 337 DEF_PANEDWINDOW_PANE_MINSIZE, -1, Tk_Offset(Slave, minSize), 0, 0, 0}, 338 {TK_OPTION_PIXELS, "-padx", NULL, NULL, 339 DEF_PANEDWINDOW_PANE_PADX, -1, Tk_Offset(Slave, padx), 0, 0, 0}, 340 {TK_OPTION_PIXELS, "-pady", NULL, NULL, 341 DEF_PANEDWINDOW_PANE_PADY, -1, Tk_Offset(Slave, pady), 0, 0, 0}, 342 {TK_OPTION_CUSTOM, "-sticky", NULL, NULL, 343 DEF_PANEDWINDOW_PANE_STICKY, -1, Tk_Offset(Slave, sticky), 0, 344 (ClientData) &stickyOption, 0}, 345 {TK_OPTION_STRING_TABLE, "-stretch", "stretch", "Stretch", 346 DEF_PANEDWINDOW_PANE_STRETCH, -1, Tk_Offset(Slave, stretch), 0, 347 (ClientData) stretchStrings, 0}, 348 {TK_OPTION_PIXELS, "-width", NULL, NULL, 349 DEF_PANEDWINDOW_PANE_WIDTH, Tk_Offset(Slave, widthPtr), 350 Tk_Offset(Slave, width), TK_OPTION_NULL_OK, 0, 0}, 351 {TK_OPTION_END} 352}; 353 354/* 355 *-------------------------------------------------------------- 356 * 357 * Tk_PanedWindowObjCmd -- 358 * 359 * This function is invoked to process the "panedwindow" Tcl command. It 360 * creates a new "panedwindow" widget. 361 * 362 * Results: 363 * A standard Tcl result. 364 * 365 * Side effects: 366 * A new widget is created and configured. 367 * 368 *-------------------------------------------------------------- 369 */ 370 371int 372Tk_PanedWindowObjCmd( 373 ClientData clientData, /* NULL. */ 374 Tcl_Interp *interp, /* Current interpreter. */ 375 int objc, /* Number of arguments. */ 376 Tcl_Obj * CONST objv[]) /* Argument objects. */ 377{ 378 PanedWindow *pwPtr; 379 Tk_Window tkwin, parent; 380 OptionTables *pwOpts; 381 XSetWindowAttributes atts; 382 383 if (objc < 2) { 384 Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?"); 385 return TCL_ERROR; 386 } 387 388 tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), 389 Tcl_GetStringFromObj(objv[1], NULL), NULL); 390 if (tkwin == NULL) { 391 return TCL_ERROR; 392 } 393 394 pwOpts = (OptionTables *) 395 Tcl_GetAssocData(interp, "PanedWindowOptionTables", NULL); 396 if (pwOpts == NULL) { 397 /* 398 * The first time this function is invoked, the option tables will be 399 * NULL. We then create the option tables from the templates and store 400 * a pointer to the tables as the command's clinical so we'll have 401 * easy access to it in the future. 402 */ 403 404 pwOpts = (OptionTables *) ckalloc(sizeof(OptionTables)); 405 406 /* 407 * Set up an exit handler to free the optionTables struct. 408 */ 409 410 Tcl_SetAssocData(interp, "PanedWindowOptionTables", 411 DestroyOptionTables, (ClientData) pwOpts); 412 413 /* 414 * Create the paned window option tables. 415 */ 416 417 pwOpts->pwOptions = Tk_CreateOptionTable(interp, optionSpecs); 418 pwOpts->slaveOpts = Tk_CreateOptionTable(interp, slaveOptionSpecs); 419 } 420 421 Tk_SetClass(tkwin, "Panedwindow"); 422 423 /* 424 * Allocate and initialize the widget record. 425 */ 426 427 pwPtr = (PanedWindow *) ckalloc(sizeof(PanedWindow)); 428 memset((void *)pwPtr, 0, (sizeof(PanedWindow))); 429 pwPtr->tkwin = tkwin; 430 pwPtr->display = Tk_Display(tkwin); 431 pwPtr->interp = interp; 432 pwPtr->widgetCmd = Tcl_CreateObjCommand(interp, 433 Tk_PathName(pwPtr->tkwin), PanedWindowWidgetObjCmd, 434 (ClientData) pwPtr, PanedWindowCmdDeletedProc); 435 pwPtr->optionTable = pwOpts->pwOptions; 436 pwPtr->slaveOpts = pwOpts->slaveOpts; 437 pwPtr->relief = TK_RELIEF_RAISED; 438 pwPtr->gc = None; 439 pwPtr->cursor = None; 440 pwPtr->sashCursor = None; 441 442 /* 443 * Keep a hold of the associated tkwin until we destroy the widget, 444 * otherwise Tk might free it while we still need it. 445 */ 446 447 Tcl_Preserve((ClientData) pwPtr->tkwin); 448 449 if (Tk_InitOptions(interp, (char *) pwPtr, pwOpts->pwOptions, 450 tkwin) != TCL_OK) { 451 Tk_DestroyWindow(pwPtr->tkwin); 452 return TCL_ERROR; 453 } 454 455 Tk_CreateEventHandler(pwPtr->tkwin, ExposureMask|StructureNotifyMask, 456 PanedWindowEventProc, (ClientData) pwPtr); 457 458 /* 459 * Find the toplevel ancestor of the panedwindow, and make a proxy win as 460 * a child of that window; this way the proxy can always float above 461 * slaves in the panedwindow. 462 */ 463 464 parent = Tk_Parent(pwPtr->tkwin); 465 while (!(Tk_IsTopLevel(parent))) { 466 parent = Tk_Parent(parent); 467 if (parent == NULL) { 468 parent = pwPtr->tkwin; 469 break; 470 } 471 } 472 473 pwPtr->proxywin = Tk_CreateAnonymousWindow(interp, parent, NULL); 474 475 /* 476 * The proxy window has to be able to share GCs with the main panedwindow 477 * despite being children of windows with potentially different 478 * characteristics, and it looks better that way too. [Bug 702230] Also 479 * set the X window save under attribute to avoid expose events as the 480 * proxy sash is dragged across the panes. [Bug 1036963] 481 */ 482 483 Tk_SetWindowVisual(pwPtr->proxywin, 484 Tk_Visual(tkwin), Tk_Depth(tkwin), Tk_Colormap(tkwin)); 485 Tk_CreateEventHandler(pwPtr->proxywin, ExposureMask, ProxyWindowEventProc, 486 (ClientData) pwPtr); 487 atts.save_under = True; 488 Tk_ChangeWindowAttributes(pwPtr->proxywin, CWSaveUnder, &atts); 489 490 if (ConfigurePanedWindow(interp, pwPtr, objc - 2, objv + 2) != TCL_OK) { 491 Tk_DestroyWindow(pwPtr->proxywin); 492 Tk_DestroyWindow(pwPtr->tkwin); 493 return TCL_ERROR; 494 } 495 496 Tcl_SetStringObj(Tcl_GetObjResult(interp), Tk_PathName(pwPtr->tkwin), -1); 497 return TCL_OK; 498} 499 500/* 501 *-------------------------------------------------------------- 502 * 503 * PanedWindowWidgetObjCmd -- 504 * 505 * This function is invoked to process the Tcl command that corresponds 506 * to a widget managed by this module. See the user documentation for 507 * details on what it does. 508 * 509 * Results: 510 * A standard Tcl result. 511 * 512 * Side effects: 513 * See the user documentation. 514 * 515 *-------------------------------------------------------------- 516 */ 517 518static int 519PanedWindowWidgetObjCmd( 520 ClientData clientData, /* Information about square widget. */ 521 Tcl_Interp *interp, /* Current interpreter. */ 522 int objc, /* Number of arguments. */ 523 Tcl_Obj * CONST objv[]) /* Argument objects. */ 524{ 525 PanedWindow *pwPtr = (PanedWindow *) clientData; 526 int result = TCL_OK; 527 static CONST char *optionStrings[] = { 528 "add", "cget", "configure", "forget", "identify", "panecget", 529 "paneconfigure", "panes", "proxy", "sash", NULL 530 }; 531 enum options { 532 PW_ADD, PW_CGET, PW_CONFIGURE, PW_FORGET, PW_IDENTIFY, PW_PANECGET, 533 PW_PANECONFIGURE, PW_PANES, PW_PROXY, PW_SASH 534 }; 535 Tcl_Obj *resultObj; 536 int index, count, i, x, y; 537 Tk_Window tkwin; 538 Slave *slavePtr; 539 540 if (objc < 2) { 541 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg...?"); 542 return TCL_ERROR; 543 } 544 545 if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "command", 546 0, &index) != TCL_OK) { 547 return TCL_ERROR; 548 } 549 550 Tcl_Preserve((ClientData) pwPtr); 551 552 switch ((enum options) index) { 553 case PW_ADD: 554 if (objc < 3) { 555 Tcl_WrongNumArgs(interp, 2, objv, "widget ?widget ...?"); 556 result = TCL_ERROR; 557 break; 558 } 559 result = ConfigureSlaves(pwPtr, interp, objc, objv); 560 break; 561 562 case PW_CGET: 563 if (objc != 3) { 564 Tcl_WrongNumArgs(interp, 2, objv, "option"); 565 result = TCL_ERROR; 566 break; 567 } 568 resultObj = Tk_GetOptionValue(interp, (char *) pwPtr, 569 pwPtr->optionTable, objv[2], pwPtr->tkwin); 570 if (resultObj == NULL) { 571 result = TCL_ERROR; 572 } else { 573 Tcl_SetObjResult(interp, resultObj); 574 } 575 break; 576 577 case PW_CONFIGURE: 578 resultObj = NULL; 579 if (objc <= 3) { 580 resultObj = Tk_GetOptionInfo(interp, (char *) pwPtr, 581 pwPtr->optionTable, 582 (objc == 3) ? objv[2] : NULL, pwPtr->tkwin); 583 if (resultObj == NULL) { 584 result = TCL_ERROR; 585 } else { 586 Tcl_SetObjResult(interp, resultObj); 587 } 588 } else { 589 result = ConfigurePanedWindow(interp, pwPtr, objc - 2, objv + 2); 590 } 591 break; 592 593 case PW_FORGET: { 594 int i; 595 596 if (objc < 3) { 597 Tcl_WrongNumArgs(interp, 2, objv, "widget ?widget ...?"); 598 result = TCL_ERROR; 599 break; 600 } 601 602 /* 603 * Clean up each window named in the arg list. 604 */ 605 for (count = 0, i = 2; i < objc; i++) { 606 Tk_Window slave = Tk_NameToWindow(interp, Tcl_GetString(objv[i]), 607 pwPtr->tkwin); 608 if (slave == NULL) { 609 continue; 610 } 611 slavePtr = GetPane(pwPtr, slave); 612 if ((slavePtr != NULL) && (slavePtr->masterPtr != NULL)) { 613 count++; 614 Tk_ManageGeometry(slave, NULL, (ClientData)NULL); 615 Tk_UnmaintainGeometry(slavePtr->tkwin, pwPtr->tkwin); 616 Tk_DeleteEventHandler(slavePtr->tkwin, StructureNotifyMask, 617 SlaveStructureProc, (ClientData) slavePtr); 618 Tk_UnmapWindow(slavePtr->tkwin); 619 Unlink(slavePtr); 620 } 621 if (count != 0) { 622 ComputeGeometry(pwPtr); 623 } 624 } 625 break; 626 } 627 628 case PW_IDENTIFY: 629 if (objc != 4) { 630 Tcl_WrongNumArgs(interp, 2, objv, "x y"); 631 result = TCL_ERROR; 632 break; 633 } 634 635 if ((Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) 636 || (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK)) { 637 result = TCL_ERROR; 638 break; 639 } 640 result = PanedWindowIdentifyCoords(pwPtr, interp, x, y); 641 break; 642 643 case PW_PANECGET: 644 if (objc != 4) { 645 Tcl_WrongNumArgs(interp, 2, objv, "pane option"); 646 result = TCL_ERROR; 647 break; 648 } 649 tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), pwPtr->tkwin); 650 if (tkwin == NULL) { 651 result = TCL_ERROR; 652 break; 653 } 654 resultObj = NULL; 655 for (i = 0; i < pwPtr->numSlaves; i++) { 656 if (pwPtr->slaves[i]->tkwin == tkwin) { 657 resultObj = Tk_GetOptionValue(interp, 658 (char *) pwPtr->slaves[i], pwPtr->slaveOpts, 659 objv[3], tkwin); 660 } 661 } 662 if (i == pwPtr->numSlaves) { 663 Tcl_SetResult(interp, "not managed by this window", TCL_STATIC); 664 } 665 if (resultObj == NULL) { 666 result = TCL_ERROR; 667 } else { 668 Tcl_SetObjResult(interp, resultObj); 669 } 670 break; 671 672 case PW_PANECONFIGURE: 673 if (objc < 3) { 674 Tcl_WrongNumArgs(interp, 2, objv, 675 "pane ?option? ?value option value ...?"); 676 result = TCL_ERROR; 677 break; 678 } 679 resultObj = NULL; 680 if (objc <= 4) { 681 tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), 682 pwPtr->tkwin); 683 for (i = 0; i < pwPtr->numSlaves; i++) { 684 if (pwPtr->slaves[i]->tkwin == tkwin) { 685 resultObj = Tk_GetOptionInfo(interp, 686 (char *) pwPtr->slaves[i], pwPtr->slaveOpts, 687 (objc == 4) ? objv[3] : NULL, 688 pwPtr->tkwin); 689 if (resultObj == NULL) { 690 result = TCL_ERROR; 691 } else { 692 Tcl_SetObjResult(interp, resultObj); 693 } 694 break; 695 } 696 } 697 } else { 698 result = ConfigureSlaves(pwPtr, interp, objc, objv); 699 } 700 break; 701 702 case PW_PANES: 703 resultObj = Tcl_NewObj(); 704 705 Tcl_IncrRefCount(resultObj); 706 707 for (i = 0; i < pwPtr->numSlaves; i++) { 708 Tcl_ListObjAppendElement(interp, resultObj, 709 Tcl_NewStringObj(Tk_PathName(pwPtr->slaves[i]->tkwin),-1)); 710 } 711 Tcl_SetObjResult(interp, resultObj); 712 Tcl_DecrRefCount(resultObj); 713 break; 714 715 case PW_PROXY: 716 result = PanedWindowProxyCommand(pwPtr, interp, objc, objv); 717 break; 718 719 case PW_SASH: 720 result = PanedWindowSashCommand(pwPtr, interp, objc, objv); 721 break; 722 } 723 Tcl_Release((ClientData) pwPtr); 724 return result; 725} 726 727/* 728 *---------------------------------------------------------------------- 729 * 730 * ConfigureSlaves -- 731 * 732 * Add or alter the configuration options of a slave in a paned window. 733 * 734 * Results: 735 * Standard Tcl result. 736 * 737 * Side effects: 738 * Depends on options; may add a slave to the paned window, may alter the 739 * geometry management options of a slave. 740 * 741 *---------------------------------------------------------------------- 742 */ 743 744static int 745ConfigureSlaves( 746 PanedWindow *pwPtr, /* Information about paned window. */ 747 Tcl_Interp *interp, /* Current interpreter. */ 748 int objc, /* Number of arguments. */ 749 Tcl_Obj *CONST objv[]) /* Argument objects. */ 750{ 751 int i, firstOptionArg, j, found, doubleBw, index, numNewSlaves, haveLoc; 752 int insertIndex; 753 Tk_Window tkwin = NULL, ancestor, parent; 754 Slave *slavePtr, **inserts, **newSlaves; 755 Slave options; 756 char *arg; 757 758 /* 759 * Find the non-window name arguments; these are the configure options for 760 * the slaves. Also validate that the window names given are legitimate 761 * (ie, they are real windows, they are not the panedwindow itself, etc.). 762 */ 763 764 for (i = 2; i < objc; i++) { 765 arg = Tcl_GetString(objv[i]); 766 if (arg[0] == '-') { 767 break; 768 } else { 769 tkwin = Tk_NameToWindow(interp, arg, pwPtr->tkwin); 770 if (tkwin == NULL) { 771 /* 772 * Just a plain old bad window; Tk_NameToWindow filled in an 773 * error message for us. 774 */ 775 776 return TCL_ERROR; 777 } else if (tkwin == pwPtr->tkwin) { 778 /* 779 * A panedwindow cannot manage itself. 780 */ 781 782 Tcl_ResetResult(interp); 783 Tcl_AppendResult(interp, "can't add ", arg, " to itself", 784 NULL); 785 return TCL_ERROR; 786 } else if (Tk_IsTopLevel(tkwin)) { 787 /* 788 * A panedwindow cannot manage a toplevel. 789 */ 790 791 Tcl_ResetResult(interp); 792 Tcl_AppendResult(interp, "can't add toplevel ", arg, " to ", 793 Tk_PathName(pwPtr->tkwin), NULL); 794 return TCL_ERROR; 795 } else { 796 /* 797 * Make sure the panedwindow is the parent of the slave, 798 * or a descendant of the slave's parent. 799 */ 800 801 parent = Tk_Parent(tkwin); 802 for (ancestor = pwPtr->tkwin;;ancestor = Tk_Parent(ancestor)) { 803 if (ancestor == parent) { 804 break; 805 } 806 if (Tk_IsTopLevel(ancestor)) { 807 Tcl_ResetResult(interp); 808 Tcl_AppendResult(interp, "can't add ", arg, " to ", 809 Tk_PathName(pwPtr->tkwin), NULL); 810 return TCL_ERROR; 811 } 812 } 813 } 814 } 815 } 816 firstOptionArg = i; 817 818 /* 819 * Pre-parse the configuration options, to get the before/after specifiers 820 * into an easy-to-find location (a local variable). Also, check the 821 * return from Tk_SetOptions once, here, so we can save a little bit of 822 * extra testing in the for loop below. 823 */ 824 825 memset((void *)&options, 0, sizeof(Slave)); 826 if (Tk_SetOptions(interp, (char *) &options, pwPtr->slaveOpts, 827 objc - firstOptionArg, objv + firstOptionArg, 828 pwPtr->tkwin, NULL, NULL) != TCL_OK) { 829 return TCL_ERROR; 830 } 831 832 /* 833 * If either -after or -before was given, find the numerical index that 834 * corresponds to the given window. If both -after and -before are given, 835 * the option precedence is: -after, then -before. 836 */ 837 838 index = -1; 839 haveLoc = 0; 840 if (options.after != None) { 841 tkwin = options.after; 842 haveLoc = 1; 843 for (i = 0; i < pwPtr->numSlaves; i++) { 844 if (options.after == pwPtr->slaves[i]->tkwin) { 845 index = i + 1; 846 break; 847 } 848 } 849 } else if (options.before != None) { 850 tkwin = options.before; 851 haveLoc = 1; 852 for (i = 0; i < pwPtr->numSlaves; i++) { 853 if (options.before == pwPtr->slaves[i]->tkwin) { 854 index = i; 855 break; 856 } 857 } 858 } 859 860 /* 861 * If a window was given for -after/-before, but it's not a window managed 862 * by the panedwindow, throw an error 863 */ 864 865 if (haveLoc && index == -1) { 866 Tcl_ResetResult(interp); 867 Tcl_AppendResult(interp, "window \"", Tk_PathName(tkwin), 868 "\" is not managed by ", Tk_PathName(pwPtr->tkwin), NULL); 869 Tk_FreeConfigOptions((char *) &options, pwPtr->slaveOpts, 870 pwPtr->tkwin); 871 return TCL_ERROR; 872 } 873 874 /* 875 * Allocate an array to hold, in order, the pointers to the slave 876 * structures corresponding to the windows specified. Some of those 877 * structures may already have existed, some may be new. 878 */ 879 880 inserts = (Slave **)ckalloc(sizeof(Slave *) * (firstOptionArg - 2)); 881 insertIndex = 0; 882 883 /* 884 * Populate the inserts array, creating new slave structures as necessary, 885 * applying the options to each structure as we go, and, if necessary, 886 * marking the spot in the original slaves array as empty (for 887 * pre-existing slave structures). 888 */ 889 890 for (i = 0, numNewSlaves = 0; i < firstOptionArg - 2; i++) { 891 /* 892 * We don't check that tkwin is NULL here, because the pre-pass above 893 * guarantees that the input at this stage is good. 894 */ 895 896 tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[i + 2]), 897 pwPtr->tkwin); 898 899 found = 0; 900 for (j = 0; j < pwPtr->numSlaves; j++) { 901 if (pwPtr->slaves[j] != NULL && pwPtr->slaves[j]->tkwin == tkwin) { 902 Tk_SetOptions(interp, (char *) pwPtr->slaves[j], 903 pwPtr->slaveOpts, objc - firstOptionArg, 904 objv + firstOptionArg, pwPtr->tkwin, NULL, NULL); 905 if (pwPtr->slaves[j]->minSize < 0) { 906 pwPtr->slaves[j]->minSize = 0; 907 } 908 found = 1; 909 910 /* 911 * If the slave is supposed to move, add it to the inserts 912 * array now; otherwise, leave it where it is. 913 */ 914 915 if (index != -1) { 916 inserts[insertIndex++] = pwPtr->slaves[j]; 917 pwPtr->slaves[j] = NULL; 918 } 919 break; 920 } 921 } 922 923 if (found) { 924 continue; 925 } 926 927 /* 928 * Make sure this slave wasn't already put into the inserts array, 929 * i.e., when the user specifies the same window multiple times in a 930 * single add commaned. 931 */ 932 for (j = 0; j < insertIndex; j++) { 933 if (inserts[j]->tkwin == tkwin) { 934 found = 1; 935 break; 936 } 937 } 938 if (found) { 939 continue; 940 } 941 942 /* 943 * Create a new slave structure and initialize it. All slaves start 944 * out with their "natural" dimensions. 945 */ 946 947 slavePtr = (Slave *) ckalloc(sizeof(Slave)); 948 memset(slavePtr, 0, sizeof(Slave)); 949 Tk_InitOptions(interp, (char *)slavePtr, pwPtr->slaveOpts, 950 pwPtr->tkwin); 951 Tk_SetOptions(interp, (char *)slavePtr, pwPtr->slaveOpts, 952 objc - firstOptionArg, objv + firstOptionArg, 953 pwPtr->tkwin, NULL, NULL); 954 slavePtr->tkwin = tkwin; 955 slavePtr->masterPtr = pwPtr; 956 doubleBw = 2 * Tk_Changes(slavePtr->tkwin)->border_width; 957 if (slavePtr->width > 0) { 958 slavePtr->paneWidth = slavePtr->width; 959 } else { 960 slavePtr->paneWidth = Tk_ReqWidth(tkwin) + doubleBw; 961 } 962 if (slavePtr->height > 0) { 963 slavePtr->paneHeight = slavePtr->height; 964 } else { 965 slavePtr->paneHeight = Tk_ReqHeight(tkwin) + doubleBw; 966 } 967 if (slavePtr->minSize < 0) { 968 slavePtr->minSize = 0; 969 } 970 971 /* 972 * Set up the geometry management callbacks for this slave. 973 */ 974 975 Tk_CreateEventHandler(slavePtr->tkwin, StructureNotifyMask, 976 SlaveStructureProc, (ClientData) slavePtr); 977 Tk_ManageGeometry(slavePtr->tkwin, &panedWindowMgrType, 978 (ClientData) slavePtr); 979 inserts[insertIndex++] = slavePtr; 980 numNewSlaves++; 981 } 982 983 /* 984 * Allocate the new slaves array, then copy the slaves into it, in order. 985 */ 986 987 i = sizeof(Slave *) * (pwPtr->numSlaves+numNewSlaves); 988 newSlaves = (Slave **)ckalloc((unsigned) i); 989 memset(newSlaves, 0, (size_t) i); 990 if (index == -1) { 991 /* 992 * If none of the existing slaves have to be moved, just copy the old 993 * and append the new. 994 */ 995 memcpy((void *)&(newSlaves[0]), pwPtr->slaves, 996 sizeof(Slave *) * pwPtr->numSlaves); 997 memcpy((void *)&(newSlaves[pwPtr->numSlaves]), inserts, 998 sizeof(Slave *) * numNewSlaves); 999 } else { 1000 /* 1001 * If some of the existing slaves were moved, the old slaves array 1002 * will be partially populated, with some valid and some invalid 1003 * entries. Walk through it, copying valid entries to the new slaves 1004 * array as we go; when we get to the insert location for the new 1005 * slaves, copy the inserts array over, then finish off the old slaves 1006 * array. 1007 */ 1008 1009 for (i = 0, j = 0; i < index; i++) { 1010 if (pwPtr->slaves[i] != NULL) { 1011 newSlaves[j] = pwPtr->slaves[i]; 1012 j++; 1013 } 1014 } 1015 1016 memcpy((void *)&(newSlaves[j]), inserts, sizeof(Slave *)*insertIndex); 1017 j += firstOptionArg - 2; 1018 1019 for (i = index; i < pwPtr->numSlaves; i++) { 1020 if (pwPtr->slaves[i] != NULL) { 1021 newSlaves[j] = pwPtr->slaves[i]; 1022 j++; 1023 } 1024 } 1025 } 1026 1027 /* 1028 * Make the new slaves array the paned window's slave array, and clean up. 1029 */ 1030 1031 ckfree((void *)pwPtr->slaves); 1032 ckfree((void *)inserts); 1033 pwPtr->slaves = newSlaves; 1034 1035 /* 1036 * Set the paned window's slave count to the new value. 1037 */ 1038 1039 pwPtr->numSlaves += numNewSlaves; 1040 1041 Tk_FreeConfigOptions((char *) &options, pwPtr->slaveOpts, pwPtr->tkwin); 1042 1043 ComputeGeometry(pwPtr); 1044 return TCL_OK; 1045} 1046 1047/* 1048 *---------------------------------------------------------------------- 1049 * 1050 * PanedWindowSashCommand -- 1051 * 1052 * Implementation of the panedwindow sash subcommand. See the user 1053 * documentation for details on what it does. 1054 * 1055 * Results: 1056 * Standard Tcl result. 1057 * 1058 * Side effects: 1059 * Depends on the arguments. 1060 * 1061 *---------------------------------------------------------------------- 1062 */ 1063 1064static int 1065PanedWindowSashCommand( 1066 PanedWindow *pwPtr, /* Pointer to paned window information. */ 1067 Tcl_Interp *interp, /* Current interpreter. */ 1068 int objc, /* Number of arguments. */ 1069 Tcl_Obj *CONST objv[]) /* Argument objects. */ 1070{ 1071 static CONST char *sashOptionStrings[] = { 1072 "coord", "dragto", "mark", "place", NULL 1073 }; 1074 enum sashOptions { 1075 SASH_COORD, SASH_DRAGTO, SASH_MARK, SASH_PLACE 1076 }; 1077 int index, sash, x, y, diff; 1078 Tcl_Obj *coords[2]; 1079 Slave *slavePtr; 1080 1081 if (objc < 3) { 1082 Tcl_WrongNumArgs(interp, 2, objv, "option ?arg ...?"); 1083 return TCL_ERROR; 1084 } 1085 1086 if (Tcl_GetIndexFromObj(interp, objv[2], sashOptionStrings, "option", 0, 1087 &index) != TCL_OK) { 1088 return TCL_ERROR; 1089 } 1090 1091 Tcl_ResetResult(interp); 1092 switch ((enum sashOptions) index) { 1093 case SASH_COORD: 1094 if (objc != 4) { 1095 Tcl_WrongNumArgs(interp, 3, objv, "index"); 1096 return TCL_ERROR; 1097 } 1098 1099 if (Tcl_GetIntFromObj(interp, objv[3], &sash) != TCL_OK) { 1100 return TCL_ERROR; 1101 } 1102 1103 if (!ValidSashIndex(pwPtr, sash)) { 1104 Tcl_ResetResult(interp); 1105 Tcl_SetResult(interp, "invalid sash index", TCL_STATIC); 1106 return TCL_ERROR; 1107 } 1108 slavePtr = pwPtr->slaves[sash]; 1109 1110 coords[0] = Tcl_NewIntObj(slavePtr->sashx); 1111 coords[1] = Tcl_NewIntObj(slavePtr->sashy); 1112 Tcl_SetListObj(Tcl_GetObjResult(interp), 2, coords); 1113 break; 1114 1115 case SASH_MARK: 1116 if (objc != 6 && objc != 4) { 1117 Tcl_WrongNumArgs(interp, 3, objv, "index ?x y?"); 1118 return TCL_ERROR; 1119 } 1120 1121 if (Tcl_GetIntFromObj(interp, objv[3], &sash) != TCL_OK) { 1122 return TCL_ERROR; 1123 } 1124 1125 if (!ValidSashIndex(pwPtr, sash)) { 1126 Tcl_ResetResult(interp); 1127 Tcl_SetResult(interp, "invalid sash index", TCL_STATIC); 1128 return TCL_ERROR; 1129 } 1130 1131 if (objc == 6) { 1132 if (Tcl_GetIntFromObj(interp, objv[4], &x) != TCL_OK) { 1133 return TCL_ERROR; 1134 } 1135 1136 if (Tcl_GetIntFromObj(interp, objv[5], &y) != TCL_OK) { 1137 return TCL_ERROR; 1138 } 1139 1140 pwPtr->slaves[sash]->markx = x; 1141 pwPtr->slaves[sash]->marky = y; 1142 } else { 1143 coords[0] = Tcl_NewIntObj(pwPtr->slaves[sash]->markx); 1144 coords[1] = Tcl_NewIntObj(pwPtr->slaves[sash]->marky); 1145 Tcl_SetListObj(Tcl_GetObjResult(interp), 2, coords); 1146 } 1147 break; 1148 1149 case SASH_DRAGTO: 1150 case SASH_PLACE: 1151 if (objc != 6) { 1152 Tcl_WrongNumArgs(interp, 3, objv, "index x y"); 1153 return TCL_ERROR; 1154 } 1155 1156 if (Tcl_GetIntFromObj(interp, objv[3], &sash) != TCL_OK) { 1157 return TCL_ERROR; 1158 } 1159 1160 if (!ValidSashIndex(pwPtr, sash)) { 1161 Tcl_ResetResult(interp); 1162 Tcl_SetResult(interp, "invalid sash index", TCL_STATIC); 1163 return TCL_ERROR; 1164 } 1165 1166 if (Tcl_GetIntFromObj(interp, objv[4], &x) != TCL_OK) { 1167 return TCL_ERROR; 1168 } 1169 1170 if (Tcl_GetIntFromObj(interp, objv[5], &y) != TCL_OK) { 1171 return TCL_ERROR; 1172 } 1173 1174 slavePtr = pwPtr->slaves[sash]; 1175 if (pwPtr->orient == ORIENT_HORIZONTAL) { 1176 if (index == SASH_PLACE) { 1177 diff = x - pwPtr->slaves[sash]->sashx; 1178 } else { 1179 diff = x - pwPtr->slaves[sash]->markx; 1180 } 1181 } else { 1182 if (index == SASH_PLACE) { 1183 diff = y - pwPtr->slaves[sash]->sashy; 1184 } else { 1185 diff = y - pwPtr->slaves[sash]->marky; 1186 } 1187 } 1188 1189 MoveSash(pwPtr, sash, diff); 1190 ComputeGeometry(pwPtr); 1191 } 1192 return TCL_OK; 1193} 1194 1195/* 1196 *---------------------------------------------------------------------- 1197 * 1198 * ConfigurePanedWindow -- 1199 * 1200 * This function is called to process an argv/argc list in conjunction 1201 * with the Tk option database to configure (or reconfigure) a paned 1202 * window widget. 1203 * 1204 * Results: 1205 * The return value is a standard Tcl result. If TCL_ERROR is returned, 1206 * then the interp's result contains an error message. 1207 * 1208 * Side effects: 1209 * Configuration information, such as colors, border width, etc. get set 1210 * for pwPtr; old resources get freed, if there were any. 1211 * 1212 *---------------------------------------------------------------------- 1213 */ 1214 1215static int 1216ConfigurePanedWindow( 1217 Tcl_Interp *interp, /* Used for error reporting. */ 1218 PanedWindow *pwPtr, /* Information about widget. */ 1219 int objc, /* Number of arguments. */ 1220 Tcl_Obj *CONST objv[]) /* Argument values. */ 1221{ 1222 Tk_SavedOptions savedOptions; 1223 int typemask = 0; 1224 1225 if (Tk_SetOptions(interp, (char *) pwPtr, pwPtr->optionTable, objc, objv, 1226 pwPtr->tkwin, &savedOptions, &typemask) != TCL_OK) { 1227 Tk_RestoreSavedOptions(&savedOptions); 1228 return TCL_ERROR; 1229 } 1230 1231 Tk_FreeSavedOptions(&savedOptions); 1232 1233 PanedWindowWorldChanged((ClientData) pwPtr); 1234 1235 /* 1236 * If an option that affects geometry has changed, make a re-layout 1237 * request. 1238 */ 1239 1240 if (typemask & GEOMETRY) { 1241 ComputeGeometry(pwPtr); 1242 } 1243 1244 return TCL_OK; 1245} 1246 1247/* 1248 *---------------------------------------------------------------------- 1249 * 1250 * PanedWindowWorldChanged -- 1251 * 1252 * This function is invoked anytime a paned window's world has changed in 1253 * some way that causes the widget to have to recompute graphics contexts 1254 * and geometry. 1255 * 1256 * Results: 1257 * None. 1258 * 1259 * Side effects: 1260 * Paned window will be relayed out and redisplayed. 1261 * 1262 *---------------------------------------------------------------------- 1263 */ 1264 1265static void 1266PanedWindowWorldChanged( 1267 ClientData instanceData) /* Information about the paned window. */ 1268{ 1269 XGCValues gcValues; 1270 GC newGC; 1271 PanedWindow *pwPtr = (PanedWindow *) instanceData; 1272 1273 /* 1274 * Allocated a graphics context for drawing the paned window widget 1275 * elements (background, sashes, etc.) and set the window background. 1276 */ 1277 1278 gcValues.background = Tk_3DBorderColor(pwPtr->background)->pixel; 1279 newGC = Tk_GetGC(pwPtr->tkwin, GCBackground, &gcValues); 1280 if (pwPtr->gc != None) { 1281 Tk_FreeGC(pwPtr->display, pwPtr->gc); 1282 } 1283 pwPtr->gc = newGC; 1284 Tk_SetWindowBackground(pwPtr->tkwin, gcValues.background); 1285 1286 /* 1287 * Issue geometry size requests to Tk. 1288 */ 1289 1290 Tk_SetInternalBorder(pwPtr->tkwin, pwPtr->borderWidth); 1291 if (pwPtr->width > 0 && pwPtr->height > 0) { 1292 Tk_GeometryRequest(pwPtr->tkwin, pwPtr->width, pwPtr->height); 1293 } 1294 1295 /* 1296 * Arrange for the window to be redrawn, if neccessary. 1297 */ 1298 1299 if (Tk_IsMapped(pwPtr->tkwin) && !(pwPtr->flags & REDRAW_PENDING)) { 1300 Tcl_DoWhenIdle(DisplayPanedWindow, (ClientData) pwPtr); 1301 pwPtr->flags |= REDRAW_PENDING; 1302 } 1303} 1304 1305/* 1306 *-------------------------------------------------------------- 1307 * 1308 * PanedWindowEventProc -- 1309 * 1310 * This function is invoked by the Tk dispatcher for various events on 1311 * paned windows. 1312 * 1313 * Results: 1314 * None. 1315 * 1316 * Side effects: 1317 * When the window gets deleted, internal structures get cleaned up. When 1318 * it gets exposed, it is redisplayed. 1319 * 1320 *-------------------------------------------------------------- 1321 */ 1322 1323static void 1324PanedWindowEventProc( 1325 ClientData clientData, /* Information about window. */ 1326 XEvent *eventPtr) /* Information about event. */ 1327{ 1328 PanedWindow *pwPtr = (PanedWindow *) clientData; 1329 1330 if (eventPtr->type == Expose) { 1331 if (pwPtr->tkwin != NULL && !(pwPtr->flags & REDRAW_PENDING)) { 1332 Tcl_DoWhenIdle(DisplayPanedWindow, (ClientData) pwPtr); 1333 pwPtr->flags |= REDRAW_PENDING; 1334 } 1335 } else if (eventPtr->type == ConfigureNotify) { 1336 pwPtr->flags |= REQUESTED_RELAYOUT; 1337 if (pwPtr->tkwin != NULL && !(pwPtr->flags & REDRAW_PENDING)) { 1338 Tcl_DoWhenIdle(DisplayPanedWindow, (ClientData) pwPtr); 1339 pwPtr->flags |= REDRAW_PENDING; 1340 } 1341 } else if (eventPtr->type == DestroyNotify) { 1342 DestroyPanedWindow(pwPtr); 1343 } 1344} 1345 1346/* 1347 *---------------------------------------------------------------------- 1348 * 1349 * PanedWindowCmdDeletedProc -- 1350 * 1351 * This function is invoked when a widget command is deleted. If the 1352 * widget isn't already in the process of being destroyed, this command 1353 * destroys it. 1354 * 1355 * Results: 1356 * None. 1357 * 1358 * Side effects: 1359 * The widget is destroyed. 1360 * 1361 *---------------------------------------------------------------------- 1362 */ 1363 1364static void 1365PanedWindowCmdDeletedProc( 1366 ClientData clientData) /* Pointer to widget record for widget. */ 1367{ 1368 PanedWindow *pwPtr = (PanedWindow *) clientData; 1369 1370 /* 1371 * This function could be invoked either because the window was destroyed 1372 * and the command was then deleted or because the command was deleted, 1373 * and then this function destroys the widget. The WIDGET_DELETED flag 1374 * distinguishes these cases. 1375 */ 1376 1377 if (!(pwPtr->flags & WIDGET_DELETED)) { 1378 Tk_DestroyWindow(pwPtr->proxywin); 1379 Tk_DestroyWindow(pwPtr->tkwin); 1380 } 1381} 1382 1383/* 1384 *-------------------------------------------------------------- 1385 * 1386 * DisplayPanedWindow -- 1387 * 1388 * This function redraws the contents of a paned window widget. It is 1389 * invoked as a do-when-idle handler, so it only runs when there's 1390 * nothing else for the application to do. 1391 * 1392 * Results: 1393 * None. 1394 * 1395 * Side effects: 1396 * Information appears on the screen. 1397 * 1398 *-------------------------------------------------------------- 1399 */ 1400 1401static void 1402DisplayPanedWindow( 1403 ClientData clientData) /* Information about window. */ 1404{ 1405 PanedWindow *pwPtr = (PanedWindow *) clientData; 1406 Slave *slavePtr; 1407 Pixmap pixmap; 1408 Tk_Window tkwin = pwPtr->tkwin; 1409 int i, sashWidth, sashHeight; 1410 const int horizontal = (pwPtr->orient == ORIENT_HORIZONTAL); 1411 1412 pwPtr->flags &= ~REDRAW_PENDING; 1413 if ((pwPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { 1414 return; 1415 } 1416 1417 if (pwPtr->flags & REQUESTED_RELAYOUT) { 1418 ArrangePanes(clientData); 1419 } 1420 1421#ifndef TK_NO_DOUBLE_BUFFERING 1422 /* 1423 * Create a pixmap for double-buffering, if necessary. 1424 */ 1425 1426 pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), 1427 Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); 1428#else 1429 pixmap = Tk_WindowId(tkwin); 1430#endif /* TK_NO_DOUBLE_BUFFERING */ 1431 1432 /* 1433 * Redraw the widget's background and border. 1434 */ 1435 1436 Tk_Fill3DRectangle(tkwin, pixmap, pwPtr->background, 0, 0, 1437 Tk_Width(tkwin), Tk_Height(tkwin), pwPtr->borderWidth, 1438 pwPtr->relief); 1439 1440 /* 1441 * Set up boilerplate geometry values for sashes (width, height, common 1442 * coordinates). 1443 */ 1444 1445 if (horizontal) { 1446 sashHeight = Tk_Height(tkwin) - (2 * Tk_InternalBorderWidth(tkwin)); 1447 sashWidth = pwPtr->sashWidth; 1448 } else { 1449 sashWidth = Tk_Width(tkwin) - (2 * Tk_InternalBorderWidth(tkwin)); 1450 sashHeight = pwPtr->sashWidth; 1451 } 1452 1453 /* 1454 * Draw the sashes. 1455 */ 1456 1457 for (i = 0; i < pwPtr->numSlaves - 1; i++) { 1458 slavePtr = pwPtr->slaves[i]; 1459 if (slavePtr->hide) { 1460 continue; 1461 } 1462 if (sashWidth > 0 && sashHeight > 0) { 1463 Tk_Fill3DRectangle(tkwin, pixmap, pwPtr->background, 1464 slavePtr->sashx, slavePtr->sashy, sashWidth, sashHeight, 1465 1, pwPtr->sashRelief); 1466 } 1467 if (pwPtr->showHandle) { 1468 Tk_Fill3DRectangle(tkwin, pixmap, pwPtr->background, 1469 slavePtr->handlex, slavePtr->handley, 1470 pwPtr->handleSize, pwPtr->handleSize, 1, 1471 TK_RELIEF_RAISED); 1472 } 1473 } 1474 1475#ifndef TK_NO_DOUBLE_BUFFERING 1476 /* 1477 * Copy the information from the off-screen pixmap onto the screen, then 1478 * delete the pixmap. 1479 */ 1480 1481 XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin), pwPtr->gc, 0, 0, 1482 (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin), 0, 0); 1483 Tk_FreePixmap(Tk_Display(tkwin), pixmap); 1484#endif /* TK_NO_DOUBLE_BUFFERING */ 1485} 1486 1487/* 1488 *---------------------------------------------------------------------- 1489 * 1490 * DestroyPanedWindow -- 1491 * 1492 * This function is invoked by PanedWindowEventProc to free the internal 1493 * structure of a paned window. 1494 * 1495 * Results: 1496 * None. 1497 * 1498 * Side effects: 1499 * Everything associated with the paned window is freed up. 1500 * 1501 *---------------------------------------------------------------------- 1502 */ 1503 1504static void 1505DestroyPanedWindow( 1506 PanedWindow *pwPtr) /* Info about paned window widget. */ 1507{ 1508 int i; 1509 1510 /* 1511 * First mark the widget as in the process of being deleted, so that any 1512 * code that causes calls to other paned window functions will abort. 1513 */ 1514 1515 pwPtr->flags |= WIDGET_DELETED; 1516 1517 /* 1518 * Cancel idle callbacks for redrawing the widget and for rearranging the 1519 * panes. 1520 */ 1521 1522 if (pwPtr->flags & REDRAW_PENDING) { 1523 Tcl_CancelIdleCall(DisplayPanedWindow, (ClientData) pwPtr); 1524 } 1525 if (pwPtr->flags & RESIZE_PENDING) { 1526 Tcl_CancelIdleCall(ArrangePanes, (ClientData) pwPtr); 1527 } 1528 1529 /* 1530 * Clean up the slave list; foreach slave: 1531 * o Cancel the slave's structure notification callback 1532 * o Cancel geometry management for the slave. 1533 * o Free memory for the slave 1534 */ 1535 1536 for (i = 0; i < pwPtr->numSlaves; i++) { 1537 Tk_DeleteEventHandler(pwPtr->slaves[i]->tkwin, StructureNotifyMask, 1538 SlaveStructureProc, (ClientData) pwPtr->slaves[i]); 1539 Tk_ManageGeometry(pwPtr->slaves[i]->tkwin, NULL, NULL); 1540 Tk_FreeConfigOptions((char *)pwPtr->slaves[i], pwPtr->slaveOpts, 1541 pwPtr->tkwin); 1542 ckfree((void *)pwPtr->slaves[i]); 1543 pwPtr->slaves[i] = NULL; 1544 } 1545 if (pwPtr->slaves) { 1546 ckfree((char *) pwPtr->slaves); 1547 } 1548 1549 /* 1550 * Remove the widget command from the interpreter. 1551 */ 1552 1553 Tcl_DeleteCommandFromToken(pwPtr->interp, pwPtr->widgetCmd); 1554 1555 /* 1556 * Let Tk_FreeConfigOptions clean up the rest. 1557 */ 1558 1559 Tk_FreeConfigOptions((char *) pwPtr, pwPtr->optionTable, pwPtr->tkwin); 1560 Tcl_Release((ClientData) pwPtr->tkwin); 1561 pwPtr->tkwin = NULL; 1562 1563 Tcl_EventuallyFree((ClientData) pwPtr, TCL_DYNAMIC); 1564} 1565 1566/* 1567 *-------------------------------------------------------------- 1568 * 1569 * PanedWindowReqProc -- 1570 * 1571 * This function is invoked by Tk_GeometryRequest for windows managed by 1572 * a paned window. 1573 * 1574 * Results: 1575 * None. 1576 * 1577 * Side effects: 1578 * Arranges for tkwin, and all its managed siblings, to be re-arranged at 1579 * the next idle point. 1580 * 1581 *-------------------------------------------------------------- 1582 */ 1583 1584static void 1585PanedWindowReqProc( 1586 ClientData clientData, /* Paned window's information about window 1587 * that got new preferred geometry. */ 1588 Tk_Window tkwin) /* Other Tk-related information about the 1589 * window. */ 1590{ 1591 Slave *slavePtr = (Slave *) clientData; 1592 PanedWindow *pwPtr = (PanedWindow *) (slavePtr->masterPtr); 1593 if (Tk_IsMapped(pwPtr->tkwin)) { 1594 if (!(pwPtr->flags & RESIZE_PENDING)) { 1595 pwPtr->flags |= RESIZE_PENDING; 1596 Tcl_DoWhenIdle(ArrangePanes, (ClientData) pwPtr); 1597 } 1598 } else { 1599 int doubleBw = 2 * Tk_Changes(slavePtr->tkwin)->border_width; 1600 1601 if (slavePtr->width <= 0) { 1602 slavePtr->paneWidth = Tk_ReqWidth(slavePtr->tkwin) + doubleBw; 1603 } 1604 if (slavePtr->height <= 0) { 1605 slavePtr->paneHeight = Tk_ReqHeight(slavePtr->tkwin) + doubleBw; 1606 } 1607 ComputeGeometry(pwPtr); 1608 } 1609} 1610 1611/* 1612 *-------------------------------------------------------------- 1613 * 1614 * PanedWindowLostSlaveProc -- 1615 * 1616 * This function is invoked by Tk whenever some other geometry claims 1617 * control over a slave that used to be managed by us. 1618 * 1619 * Results: 1620 * None. 1621 * 1622 * Side effects: 1623 * Forgets all information about the slave. Causes geometry to be 1624 * recomputed for the panedwindow. 1625 * 1626 *-------------------------------------------------------------- 1627 */ 1628 1629static void 1630PanedWindowLostSlaveProc( 1631 ClientData clientData, /* Grid structure for slave window that was 1632 * stolen away. */ 1633 Tk_Window tkwin) /* Tk's handle for the slave window. */ 1634{ 1635 register Slave *slavePtr = (Slave *) clientData; 1636 PanedWindow *pwPtr = (PanedWindow *) (slavePtr->masterPtr); 1637 1638 if (pwPtr->tkwin != Tk_Parent(slavePtr->tkwin)) { 1639 Tk_UnmaintainGeometry(slavePtr->tkwin, pwPtr->tkwin); 1640 } 1641 Unlink(slavePtr); 1642 Tk_DeleteEventHandler(slavePtr->tkwin, StructureNotifyMask, 1643 SlaveStructureProc, (ClientData) slavePtr); 1644 Tk_UnmapWindow(slavePtr->tkwin); 1645 slavePtr->tkwin = NULL; 1646 ckfree((void *)slavePtr); 1647 ComputeGeometry(pwPtr); 1648} 1649 1650/* 1651 *-------------------------------------------------------------- 1652 * 1653 * ArrangePanes -- 1654 * 1655 * This function is invoked (using the Tcl_DoWhenIdle mechanism) to 1656 * re-layout a set of windows managed by a paned window. It is invoked at 1657 * idle time so that a series of pane requests can be merged into a 1658 * single layout operation. 1659 * 1660 * Results: 1661 * None. 1662 * 1663 * Side effects: 1664 * The slaves of masterPtr may get resized or moved. 1665 * 1666 *-------------------------------------------------------------- 1667 */ 1668 1669static void 1670ArrangePanes( 1671 ClientData clientData) /* Structure describing parent whose slaves 1672 * are to be re-layed out. */ 1673{ 1674 register PanedWindow *pwPtr = (PanedWindow *) clientData; 1675 register Slave *slavePtr; 1676 int i, slaveWidth, slaveHeight, slaveX, slaveY; 1677 int paneWidth, paneHeight, paneSize, paneMinSize; 1678 int doubleBw; 1679 int x, y; 1680 int sashWidth, sashOffset, sashCount, handleOffset; 1681 int sashReserve, sxReserve, syReserve; 1682 int internalBW; 1683 int paneDynSize, paneDynMinSize, pwHeight, pwWidth, pwSize; 1684 int first, last; 1685 int stretchReserve, stretchAmount; 1686 const int horizontal = (pwPtr->orient == ORIENT_HORIZONTAL); 1687 1688 pwPtr->flags &= ~(REQUESTED_RELAYOUT|RESIZE_PENDING); 1689 1690 /* 1691 * If the parent has no slaves anymore, then don't do anything at all: 1692 * just leave the parent's size as-is. Otherwise there is no way to 1693 * "relinquish" control over the parent so another geometry manager can 1694 * take over. 1695 */ 1696 1697 if (pwPtr->numSlaves == 0) { 1698 return; 1699 } 1700 1701 Tcl_Preserve((ClientData) pwPtr); 1702 1703 /* 1704 * Find index of last visible pane. 1705 */ 1706 1707 for (i = 0, last = 0, first = -1; i < pwPtr->numSlaves; i++) { 1708 if (pwPtr->slaves[i]->hide == 0) { 1709 if (first < 0) { 1710 first = i; 1711 } 1712 last = i; 1713 } 1714 } 1715 1716 /* 1717 * First pass; compute sizes 1718 */ 1719 1720 paneDynSize = paneDynMinSize = 0; 1721 internalBW = Tk_InternalBorderWidth(pwPtr->tkwin); 1722 pwHeight = Tk_Height(pwPtr->tkwin) - (2 * internalBW); 1723 pwWidth = Tk_Width(pwPtr->tkwin) - (2 * internalBW); 1724 x = y = internalBW; 1725 stretchReserve = (horizontal ? pwWidth : pwHeight); 1726 1727 /* 1728 * Calculate the sash width, including handle and padding, and the sash 1729 * and handle offsets. 1730 */ 1731 1732 sashOffset = handleOffset = pwPtr->sashPad; 1733 if (pwPtr->showHandle && pwPtr->handleSize > pwPtr->sashWidth) { 1734 sashWidth = (2 * pwPtr->sashPad) + pwPtr->handleSize; 1735 sashOffset = ((pwPtr->handleSize - pwPtr->sashWidth) / 2) 1736 + pwPtr->sashPad; 1737 } else { 1738 sashWidth = (2 * pwPtr->sashPad) + pwPtr->sashWidth; 1739 handleOffset = ((pwPtr->sashWidth - pwPtr->handleSize) / 2) 1740 + pwPtr->sashPad; 1741 } 1742 1743 for (i = sashCount = 0; i < pwPtr->numSlaves; i++) { 1744 slavePtr = pwPtr->slaves[i]; 1745 1746 if (slavePtr->hide) { 1747 continue; 1748 } 1749 1750 /* 1751 * Compute the total size needed by all the slaves and the left-over, 1752 * or shortage of space available. 1753 */ 1754 1755 if (horizontal) { 1756 paneSize = slavePtr->paneWidth; 1757 stretchReserve -= paneSize + (2 * slavePtr->padx); 1758 } else { 1759 paneSize = slavePtr->paneHeight; 1760 stretchReserve -= paneSize + (2 * slavePtr->pady); 1761 } 1762 if (IsStretchable(slavePtr->stretch,i,first,last) 1763 && Tk_IsMapped(pwPtr->tkwin)) { 1764 paneDynSize += paneSize; 1765 paneDynMinSize += slavePtr->minSize; 1766 } 1767 if (i != last) { 1768 stretchReserve -= sashWidth; 1769 sashCount++; 1770 } 1771 } 1772 1773 /* 1774 * Second pass; adjust/arrange panes. 1775 */ 1776 1777 for (i = 0; i < pwPtr->numSlaves; i++) { 1778 slavePtr = pwPtr->slaves[i]; 1779 1780 if (slavePtr->hide) { 1781 Tk_UnmaintainGeometry(slavePtr->tkwin, pwPtr->tkwin); 1782 Tk_UnmapWindow(slavePtr->tkwin); 1783 continue; 1784 } 1785 1786 /* 1787 * Compute the size of this slave. The algorithm (assuming a 1788 * horizontal paned window) is: 1789 * 1790 * 1. Get "base" dimensions. If a width or height is specified for 1791 * this slave, use those values; else use the ReqWidth/ReqHeight. 1792 * 2. Using base dimensions, pane dimensions, and sticky values, 1793 * determine the x and y, and actual width and height of the 1794 * widget. 1795 */ 1796 1797 doubleBw = 2 * Tk_Changes(slavePtr->tkwin)->border_width; 1798 slaveWidth = (slavePtr->width > 0 ? slavePtr->width : 1799 Tk_ReqWidth(slavePtr->tkwin) + doubleBw); 1800 slaveHeight = (slavePtr->height > 0 ? slavePtr->height : 1801 Tk_ReqHeight(slavePtr->tkwin) + doubleBw); 1802 paneMinSize = slavePtr->minSize; 1803 1804 /* 1805 * Calculate pane width and height. 1806 */ 1807 1808 if (horizontal) { 1809 paneSize = slavePtr->paneWidth; 1810 pwSize = pwWidth; 1811 } else { 1812 paneSize = slavePtr->paneHeight; 1813 pwSize = pwHeight; 1814 } 1815 if (IsStretchable(slavePtr->stretch, i, first, last)) { 1816 double frac; 1817 1818 if (paneDynSize > 0) { 1819 frac = (double)paneSize / (double)paneDynSize; 1820 } else { 1821 frac = (double)paneSize / (double)pwSize; 1822 } 1823 1824 paneDynSize -= paneSize; 1825 paneDynMinSize -= slavePtr->minSize; 1826 stretchAmount = (int) (frac * stretchReserve); 1827 if (paneSize + stretchAmount >= paneMinSize) { 1828 stretchReserve -= stretchAmount; 1829 paneSize += stretchAmount; 1830 } else { 1831 stretchReserve += paneSize - paneMinSize; 1832 paneSize = paneMinSize; 1833 } 1834 if (i == last && stretchReserve > 0) { 1835 paneSize += stretchReserve; 1836 stretchReserve = 0; 1837 } 1838 } else if (paneDynSize - paneDynMinSize + stretchReserve < 0) { 1839 if (paneSize + paneDynSize - paneDynMinSize + stretchReserve 1840 <= paneMinSize) { 1841 stretchReserve += paneSize - paneMinSize; 1842 paneSize = paneMinSize; 1843 } else { 1844 paneSize += paneDynSize - paneDynMinSize + stretchReserve; 1845 stretchReserve = paneDynMinSize - paneDynSize; 1846 } 1847 } 1848 if (horizontal) { 1849 paneWidth = paneSize; 1850 paneHeight = pwHeight - (2 * slavePtr->pady); 1851 } else { 1852 paneWidth = pwWidth - (2 * slavePtr->padx); 1853 paneHeight = paneSize; 1854 } 1855 1856 /* 1857 * Adjust for area reserved for sashes. 1858 */ 1859 1860 if (sashCount) { 1861 sashReserve = sashWidth * sashCount; 1862 if (horizontal) { 1863 sxReserve = sashReserve; 1864 syReserve = 0; 1865 } else { 1866 sxReserve = 0; 1867 syReserve = sashReserve; 1868 } 1869 } else { 1870 sxReserve = syReserve = 0; 1871 } 1872 1873 if (pwWidth - sxReserve < x + paneWidth - internalBW) { 1874 paneWidth = pwWidth - sxReserve - x + internalBW; 1875 } 1876 if (pwHeight - syReserve < y + paneHeight - internalBW) { 1877 paneHeight = pwHeight - syReserve - y + internalBW; 1878 } 1879 1880 if (slaveWidth > paneWidth) { 1881 slaveWidth = paneWidth; 1882 } 1883 if (slaveHeight > paneHeight) { 1884 slaveHeight = paneHeight; 1885 } 1886 1887 slavePtr->x = x; 1888 slavePtr->y = y; 1889 1890 /* 1891 * Compute the location of the sash at the right or bottom of the 1892 * parcel and the location of the next parcel. 1893 */ 1894 1895 if (horizontal) { 1896 x += paneWidth + (2 * slavePtr->padx); 1897 if (x < internalBW) { 1898 x = internalBW; 1899 } 1900 slavePtr->sashx = x + sashOffset; 1901 slavePtr->sashy = y; 1902 slavePtr->handlex = x + handleOffset; 1903 slavePtr->handley = y + pwPtr->handlePad; 1904 x += sashWidth; 1905 } else { 1906 y += paneHeight + (2 * slavePtr->pady); 1907 if (y < internalBW) { 1908 y = internalBW; 1909 } 1910 slavePtr->sashx = x; 1911 slavePtr->sashy = y + sashOffset; 1912 slavePtr->handlex = x + pwPtr->handlePad; 1913 slavePtr->handley = y + handleOffset; 1914 y += sashWidth; 1915 } 1916 1917 /* 1918 * Compute the actual dimensions of the slave in the pane. 1919 */ 1920 1921 slaveX = slavePtr->x; 1922 slaveY = slavePtr->y; 1923 AdjustForSticky(slavePtr->sticky, paneWidth, paneHeight, 1924 &slaveX, &slaveY, &slaveWidth, &slaveHeight); 1925 1926 slaveX += slavePtr->padx; 1927 slaveY += slavePtr->pady; 1928 1929 /* 1930 * Now put the window in the proper spot. 1931 */ 1932 1933 if (slaveWidth <= 0 || slaveHeight <= 0 || 1934 (horizontal ? slaveX - internalBW > pwWidth : 1935 slaveY - internalBW > pwHeight)) { 1936 Tk_UnmaintainGeometry(slavePtr->tkwin, pwPtr->tkwin); 1937 Tk_UnmapWindow(slavePtr->tkwin); 1938 } else { 1939 Tk_MaintainGeometry(slavePtr->tkwin, pwPtr->tkwin, 1940 slaveX, slaveY, slaveWidth, slaveHeight); 1941 } 1942 sashCount--; 1943 } 1944 Tcl_Release((ClientData) pwPtr); 1945} 1946 1947/* 1948 *---------------------------------------------------------------------- 1949 * 1950 * Unlink -- 1951 * 1952 * Remove a slave from a paned window. 1953 * 1954 * Results: 1955 * None. 1956 * 1957 * Side effects: 1958 * The paned window will be scheduled for re-arranging and redrawing. 1959 * 1960 *---------------------------------------------------------------------- 1961 */ 1962 1963static void 1964Unlink( 1965 register Slave *slavePtr) /* Window to unlink. */ 1966{ 1967 register PanedWindow *masterPtr; 1968 int i, j; 1969 1970 masterPtr = slavePtr->masterPtr; 1971 if (masterPtr == NULL) { 1972 return; 1973 } 1974 1975 /* 1976 * Find the specified slave in the panedwindow's list of slaves, then 1977 * remove it from that list. 1978 */ 1979 1980 for (i = 0; i < masterPtr->numSlaves; i++) { 1981 if (masterPtr->slaves[i] == slavePtr) { 1982 for (j = i; j < masterPtr->numSlaves - 1; j++) { 1983 masterPtr->slaves[j] = masterPtr->slaves[j + 1]; 1984 } 1985 break; 1986 } 1987 } 1988 1989 /* 1990 * Clean out any -after or -before references to this slave 1991 */ 1992 1993 for (i = 0; i < masterPtr->numSlaves; i++) { 1994 if (masterPtr->slaves[i]->before == slavePtr->tkwin) { 1995 masterPtr->slaves[i]->before = None; 1996 } 1997 if (masterPtr->slaves[i]->after == slavePtr->tkwin) { 1998 masterPtr->slaves[i]->after = None; 1999 } 2000 } 2001 2002 masterPtr->flags |= REQUESTED_RELAYOUT; 2003 if (!(masterPtr->flags & REDRAW_PENDING)) { 2004 masterPtr->flags |= REDRAW_PENDING; 2005 Tcl_DoWhenIdle(DisplayPanedWindow, (ClientData) masterPtr); 2006 } 2007 2008 /* 2009 * Set the slave's masterPtr to NULL, so that we can tell that the slave 2010 * is no longer attached to any panedwindow. 2011 */ 2012 2013 slavePtr->masterPtr = NULL; 2014 2015 masterPtr->numSlaves--; 2016} 2017 2018/* 2019 *---------------------------------------------------------------------- 2020 * 2021 * GetPane -- 2022 * 2023 * Given a token to a Tk window, find the pane that corresponds to that 2024 * token in a given paned window. 2025 * 2026 * Results: 2027 * Pointer to the slave structure, or NULL if the window is not managed 2028 * by this paned window. 2029 * 2030 * Side effects: 2031 * None. 2032 * 2033 *---------------------------------------------------------------------- 2034 */ 2035 2036static Slave * 2037GetPane( 2038 PanedWindow *pwPtr, /* Pointer to the paned window info. */ 2039 Tk_Window tkwin) /* Window to search for. */ 2040{ 2041 int i; 2042 2043 for (i = 0; i < pwPtr->numSlaves; i++) { 2044 if (pwPtr->slaves[i]->tkwin == tkwin) { 2045 return pwPtr->slaves[i]; 2046 } 2047 } 2048 return NULL; 2049} 2050 2051/* 2052 *-------------------------------------------------------------- 2053 * 2054 * SlaveStructureProc -- 2055 * 2056 * This function is invoked whenever StructureNotify events occur for a 2057 * window that's managed by a paned window. This function's only purpose 2058 * is to clean up when windows are deleted. 2059 * 2060 * Results: 2061 * None. 2062 * 2063 * Side effects: 2064 * The paned window slave structure associated with the window 2065 * is freed, and the slave is disassociated from the paned 2066 * window which managed it. 2067 * 2068 *-------------------------------------------------------------- 2069 */ 2070 2071static void 2072SlaveStructureProc( 2073 ClientData clientData, /* Pointer to record describing window item. */ 2074 XEvent *eventPtr) /* Describes what just happened. */ 2075{ 2076 Slave *slavePtr = (Slave *) clientData; 2077 PanedWindow *pwPtr = slavePtr->masterPtr; 2078 2079 if (eventPtr->type == DestroyNotify) { 2080 Unlink(slavePtr); 2081 slavePtr->tkwin = NULL; 2082 ckfree((void *)slavePtr); 2083 ComputeGeometry(pwPtr); 2084 } 2085} 2086 2087/* 2088 *---------------------------------------------------------------------- 2089 * 2090 * ComputeGeometry -- 2091 * 2092 * Compute geometry for the paned window, including coordinates of all 2093 * slave windows and each sash. 2094 * 2095 * Results: 2096 * None. 2097 * 2098 * Side effects: 2099 * Recomputes geometry information for a paned window. 2100 * 2101 *---------------------------------------------------------------------- 2102 */ 2103 2104static void 2105ComputeGeometry( 2106 PanedWindow *pwPtr) /* Pointer to the Paned Window structure. */ 2107{ 2108 int i, x, y, doubleBw, internalBw; 2109 int sashWidth, sashOffset, handleOffset; 2110 int reqWidth, reqHeight, dim; 2111 Slave *slavePtr; 2112 const int horizontal = (pwPtr->orient == ORIENT_HORIZONTAL); 2113 2114 pwPtr->flags |= REQUESTED_RELAYOUT; 2115 2116 x = y = internalBw = Tk_InternalBorderWidth(pwPtr->tkwin); 2117 reqWidth = reqHeight = 0; 2118 2119 /* 2120 * Sashes and handles share space on the display. To simplify processing 2121 * below, precompute the x and y offsets of the handles and sashes within 2122 * the space occupied by their combination; later, just add those offsets 2123 * blindly (avoiding the extra showHandle, etc, checks). 2124 */ 2125 2126 sashOffset = handleOffset = pwPtr->sashPad; 2127 if (pwPtr->showHandle && pwPtr->handleSize > pwPtr->sashWidth) { 2128 sashWidth = (2 * pwPtr->sashPad) + pwPtr->handleSize; 2129 sashOffset = ((pwPtr->handleSize - pwPtr->sashWidth) / 2) 2130 + pwPtr->sashPad; 2131 } else { 2132 sashWidth = (2 * pwPtr->sashPad) + pwPtr->sashWidth; 2133 handleOffset = ((pwPtr->sashWidth - pwPtr->handleSize) / 2) 2134 + pwPtr->sashPad; 2135 } 2136 2137 for (i = 0; i < pwPtr->numSlaves; i++) { 2138 slavePtr = pwPtr->slaves[i]; 2139 2140 if (slavePtr->hide) { 2141 continue; 2142 } 2143 2144 /* 2145 * First set the coordinates for the top left corner of the slave's 2146 * parcel. 2147 */ 2148 2149 slavePtr->x = x; 2150 slavePtr->y = y; 2151 2152 /* 2153 * Make sure the pane's paned dimension is at least minsize. This 2154 * check may be redundant, since the only way to change a pane's size 2155 * is by moving a sash, and that code checks the minsize. 2156 */ 2157 2158 if (horizontal) { 2159 if (slavePtr->paneWidth < slavePtr->minSize) { 2160 slavePtr->paneWidth = slavePtr->minSize; 2161 } 2162 } else { 2163 if (slavePtr->paneHeight < slavePtr->minSize) { 2164 slavePtr->paneHeight = slavePtr->minSize; 2165 } 2166 } 2167 2168 /* 2169 * Compute the location of the sash at the right or bottom of the 2170 * parcel. 2171 */ 2172 2173 if (horizontal) { 2174 x += slavePtr->paneWidth + (2 * slavePtr->padx); 2175 slavePtr->sashx = x + sashOffset; 2176 slavePtr->sashy = y; 2177 slavePtr->handlex = x + handleOffset; 2178 slavePtr->handley = y + pwPtr->handlePad; 2179 x += sashWidth; 2180 } else { 2181 y += slavePtr->paneHeight + (2 * slavePtr->pady); 2182 slavePtr->sashx = x; 2183 slavePtr->sashy = y + sashOffset; 2184 slavePtr->handlex = x + pwPtr->handlePad; 2185 slavePtr->handley = y + handleOffset; 2186 y += sashWidth; 2187 } 2188 2189 /* 2190 * Find the maximum height/width of the slaves, for computing the 2191 * requested height/width of the paned window. 2192 */ 2193 2194 if (horizontal) { 2195 /* 2196 * If the slave has an explicit height set, use that; otherwise, 2197 * use the slave's requested height. 2198 */ 2199 2200 if (slavePtr->height > 0) { 2201 dim = slavePtr->height; 2202 } else { 2203 doubleBw = 2 * Tk_Changes(slavePtr->tkwin)->border_width; 2204 dim = Tk_ReqHeight(slavePtr->tkwin) + doubleBw; 2205 } 2206 dim += 2 * slavePtr->pady; 2207 if (dim > reqHeight) { 2208 reqHeight = dim; 2209 } 2210 } else { 2211 /* 2212 * If the slave has an explicit width set use that; otherwise, use 2213 * the slave's requested width. 2214 */ 2215 2216 if (slavePtr->width > 0) { 2217 dim = slavePtr->width; 2218 } else { 2219 doubleBw = 2 * Tk_Changes(slavePtr->tkwin)->border_width; 2220 dim = Tk_ReqWidth(slavePtr->tkwin) + doubleBw; 2221 } 2222 dim += 2 * slavePtr->padx; 2223 if (dim > reqWidth) { 2224 reqWidth = dim; 2225 } 2226 } 2227 } 2228 2229 /* 2230 * The loop above should have left x (or y) equal to the sum of the widths 2231 * (or heights) of the widgets, plus the size of one sash and the sash 2232 * padding for each widget, plus the width of the left (or top) border of 2233 * the paned window. 2234 * 2235 * The requested width (or height) is therefore x (or y) minus the size of 2236 * one sash and padding, plus the width of the right (or bottom) border of 2237 * the paned window. 2238 * 2239 * The height (or width) is equal to the maximum height (or width) of the 2240 * slaves, plus the width of the border of the top and bottom (or left and 2241 * right) of the paned window. 2242 * 2243 * If the panedwindow has an explicit width/height set use that; 2244 * otherwise, use the requested width/height. 2245 */ 2246 2247 if (horizontal) { 2248 reqWidth = (pwPtr->width > 0 ? 2249 pwPtr->width : x - sashWidth + internalBw); 2250 reqHeight = (pwPtr->height > 0 ? 2251 pwPtr->height : reqHeight + (2 * internalBw)); 2252 } else { 2253 reqWidth = (pwPtr->width > 0 ? 2254 pwPtr->width : reqWidth + (2 * internalBw)); 2255 reqHeight = (pwPtr->height > 0 ? 2256 pwPtr->height : y - sashWidth + internalBw); 2257 } 2258 Tk_GeometryRequest(pwPtr->tkwin, reqWidth, reqHeight); 2259 if (Tk_IsMapped(pwPtr->tkwin) && !(pwPtr->flags & REDRAW_PENDING)) { 2260 pwPtr->flags |= REDRAW_PENDING; 2261 Tcl_DoWhenIdle(DisplayPanedWindow, (ClientData) pwPtr); 2262 } 2263} 2264 2265/* 2266 *---------------------------------------------------------------------- 2267 * 2268 * DestroyOptionTables -- 2269 * 2270 * This function is registered as an exit callback when the paned window 2271 * command is first called. It cleans up the OptionTables structure 2272 * allocated by that command. 2273 * 2274 * Results: 2275 * None. 2276 * 2277 * Side effects: 2278 * Frees memory. 2279 * 2280 *---------------------------------------------------------------------- 2281 */ 2282 2283static void 2284DestroyOptionTables( 2285 ClientData clientData, /* Pointer to the OptionTables struct */ 2286 Tcl_Interp *interp) /* Pointer to the calling interp */ 2287{ 2288 ckfree((char *)clientData); 2289 return; 2290} 2291 2292/* 2293 *---------------------------------------------------------------------- 2294 * 2295 * GetSticky - 2296 * 2297 * Converts an internal boolean combination of "sticky" bits into a Tcl 2298 * string obj containing zero or more of n, s, e, or w. 2299 * 2300 * Results: 2301 * Tcl_Obj containing the string representation of the sticky value. 2302 * 2303 * Side effects: 2304 * Creates a new Tcl_Obj. 2305 * 2306 *---------------------------------------------------------------------- 2307 */ 2308 2309static Tcl_Obj * 2310GetSticky( 2311 ClientData clientData, 2312 Tk_Window tkwin, 2313 char *recordPtr, /* Pointer to widget record. */ 2314 int internalOffset) /* Offset within *recordPtr containing the 2315 * sticky value. */ 2316{ 2317 int sticky = *(int *)(recordPtr + internalOffset); 2318 static char buffer[5]; 2319 int count = 0; 2320 2321 if (sticky & STICK_NORTH) { 2322 buffer[count++] = 'n'; 2323 } 2324 if (sticky & STICK_EAST) { 2325 buffer[count++] = 'e'; 2326 } 2327 if (sticky & STICK_SOUTH) { 2328 buffer[count++] = 's'; 2329 } 2330 if (sticky & STICK_WEST) { 2331 buffer[count++] = 'w'; 2332 } 2333 buffer[count] = '\0'; 2334 2335 return Tcl_NewStringObj(buffer, -1); 2336} 2337 2338/* 2339 *---------------------------------------------------------------------- 2340 * 2341 * SetSticky -- 2342 * 2343 * Converts a Tcl_Obj representing a widgets stickyness into an integer 2344 * value. 2345 * 2346 * Results: 2347 * Standard Tcl result. 2348 * 2349 * Side effects: 2350 * May store the integer value into the internal representation pointer. 2351 * May change the pointer to the Tcl_Obj to NULL to indicate that the 2352 * specified string was empty and that is acceptable. 2353 * 2354 *---------------------------------------------------------------------- 2355 */ 2356 2357static int 2358SetSticky( 2359 ClientData clientData, 2360 Tcl_Interp *interp, /* Current interp; may be used for errors. */ 2361 Tk_Window tkwin, /* Window for which option is being set. */ 2362 Tcl_Obj **value, /* Pointer to the pointer to the value object. 2363 * We use a pointer to the pointer because we 2364 * may need to return a value (NULL). */ 2365 char *recordPtr, /* Pointer to storage for the widget record. */ 2366 int internalOffset, /* Offset within *recordPtr at which the 2367 * internal value is to be stored. */ 2368 char *oldInternalPtr, /* Pointer to storage for the old value. */ 2369 int flags) /* Flags for the option, set Tk_SetOptions. */ 2370{ 2371 int sticky = 0; 2372 char c, *string, *internalPtr; 2373 2374 internalPtr = ComputeSlotAddress(recordPtr, internalOffset); 2375 2376 if (flags & TK_OPTION_NULL_OK && ObjectIsEmpty(*value)) { 2377 *value = NULL; 2378 } else { 2379 /* 2380 * Convert the sticky specifier into an integer value. 2381 */ 2382 2383 string = Tcl_GetString(*value); 2384 2385 while ((c = *string++) != '\0') { 2386 switch (c) { 2387 case 'n': case 'N': 2388 sticky |= STICK_NORTH; 2389 break; 2390 case 'e': case 'E': 2391 sticky |= STICK_EAST; 2392 break; 2393 case 's': case 'S': 2394 sticky |= STICK_SOUTH; 2395 break; 2396 case 'w': case 'W': 2397 sticky |= STICK_WEST; 2398 break; 2399 case ' ': case ',': case '\t': case '\r': case '\n': 2400 break; 2401 default: 2402 Tcl_ResetResult(interp); 2403 Tcl_AppendResult(interp, "bad stickyness value \"", 2404 Tcl_GetString(*value), "\": must be a string ", 2405 "containing zero or more of n, e, s, and w", NULL); 2406 return TCL_ERROR; 2407 } 2408 } 2409 } 2410 2411 if (internalPtr != NULL) { 2412 *((int *) oldInternalPtr) = *((int *) internalPtr); 2413 *((int *) internalPtr) = sticky; 2414 } 2415 return TCL_OK; 2416} 2417 2418/* 2419 *---------------------------------------------------------------------- 2420 * 2421 * RestoreSticky -- 2422 * 2423 * Restore a sticky option value from a saved value. 2424 * 2425 * Results: 2426 * None. 2427 * 2428 * Side effects: 2429 * Restores the old value. 2430 * 2431 *---------------------------------------------------------------------- 2432 */ 2433 2434static void 2435RestoreSticky( 2436 ClientData clientData, 2437 Tk_Window tkwin, 2438 char *internalPtr, /* Pointer to storage for value. */ 2439 char *oldInternalPtr) /* Pointer to old value. */ 2440{ 2441 *(int *)internalPtr = *(int *)oldInternalPtr; 2442} 2443 2444/* 2445 *---------------------------------------------------------------------- 2446 * 2447 * AdjustForSticky -- 2448 * 2449 * Given the x,y coords of the top-left corner of a pane, the dimensions 2450 * of that pane, and the dimensions of a slave, compute the x,y coords 2451 * and actual dimensions of the slave based on the slave's sticky value. 2452 * 2453 * Results: 2454 * No direct return; sets the x, y, slaveWidth and slaveHeight to correct 2455 * values. 2456 * 2457 * Side effects: 2458 * None. 2459 * 2460 *---------------------------------------------------------------------- 2461 */ 2462 2463static void 2464AdjustForSticky( 2465 int sticky, /* Sticky value; see top of file for 2466 * definition. */ 2467 int cavityWidth, /* Width of the cavity. */ 2468 int cavityHeight, /* Height of the cavity. */ 2469 int *xPtr, int *yPtr, /* Initially, coordinates of the top-left 2470 * corner of cavity; also return values for 2471 * actual x, y coords of slave. */ 2472 int *slaveWidthPtr, /* Slave width. */ 2473 int *slaveHeightPtr) /* Slave height. */ 2474{ 2475 int diffx = 0; /* Cavity width - slave width. */ 2476 int diffy = 0; /* Cavity hight - slave height. */ 2477 2478 if (cavityWidth > *slaveWidthPtr) { 2479 diffx = cavityWidth - *slaveWidthPtr; 2480 } 2481 2482 if (cavityHeight > *slaveHeightPtr) { 2483 diffy = cavityHeight - *slaveHeightPtr; 2484 } 2485 2486 if ((sticky & STICK_EAST) && (sticky & STICK_WEST)) { 2487 *slaveWidthPtr += diffx; 2488 } 2489 if ((sticky & STICK_NORTH) && (sticky & STICK_SOUTH)) { 2490 *slaveHeightPtr += diffy; 2491 } 2492 if (!(sticky & STICK_WEST)) { 2493 *xPtr += (sticky & STICK_EAST) ? diffx : diffx/2; 2494 } 2495 if (!(sticky & STICK_NORTH)) { 2496 *yPtr += (sticky & STICK_SOUTH) ? diffy : diffy/2; 2497 } 2498} 2499 2500/* 2501 *---------------------------------------------------------------------- 2502 * 2503 * MoveSash -- 2504 * 2505 * Move the sash given by index the amount given. 2506 * 2507 * Results: 2508 * None. 2509 * 2510 * Side effects: 2511 * Recomputes the sizes of the panes in a panedwindow. 2512 * 2513 *---------------------------------------------------------------------- 2514 */ 2515 2516static void 2517MoveSash( 2518 PanedWindow *pwPtr, 2519 int sash, 2520 int diff) 2521{ 2522 int i; 2523 int expandPane, reduceFirst, reduceLast, reduceIncr, slaveSize, sashOffset; 2524 Slave *slavePtr; 2525 int stretchReserve = 0; 2526 int nextSash = sash + 1; 2527 const int horizontal = (pwPtr->orient == ORIENT_HORIZONTAL); 2528 2529 if (diff == 0) 2530 return; 2531 2532 /* 2533 * Update the slave sizes with their real sizes. 2534 */ 2535 2536 if (pwPtr->showHandle && pwPtr->handleSize > pwPtr->sashWidth) { 2537 sashOffset = ((pwPtr->handleSize - pwPtr->sashWidth) / 2) 2538 + pwPtr->sashPad; 2539 } else { 2540 sashOffset = pwPtr->sashPad; 2541 } 2542 for (i = 0; i < pwPtr->numSlaves; i++) { 2543 slavePtr = pwPtr->slaves[i]; 2544 if (slavePtr->hide) { 2545 continue; 2546 } 2547 if (horizontal) { 2548 slavePtr->paneWidth = slavePtr->width = slavePtr->sashx 2549 - sashOffset - slavePtr->x - (2 * slavePtr->padx); 2550 } else { 2551 slavePtr->paneWidth = slavePtr->height = slavePtr->sashy 2552 - sashOffset - slavePtr->y - (2 * slavePtr->pady); 2553 } 2554 } 2555 2556 /* 2557 * There must be a next sash since it is only possible to enter this 2558 * routine when moving an actual sash which implies there exists a visible 2559 * pane to either side of the sash. 2560 */ 2561 2562 while (nextSash < pwPtr->numSlaves-1 && pwPtr->slaves[nextSash]->hide) { 2563 nextSash++; 2564 } 2565 2566 /* 2567 * Consolidate +/-diff variables to reduce duplicate code. 2568 */ 2569 2570 if (diff > 0) { 2571 expandPane = sash; 2572 reduceFirst = nextSash; 2573 reduceLast = pwPtr->numSlaves; 2574 reduceIncr = 1; 2575 } else { 2576 diff = abs(diff); 2577 expandPane = nextSash; 2578 reduceFirst = sash; 2579 reduceLast = -1; 2580 reduceIncr = -1; 2581 } 2582 2583 /* 2584 * Calculate how much room we have to stretch in and adjust diff value 2585 * accordingly. 2586 */ 2587 2588 for (i = reduceFirst; i != reduceLast; i += reduceIncr) { 2589 slavePtr = pwPtr->slaves[i]; 2590 if (slavePtr->hide) { 2591 continue; 2592 } 2593 if (horizontal) { 2594 stretchReserve += slavePtr->width - slavePtr->minSize; 2595 } else { 2596 stretchReserve += slavePtr->height - slavePtr->minSize; 2597 } 2598 } 2599 if (stretchReserve <= 0) { 2600 return; 2601 } 2602 if (diff > stretchReserve) { 2603 diff = stretchReserve; 2604 } 2605 2606 /* 2607 * Expand pane by diff amount. 2608 */ 2609 2610 slavePtr = pwPtr->slaves[expandPane]; 2611 if (horizontal) { 2612 slavePtr->paneWidth = slavePtr->width += diff; 2613 } else { 2614 slavePtr->paneHeight = slavePtr->height += diff; 2615 } 2616 2617 /* 2618 * Reduce panes, respecting minsize, until diff amount has been used. 2619 */ 2620 2621 for (i = reduceFirst; i != reduceLast; i += reduceIncr) { 2622 slavePtr = pwPtr->slaves[i]; 2623 if (slavePtr->hide) { 2624 continue; 2625 } 2626 if (horizontal) { 2627 slaveSize = slavePtr->width; 2628 } else { 2629 slaveSize = slavePtr->height; 2630 } 2631 if (diff > (slaveSize - slavePtr->minSize)) { 2632 diff -= slaveSize - slavePtr->minSize; 2633 slaveSize = slavePtr->minSize; 2634 } else { 2635 slaveSize -= diff; 2636 i = reduceLast - reduceIncr; 2637 } 2638 if (horizontal) { 2639 slavePtr->paneWidth = slavePtr->width = slaveSize; 2640 } else { 2641 slavePtr->paneHeight = slavePtr->height = slaveSize; 2642 } 2643 } 2644} 2645 2646/* 2647 *---------------------------------------------------------------------- 2648 * 2649 * ProxyWindowEventProc -- 2650 * 2651 * This function is invoked by the Tk dispatcher for various events on 2652 * paned window proxy windows. 2653 * 2654 * Results: 2655 * None. 2656 * 2657 * Side effects: 2658 * When the window gets deleted, internal structures get cleaned up. Whena 2659 * it gets exposed, it is redisplayed. 2660 * 2661 *-------------------------------------------------------------- 2662 */ 2663 2664static void 2665ProxyWindowEventProc( 2666 ClientData clientData, /* Information about window. */ 2667 XEvent *eventPtr) /* Information about event. */ 2668{ 2669 PanedWindow *pwPtr = (PanedWindow *) clientData; 2670 2671 if (eventPtr->type == Expose) { 2672 if (pwPtr->proxywin != NULL &&!(pwPtr->flags & PROXY_REDRAW_PENDING)) { 2673 Tcl_DoWhenIdle(DisplayProxyWindow, (ClientData) pwPtr); 2674 pwPtr->flags |= PROXY_REDRAW_PENDING; 2675 } 2676 } 2677} 2678 2679/* 2680 *-------------------------------------------------------------- 2681 * 2682 * DisplayProxyWindow -- 2683 * 2684 * This function redraws a paned window proxy window. It is invoked as a 2685 * do-when-idle handler, so it only runs when there's nothing else for 2686 * the application to do. 2687 * 2688 * Results: 2689 * None. 2690 * 2691 * Side effects: 2692 * Information appears on the screen. 2693 * 2694 *-------------------------------------------------------------- 2695 */ 2696 2697static void 2698DisplayProxyWindow( 2699 ClientData clientData) /* Information about window. */ 2700{ 2701 PanedWindow *pwPtr = (PanedWindow *) clientData; 2702 Pixmap pixmap; 2703 Tk_Window tkwin = pwPtr->proxywin; 2704 pwPtr->flags &= ~PROXY_REDRAW_PENDING; 2705 if ((tkwin == NULL) || !Tk_IsMapped(tkwin)) { 2706 return; 2707 } 2708 2709#ifndef TK_NO_DOUBLE_BUFFERING 2710 /* 2711 * Create a pixmap for double-buffering, if necessary. 2712 */ 2713 2714 pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), 2715 Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); 2716#else 2717 pixmap = Tk_WindowId(tkwin); 2718#endif /* TK_NO_DOUBLE_BUFFERING */ 2719 2720 /* 2721 * Redraw the widget's background and border. 2722 */ 2723 2724 Tk_Fill3DRectangle(tkwin, pixmap, pwPtr->background, 0, 0, 2725 Tk_Width(tkwin), Tk_Height(tkwin), 2, pwPtr->sashRelief); 2726 2727#ifndef TK_NO_DOUBLE_BUFFERING 2728 /* 2729 * Copy the pixmap to the display. 2730 */ 2731 2732 XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin), pwPtr->gc, 0, 0, 2733 (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin), 0, 0); 2734 Tk_FreePixmap(Tk_Display(tkwin), pixmap); 2735#endif /* TK_NO_DOUBLE_BUFFERING */ 2736} 2737 2738/* 2739 *---------------------------------------------------------------------- 2740 * 2741 * PanedWindowProxyCommand -- 2742 * 2743 * Handles the panedwindow proxy subcommand. See the user documentation 2744 * for details. 2745 * 2746 * Results: 2747 * Standard Tcl result. 2748 * 2749 * Side effects: 2750 * May map or unmap the proxy sash. 2751 * 2752 *---------------------------------------------------------------------- 2753 */ 2754 2755static int 2756PanedWindowProxyCommand( 2757 PanedWindow *pwPtr, /* Pointer to paned window information. */ 2758 Tcl_Interp *interp, /* Current interpreter. */ 2759 int objc, /* Number of arguments. */ 2760 Tcl_Obj *CONST objv[]) /* Argument objects. */ 2761{ 2762 static CONST char *optionStrings[] = { 2763 "coord", "forget", "place", NULL 2764 }; 2765 enum options { 2766 PROXY_COORD, PROXY_FORGET, PROXY_PLACE 2767 }; 2768 int index, x, y, sashWidth, sashHeight; 2769 Tcl_Obj *coords[2]; 2770 2771 if (objc < 3) { 2772 Tcl_WrongNumArgs(interp, 2, objv, "option ?arg ...?"); 2773 return TCL_ERROR; 2774 } 2775 2776 if (Tcl_GetIndexFromObj(interp, objv[2], optionStrings, "option", 0, 2777 &index) != TCL_OK) { 2778 return TCL_ERROR; 2779 } 2780 2781 switch ((enum options) index) { 2782 case PROXY_COORD: 2783 if (objc != 3) { 2784 Tcl_WrongNumArgs(interp, 3, objv, NULL); 2785 return TCL_ERROR; 2786 } 2787 2788 coords[0] = Tcl_NewIntObj(pwPtr->proxyx); 2789 coords[1] = Tcl_NewIntObj(pwPtr->proxyy); 2790 Tcl_SetListObj(Tcl_GetObjResult(interp), 2, coords); 2791 break; 2792 2793 case PROXY_FORGET: 2794 if (objc != 3) { 2795 Tcl_WrongNumArgs(interp, 3, objv, NULL); 2796 return TCL_ERROR; 2797 } 2798 if (Tk_IsMapped(pwPtr->proxywin)) { 2799 Tk_UnmapWindow(pwPtr->proxywin); 2800 Tk_UnmaintainGeometry(pwPtr->proxywin, pwPtr->tkwin); 2801 } 2802 break; 2803 2804 case PROXY_PLACE: 2805 if (objc != 5) { 2806 Tcl_WrongNumArgs(interp, 3, objv, "x y"); 2807 return TCL_ERROR; 2808 } 2809 2810 if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) { 2811 return TCL_ERROR; 2812 } 2813 2814 if (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) { 2815 return TCL_ERROR; 2816 } 2817 2818 if (pwPtr->orient == ORIENT_HORIZONTAL) { 2819 if (x < 0) { 2820 x = 0; 2821 } 2822 y = Tk_InternalBorderWidth(pwPtr->tkwin); 2823 sashWidth = pwPtr->sashWidth; 2824 sashHeight = Tk_Height(pwPtr->tkwin) - 2825 (2 * Tk_InternalBorderWidth(pwPtr->tkwin)); 2826 } else { 2827 if (y < 0) { 2828 y = 0; 2829 } 2830 x = Tk_InternalBorderWidth(pwPtr->tkwin); 2831 sashHeight = pwPtr->sashWidth; 2832 sashWidth = Tk_Width(pwPtr->tkwin) - 2833 (2 * Tk_InternalBorderWidth(pwPtr->tkwin)); 2834 } 2835 2836 if (sashWidth < 1) { 2837 sashWidth = 1; 2838 } 2839 if (sashHeight < 1) { 2840 sashHeight = 1; 2841 } 2842 2843 /* 2844 * Stash the proxy coordinates for future "proxy coord" calls. 2845 */ 2846 2847 pwPtr->proxyx = x; 2848 pwPtr->proxyy = y; 2849 2850 /* 2851 * Make sure the proxy window is higher in the stacking order than the 2852 * slaves, so that it will be visible when drawn. It would be more 2853 * correct to push the proxy window just high enough to appear above 2854 * the highest slave, but it's much easier to just force it all the 2855 * way to the top of the stacking order. 2856 */ 2857 2858 Tk_RestackWindow(pwPtr->proxywin, Above, NULL); 2859 2860 /* 2861 * Let Tk_MaintainGeometry take care of placing the window at the 2862 * right coordinates. 2863 */ 2864 2865 Tk_MaintainGeometry(pwPtr->proxywin, pwPtr->tkwin, x, y, 2866 sashWidth, sashHeight); 2867 break; 2868 } 2869 2870 return TCL_OK; 2871} 2872 2873/* 2874 *---------------------------------------------------------------------- 2875 * 2876 * ObjectIsEmpty -- 2877 * 2878 * This function tests whether the string value of an object is empty. 2879 * 2880 * Results: 2881 * The return value is 1 if the string value of objPtr has length zero, 2882 * and 0 otherwise. 2883 * 2884 * Side effects: 2885 * May cause object shimmering, since this function can force a 2886 * conversion to a string object. 2887 * 2888 *---------------------------------------------------------------------- 2889 */ 2890 2891static int 2892ObjectIsEmpty( 2893 Tcl_Obj *objPtr) /* Object to test. May be NULL. */ 2894{ 2895 int length; 2896 2897 if (objPtr == NULL) { 2898 return 1; 2899 } 2900 if (objPtr->bytes != NULL) { 2901 return (objPtr->length == 0); 2902 } 2903 Tcl_GetStringFromObj(objPtr, &length); 2904 return (length == 0); 2905} 2906 2907/* 2908 *---------------------------------------------------------------------- 2909 * 2910 * ComputeInternalPointer -- 2911 * 2912 * Given a pointer to the start of a record and the offset of a slot 2913 * within that record, compute the address of that slot. 2914 * 2915 * Results: 2916 * If offset is non-negative, returns the computed address; else, returns 2917 * NULL. 2918 * 2919 * Side effects: 2920 * None. 2921 * 2922 *---------------------------------------------------------------------- 2923 */ 2924 2925static char * 2926ComputeSlotAddress( 2927 char *recordPtr, /* Pointer to the start of a record. */ 2928 int offset) /* Offset of a slot within that record; may be < 0. */ 2929{ 2930 if (offset >= 0) { 2931 return recordPtr + offset; 2932 } else { 2933 return NULL; 2934 } 2935} 2936 2937/* 2938 *---------------------------------------------------------------------- 2939 * 2940 * PanedWindowIdentifyCoords -- 2941 * 2942 * Given a pair of x,y coordinates, identify the panedwindow component at 2943 * that point, if any. 2944 * 2945 * Results: 2946 * Standard Tcl result. 2947 * 2948 * Side effects: 2949 * Modifies the interpreter's result to contain either an empty list, or 2950 * a two element list of the form {sash n} or {handle n} to indicate that 2951 * the point lies within the n'th sash or handle. 2952 * 2953 *---------------------------------------------------------------------- 2954 */ 2955 2956static int 2957PanedWindowIdentifyCoords( 2958 PanedWindow *pwPtr, /* Information about the widget. */ 2959 Tcl_Interp *interp, /* Interpreter in which to store result. */ 2960 int x, int y) /* Coordinates of the point to identify. */ 2961{ 2962 Tcl_Obj *list; 2963 int i, sashHeight, sashWidth, thisx, thisy; 2964 int found, isHandle, lpad, rpad, tpad, bpad; 2965 list = Tcl_NewObj(); 2966 2967 if (pwPtr->orient == ORIENT_HORIZONTAL) { 2968 if (Tk_IsMapped(pwPtr->tkwin)) { 2969 sashHeight = Tk_Height(pwPtr->tkwin); 2970 } else { 2971 sashHeight = Tk_ReqHeight(pwPtr->tkwin); 2972 } 2973 sashHeight -= 2 * Tk_InternalBorderWidth(pwPtr->tkwin); 2974 if (pwPtr->showHandle && pwPtr->handleSize > pwPtr->sashWidth) { 2975 sashWidth = pwPtr->handleSize; 2976 lpad = (pwPtr->handleSize - pwPtr->sashWidth) / 2; 2977 rpad = pwPtr->handleSize - lpad; 2978 lpad += pwPtr->sashPad; 2979 rpad += pwPtr->sashPad; 2980 } else { 2981 sashWidth = pwPtr->sashWidth; 2982 lpad = rpad = pwPtr->sashPad; 2983 } 2984 tpad = bpad = 0; 2985 } else { 2986 if (pwPtr->showHandle && pwPtr->handleSize > pwPtr->sashWidth) { 2987 sashHeight = pwPtr->handleSize; 2988 tpad = (pwPtr->handleSize - pwPtr->sashWidth) / 2; 2989 bpad = pwPtr->handleSize - tpad; 2990 tpad += pwPtr->sashPad; 2991 bpad += pwPtr->sashPad; 2992 } else { 2993 sashHeight = pwPtr->sashWidth; 2994 tpad = bpad = pwPtr->sashPad; 2995 } 2996 if (Tk_IsMapped(pwPtr->tkwin)) { 2997 sashWidth = Tk_Width(pwPtr->tkwin); 2998 } else { 2999 sashWidth = Tk_ReqWidth(pwPtr->tkwin); 3000 } 3001 sashWidth -= 2 * Tk_InternalBorderWidth(pwPtr->tkwin); 3002 lpad = rpad = 0; 3003 } 3004 3005 isHandle = 0; 3006 found = -1; 3007 for (i = 0; i < pwPtr->numSlaves - 1; i++) { 3008 if (pwPtr->slaves[i]->hide) { 3009 continue; 3010 } 3011 thisx = pwPtr->slaves[i]->sashx; 3012 thisy = pwPtr->slaves[i]->sashy; 3013 3014 if (((thisx - lpad) <= x && x <= (thisx + rpad + sashWidth)) && 3015 ((thisy - tpad) <= y && y <= (thisy + bpad + sashHeight))) { 3016 found = i; 3017 3018 /* 3019 * Determine if the point is over the handle or the sash. 3020 */ 3021 3022 if (pwPtr->showHandle) { 3023 thisx = pwPtr->slaves[i]->handlex; 3024 thisy = pwPtr->slaves[i]->handley; 3025 if (pwPtr->orient == ORIENT_HORIZONTAL) { 3026 if (thisy <= y && y <= (thisy + pwPtr->handleSize)) { 3027 isHandle = 1; 3028 } 3029 } else { 3030 if (thisx <= x && x <= (thisx + pwPtr->handleSize)) { 3031 isHandle = 1; 3032 } 3033 } 3034 } 3035 break; 3036 } 3037 } 3038 3039 /* 3040 * Set results. 3041 */ 3042 3043 if (found != -1) { 3044 Tcl_ListObjAppendElement(interp, list, Tcl_NewIntObj(found)); 3045 Tcl_ListObjAppendElement(interp, list, Tcl_NewStringObj( 3046 (isHandle ? "handle" : "sash"), -1)); 3047 } 3048 3049 Tcl_SetObjResult(interp, list); 3050 return TCL_OK; 3051} 3052 3053/* 3054 * Local Variables: 3055 * mode: c 3056 * c-basic-offset: 4 3057 * fill-column: 78 3058 * End: 3059 */ 3060