/* * tkPanedWindow.c -- * * This module implements "paned window" widgets that are object * based. A "paned window" is a widget that manages the geometry for * some number of other widgets, placing a movable "sash" between them, * which can be used to alter the relative sizes of adjacent widgets. * * Copyright (c) 1997 Sun Microsystems, Inc. * Copyright (c) 2000 Ajuba Solutions. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tkPanedWindow.c,v 1.13.2.1 2003/07/17 00:37:03 hobbs Exp $ */ #include "tkPort.h" #include "default.h" #include "tkInt.h" /* Flag values for "sticky"ness The 16 combinations subsume the packer's * notion of anchor and fill. * * STICK_NORTH This window sticks to the top of its cavity. * STICK_EAST This window sticks to the right edge of its cavity. * STICK_SOUTH This window sticks to the bottom of its cavity. * STICK_WEST This window sticks to the left edge of its cavity. */ #define STICK_NORTH 1 #define STICK_EAST 2 #define STICK_SOUTH 4 #define STICK_WEST 8 /* * The following table defines the legal values for the -orient option. */ static char *orientStrings[] = { "horizontal", "vertical", (char *) NULL }; enum orient { ORIENT_HORIZONTAL, ORIENT_VERTICAL }; typedef struct { Tk_OptionTable pwOptions; /* Token for paned window option table. */ Tk_OptionTable slaveOpts; /* Token for slave cget option table. */ } OptionTables; /* * One structure of the following type is kept for each window * managed by a paned window widget. */ typedef struct Slave { Tk_Window tkwin; /* Window being managed. */ int minSize; /* Minimum size of this pane, on the * relevant axis, in pixels. */ int padx; /* Additional padding requested for * slave, in the x dimension. */ int pady; /* Additional padding requested for * slave, in the y dimension. */ Tcl_Obj *widthPtr, *heightPtr; /* Tcl_Obj rep's of slave width/height, * to allow for null values. */ int width; /* Slave width. */ int height; /* Slave height. */ int sticky; /* Sticky string. */ int x, y; /* Coordinates of the widget. */ int paneWidth, paneHeight; /* Pane dimensions (may be different * from slave width/height). */ int sashx, sashy; /* Coordinates of the sash of the * right or bottom of this pane. */ int markx, marky; /* Coordinates of the last mark set * for the sash. */ int handlex, handley; /* Coordinates of the sash handle. */ struct PanedWindow *masterPtr; /* Paned window managing the window. */ Tk_Window after; /* Placeholder for parsing options. */ Tk_Window before; /* Placeholder for parsing options. */ } Slave; /* * A data structure of the following type is kept for each paned window * widget managed by this file: */ typedef struct PanedWindow { Tk_Window tkwin; /* Window that embodies the paned window. */ Tk_Window proxywin; /* Window for the resizing proxy. */ Display *display; /* X's token for the window's display. */ Tcl_Interp *interp; /* Interpreter associated with widget. */ Tcl_Command widgetCmd; /* Token for square's widget command. */ Tk_OptionTable optionTable; /* Token representing the configuration * specifications. */ Tk_OptionTable slaveOpts; /* Token for slave cget table. */ Tk_3DBorder background; /* Background color. */ int borderWidth; /* Value of -borderwidth option. */ int relief; /* 3D border effect (TK_RELIEF_RAISED, etc) */ Tcl_Obj *widthPtr; /* Tcl_Obj rep for width. */ Tcl_Obj *heightPtr; /* Tcl_Obj rep for height. */ int width, height; /* Width and height of the widget. */ enum orient orient; /* Orientation of the widget. */ Tk_Cursor cursor; /* Current cursor for window, or None. */ int resizeOpaque; /* Boolean indicating whether resize should be * opaque or rubberband style. */ int sashRelief; /* Relief used to draw sash. */ int sashWidth; /* Width of each sash, in pixels. */ Tcl_Obj *sashWidthPtr; /* Tcl_Obj rep for sash width. */ int sashPad; /* Additional padding around each sash. */ Tcl_Obj *sashPadPtr; /* Tcl_Obj rep for sash padding. */ int showHandle; /* Boolean indicating whether sash handles * should be drawn. */ int handleSize; /* Size of one side of a sash handle (handles * are square), in pixels. */ int handlePad; /* Distance from border to draw handle. */ Tcl_Obj *handleSizePtr; /* Tcl_Obj rep for handle size. */ Tk_Cursor sashCursor; /* Cursor used when mouse is above a sash. */ GC gc; /* Graphics context for copying from * off-screen pixmap onto screen. */ int proxyx, proxyy; /* Proxy x,y coordinates. */ Slave **slaves; /* Pointer to array of Slaves. */ int numSlaves; /* Number of slaves. */ int sizeofSlaves; /* Number of elements in the slaves array. */ int flags; /* Flags for widget; see below. */ } PanedWindow; /* * Flags used for paned windows: * * REDRAW_PENDING: Non-zero means a DoWhenIdle handler has * been queued to redraw this window. * * WIDGET_DELETED: Non-zero means that the paned window has * been, or is in the process of being, deleted. * * RESIZE_PENDING: Non-zero means that the window might need to * change its size (or the size of its panes) * because of a change in the size of one of its * children. */ #define REDRAW_PENDING 0x0001 #define WIDGET_DELETED 0x0002 #define REQUESTED_RELAYOUT 0x0004 #define RECOMPUTE_GEOMETRY 0x0008 #define PROXY_REDRAW_PENDING 0x0010 #define RESIZE_PENDING 0x0020 /* * Forward declarations for procedures defined later in this file: */ int Tk_PanedWindowObjCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * CONST objv[])); static void PanedWindowCmdDeletedProc _ANSI_ARGS_((ClientData clientData)); static int ConfigurePanedWindow _ANSI_ARGS_((Tcl_Interp *interp, PanedWindow *pwPtr, int objc, Tcl_Obj *CONST objv[])); static void DestroyPanedWindow _ANSI_ARGS_((PanedWindow *pwPtr)); static void DisplayPanedWindow _ANSI_ARGS_((ClientData clientData)); static void PanedWindowEventProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr)); static void ProxyWindowEventProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr)); static void DisplayProxyWindow _ANSI_ARGS_((ClientData clientData)); void PanedWindowWorldChanged _ANSI_ARGS_((ClientData instanceData)); static int PanedWindowWidgetObjCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp *, int objc, Tcl_Obj * CONST objv[])); static void PanedWindowLostSlaveProc _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin)); static void PanedWindowReqProc _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin)); static void ArrangePanes _ANSI_ARGS_((ClientData clientData)); static void Unlink _ANSI_ARGS_((Slave *slavePtr)); static Slave * GetPane _ANSI_ARGS_((PanedWindow *pwPtr, Tk_Window tkwin)); static void SlaveStructureProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr)); static int PanedWindowSashCommand _ANSI_ARGS_((PanedWindow *pwPtr, Tcl_Interp *interp, int objc, Tcl_Obj * CONST objv[])); static int PanedWindowProxyCommand _ANSI_ARGS_((PanedWindow *pwPtr, Tcl_Interp *interp, int objc, Tcl_Obj * CONST objv[])); static void ComputeGeometry _ANSI_ARGS_((PanedWindow *pwPtr)); static int ConfigureSlaves _ANSI_ARGS_((PanedWindow *pwPtr, Tcl_Interp *interp, int objc, Tcl_Obj * CONST objv[])); static void DestroyOptionTables _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp)); static int SetSticky _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj **value, char *recordPtr, int internalOffset, char *oldInternalPtr, int flags)); static Tcl_Obj *GetSticky _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *recordPtr, int internalOffset)); static void RestoreSticky _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *internalPtr, char *oldInternalPtr)); static void AdjustForSticky _ANSI_ARGS_((int sticky, int cavityWidth, int cavityHeight, int *xPtr, int *yPtr, int *slaveWidthPtr, int *slaveHeightPtr)); static void MoveSash _ANSI_ARGS_((PanedWindow *pwPtr, int sash, int diff)); static int ObjectIsEmpty _ANSI_ARGS_((Tcl_Obj *objPtr)); static char * ComputeSlotAddress _ANSI_ARGS_((char *recordPtr, int offset)); static int PanedWindowIdentifyCoords _ANSI_ARGS_((PanedWindow *pwPtr, Tcl_Interp *interp, int x, int y)); /* * Sashes are between panes only, so there is one less sash than slaves */ #define ValidSashIndex(pwPtr, sash) \ (((sash) >= 0) && ((sash) < ((pwPtr)->numSlaves-1))) static Tk_GeomMgr panedWindowMgrType = { "panedwindow", /* name */ PanedWindowReqProc, /* requestProc */ PanedWindowLostSlaveProc, /* lostSlaveProc */ }; /* * Information used for objv parsing. */ #define GEOMETRY 0x0001 /* * The following structure contains pointers to functions used for processing * the custom "-sticky" option for slave windows. */ static Tk_ObjCustomOption stickyOption = { "sticky", /* name */ SetSticky, /* setProc */ GetSticky, /* getProc */ RestoreSticky, /* restoreProc */ (Tk_CustomOptionFreeProc *)NULL, /* freeProc */ 0 }; static Tk_OptionSpec optionSpecs[] = { {TK_OPTION_BORDER, "-background", "background", "Background", DEF_PANEDWINDOW_BG_COLOR, -1, Tk_Offset(PanedWindow, background), 0, (ClientData) DEF_PANEDWINDOW_BG_MONO}, {TK_OPTION_SYNONYM, "-bd", (char *) NULL, (char *) NULL, (char *) NULL, 0, -1, 0, (ClientData) "-borderwidth"}, {TK_OPTION_SYNONYM, "-bg", (char *) NULL, (char *) NULL, (char *) NULL, 0, -1, 0, (ClientData) "-background"}, {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", DEF_PANEDWINDOW_BORDERWIDTH, -1, Tk_Offset(PanedWindow, borderWidth), 0, 0, GEOMETRY}, {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor", DEF_PANEDWINDOW_CURSOR, -1, Tk_Offset(PanedWindow, cursor), TK_OPTION_NULL_OK, 0, 0}, {TK_OPTION_PIXELS, "-handlepad", "handlePad", "HandlePad", DEF_PANEDWINDOW_HANDLEPAD, -1, Tk_Offset(PanedWindow, handlePad), 0, 0}, {TK_OPTION_PIXELS, "-handlesize", "handleSize", "HandleSize", DEF_PANEDWINDOW_HANDLESIZE, Tk_Offset(PanedWindow, handleSizePtr), Tk_Offset(PanedWindow, handleSize), 0, 0, GEOMETRY}, {TK_OPTION_PIXELS, "-height", "height", "Height", DEF_PANEDWINDOW_HEIGHT, Tk_Offset(PanedWindow, heightPtr), Tk_Offset(PanedWindow, height), TK_OPTION_NULL_OK, 0, GEOMETRY}, {TK_OPTION_BOOLEAN, "-opaqueresize", "opaqueResize", "OpaqueResize", DEF_PANEDWINDOW_OPAQUERESIZE, -1, Tk_Offset(PanedWindow, resizeOpaque), 0, 0, 0}, {TK_OPTION_STRING_TABLE, "-orient", "orient", "Orient", DEF_PANEDWINDOW_ORIENT, -1, Tk_Offset(PanedWindow, orient), 0, (ClientData) orientStrings, GEOMETRY}, {TK_OPTION_RELIEF, "-relief", "relief", "Relief", DEF_PANEDWINDOW_RELIEF, -1, Tk_Offset(PanedWindow, relief), 0, 0, 0}, {TK_OPTION_CURSOR, "-sashcursor", "sashCursor", "Cursor", DEF_PANEDWINDOW_SASHCURSOR, -1, Tk_Offset(PanedWindow, sashCursor), TK_OPTION_NULL_OK, 0, 0}, {TK_OPTION_PIXELS, "-sashpad", "sashPad", "SashPad", DEF_PANEDWINDOW_SASHPAD, -1, Tk_Offset(PanedWindow, sashPad), 0, 0, GEOMETRY}, {TK_OPTION_RELIEF, "-sashrelief", "sashRelief", "Relief", DEF_PANEDWINDOW_SASHRELIEF, -1, Tk_Offset(PanedWindow, sashRelief), 0, 0, 0}, {TK_OPTION_PIXELS, "-sashwidth", "sashWidth", "Width", DEF_PANEDWINDOW_SASHWIDTH, Tk_Offset(PanedWindow, sashWidthPtr), Tk_Offset(PanedWindow, sashWidth), 0, 0, GEOMETRY}, {TK_OPTION_BOOLEAN, "-showhandle", "showHandle", "ShowHandle", DEF_PANEDWINDOW_SHOWHANDLE, -1, Tk_Offset(PanedWindow, showHandle), 0, 0, GEOMETRY}, {TK_OPTION_PIXELS, "-width", "width", "Width", DEF_PANEDWINDOW_WIDTH, Tk_Offset(PanedWindow, widthPtr), Tk_Offset(PanedWindow, width), TK_OPTION_NULL_OK, 0, GEOMETRY}, {TK_OPTION_END} }; static Tk_OptionSpec slaveOptionSpecs[] = { {TK_OPTION_WINDOW, "-after", (char *) NULL, (char *) NULL, DEF_PANEDWINDOW_PANE_AFTER, -1, Tk_Offset(Slave, after), TK_OPTION_NULL_OK, 0, 0}, {TK_OPTION_WINDOW, "-before", (char *) NULL, (char *) NULL, DEF_PANEDWINDOW_PANE_BEFORE, -1, Tk_Offset(Slave, before), TK_OPTION_NULL_OK, 0, 0}, {TK_OPTION_PIXELS, "-height", (char *) NULL, (char *) NULL, DEF_PANEDWINDOW_PANE_HEIGHT, Tk_Offset(Slave, heightPtr), Tk_Offset(Slave, height), TK_OPTION_NULL_OK, 0, 0}, {TK_OPTION_PIXELS, "-minsize", (char *) NULL, (char *) NULL, DEF_PANEDWINDOW_PANE_MINSIZE, -1, Tk_Offset(Slave, minSize), 0, 0, 0}, {TK_OPTION_PIXELS, "-padx", (char *) NULL, (char *) NULL, DEF_PANEDWINDOW_PANE_PADX, -1, Tk_Offset(Slave, padx), 0, 0, 0}, {TK_OPTION_PIXELS, "-pady", (char *) NULL, (char *) NULL, DEF_PANEDWINDOW_PANE_PADY, -1, Tk_Offset(Slave, pady), 0, 0, 0}, {TK_OPTION_CUSTOM, "-sticky", (char *) NULL, (char *) NULL, DEF_PANEDWINDOW_PANE_STICKY, -1, Tk_Offset(Slave, sticky), 0, (ClientData) &stickyOption, 0}, {TK_OPTION_PIXELS, "-width", (char *) NULL, (char *) NULL, DEF_PANEDWINDOW_PANE_WIDTH, Tk_Offset(Slave, widthPtr), Tk_Offset(Slave, width), TK_OPTION_NULL_OK, 0, 0}, {TK_OPTION_END} }; /* *-------------------------------------------------------------- * * Tk_PanedWindowObjCmd -- * * This procedure is invoked to process the "panedwindow" Tcl * command. It creates a new "panedwindow" widget. * * Results: * A standard Tcl result. * * Side effects: * A new widget is created and configured. * *-------------------------------------------------------------- */ int Tk_PanedWindowObjCmd(clientData, interp, objc, objv) ClientData clientData; /* NULL. */ Tcl_Interp *interp; /* Current interpreter. */ int objc; /* Number of arguments. */ Tcl_Obj * CONST objv[]; /* Argument objects. */ { PanedWindow *pwPtr; Tk_Window tkwin, parent; OptionTables *pwOpts; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?"); return TCL_ERROR; } tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), Tcl_GetStringFromObj(objv[1], NULL), (char *) NULL); if (tkwin == NULL) { return TCL_ERROR; } pwOpts = (OptionTables *) Tcl_GetAssocData(interp, "PanedWindowOptionTables", NULL); if (pwOpts == NULL) { /* * The first time this procedure is invoked, the option tables will * be NULL. We then create the option tables from the templates * and store a pointer to the tables as the command's clinical so * we'll have easy access to it in the future. */ pwOpts = (OptionTables *) ckalloc(sizeof(OptionTables)); /* Set up an exit handler to free the optionTables struct */ Tcl_SetAssocData(interp, "PanedWindowOptionTables", DestroyOptionTables, (ClientData) pwOpts); /* Create the paned window option tables. */ pwOpts->pwOptions = Tk_CreateOptionTable(interp, optionSpecs); pwOpts->slaveOpts = Tk_CreateOptionTable(interp, slaveOptionSpecs); } Tk_SetClass(tkwin, "Panedwindow"); /* * Allocate and initialize the widget record. */ pwPtr = (PanedWindow *) ckalloc(sizeof(PanedWindow)); memset((void *)pwPtr, 0, (sizeof(PanedWindow))); pwPtr->tkwin = tkwin; pwPtr->display = Tk_Display(tkwin); pwPtr->interp = interp; pwPtr->widgetCmd = Tcl_CreateObjCommand(interp, Tk_PathName(pwPtr->tkwin), PanedWindowWidgetObjCmd, (ClientData) pwPtr, PanedWindowCmdDeletedProc); pwPtr->optionTable = pwOpts->pwOptions; pwPtr->slaveOpts = pwOpts->slaveOpts; pwPtr->relief = TK_RELIEF_RAISED; pwPtr->gc = None; pwPtr->cursor = None; pwPtr->sashCursor = None; /* * Keep a hold of the associated tkwin until we destroy the widget, * otherwise Tk might free it while we still need it. */ Tcl_Preserve((ClientData) pwPtr->tkwin); if (Tk_InitOptions(interp, (char *) pwPtr, pwOpts->pwOptions, tkwin) != TCL_OK) { Tk_DestroyWindow(pwPtr->tkwin); return TCL_ERROR; } Tk_CreateEventHandler(pwPtr->tkwin, ExposureMask|StructureNotifyMask, PanedWindowEventProc, (ClientData) pwPtr); /* * Find the toplevel ancestor of the panedwindow, and make a proxy * win as a child of that window; this way the proxy can always float * above slaves in the panedwindow. */ parent = Tk_Parent(pwPtr->tkwin); while (!(Tk_IsTopLevel(parent))) { parent = Tk_Parent(parent); if (parent == NULL) { parent = pwPtr->tkwin; break; } } pwPtr->proxywin = Tk_CreateAnonymousWindow(interp, parent, (char *) NULL); Tk_CreateEventHandler(pwPtr->proxywin, ExposureMask, ProxyWindowEventProc, (ClientData) pwPtr); if (ConfigurePanedWindow(interp, pwPtr, objc - 2, objv + 2) != TCL_OK) { Tk_DestroyWindow(pwPtr->proxywin); Tk_DestroyWindow(pwPtr->tkwin); return TCL_ERROR; } Tcl_SetStringObj(Tcl_GetObjResult(interp), Tk_PathName(pwPtr->tkwin), -1); return TCL_OK; } /* *-------------------------------------------------------------- * * PanedWindowWidgetObjCmd -- * * This procedure is invoked to process the Tcl command * that corresponds to a widget managed by this module. * See the user documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *-------------------------------------------------------------- */ static int PanedWindowWidgetObjCmd(clientData, interp, objc, objv) ClientData clientData; /* Information about square widget. */ Tcl_Interp *interp; /* Current interpreter. */ int objc; /* Number of arguments. */ Tcl_Obj * CONST objv[]; /* Argument objects. */ { PanedWindow *pwPtr = (PanedWindow *) clientData; int result = TCL_OK; static CONST char *optionStrings[] = {"add", "cget", "configure", "forget", "identify", "panecget", "paneconfigure", "panes", "proxy", "sash", (char *) NULL}; enum options { PW_ADD, PW_CGET, PW_CONFIGURE, PW_FORGET, PW_IDENTIFY, PW_PANECGET, PW_PANECONFIGURE, PW_PANES, PW_PROXY, PW_SASH }; Tcl_Obj *resultObj; int index, count, i, x, y; Tk_Window tkwin; Slave *slavePtr; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "command", 0, &index) != TCL_OK) { return TCL_ERROR; } Tcl_Preserve((ClientData) pwPtr); switch ((enum options) index) { case PW_ADD: { if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "widget ?widget ...?"); result = TCL_ERROR; break; } result = ConfigureSlaves(pwPtr, interp, objc, objv); break; } case PW_CGET: { if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "option"); result = TCL_ERROR; break; } resultObj = Tk_GetOptionValue(interp, (char *) pwPtr, pwPtr->optionTable, objv[2], pwPtr->tkwin); if (resultObj == NULL) { result = TCL_ERROR; } else { Tcl_SetObjResult(interp, resultObj); } break; } case PW_CONFIGURE: { resultObj = NULL; if (objc <= 3) { resultObj = Tk_GetOptionInfo(interp, (char *) pwPtr, pwPtr->optionTable, (objc == 3) ? objv[2] : (Tcl_Obj *) NULL, pwPtr->tkwin); if (resultObj == NULL) { result = TCL_ERROR; } else { Tcl_SetObjResult(interp, resultObj); } } else { result = ConfigurePanedWindow(interp, pwPtr, objc - 2, objv + 2); } break; } case PW_FORGET: { Tk_Window slave; int i; if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "widget ?widget ...?"); result = TCL_ERROR; break; } /* * Clean up each window named in the arg list. */ for (count = 0, i = 2; i < objc; i++) { slave = Tk_NameToWindow(interp, Tcl_GetString(objv[i]), pwPtr->tkwin); if (slave == NULL) { continue; } slavePtr = GetPane(pwPtr, slave); if ((slavePtr != NULL) && (slavePtr->masterPtr != NULL)) { count++; Tk_ManageGeometry(slave, (Tk_GeomMgr *) NULL, (ClientData) NULL); Tk_UnmaintainGeometry(slavePtr->tkwin, pwPtr->tkwin); Tk_DeleteEventHandler(slavePtr->tkwin, StructureNotifyMask, SlaveStructureProc, (ClientData) slavePtr); Tk_UnmapWindow(slavePtr->tkwin); Unlink(slavePtr); } if (count != 0) { ComputeGeometry(pwPtr); } } break; } case PW_IDENTIFY: { if (objc != 4) { Tcl_WrongNumArgs(interp, 2, objv, "x y"); result = TCL_ERROR; break; } if ((Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) || (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK)) { result = TCL_ERROR; break; } result = PanedWindowIdentifyCoords(pwPtr, interp, x, y); break; } case PW_PANECGET: { if (objc != 4) { Tcl_WrongNumArgs(interp, 2, objv, "pane option"); result = TCL_ERROR; break; } tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), pwPtr->tkwin); if (tkwin == NULL) { result = TCL_ERROR; break; } resultObj = NULL; for (i = 0; i < pwPtr->numSlaves; i++) { if (pwPtr->slaves[i]->tkwin == tkwin) { resultObj = Tk_GetOptionValue(interp, (char *) pwPtr->slaves[i], pwPtr->slaveOpts, objv[3], tkwin); } } if (i == pwPtr->numSlaves) { Tcl_SetResult(interp, "not managed by this window", TCL_STATIC); } if (resultObj == NULL) { result = TCL_ERROR; } else { Tcl_SetObjResult(interp, resultObj); } break; } case PW_PANECONFIGURE: { if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "pane ?option? ?value option value ...?"); result = TCL_ERROR; break; } resultObj = NULL; if (objc <= 4) { tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), pwPtr->tkwin); for (i = 0; i < pwPtr->numSlaves; i++) { if (pwPtr->slaves[i]->tkwin == tkwin) { resultObj = Tk_GetOptionInfo(interp, (char *) pwPtr->slaves[i], pwPtr->slaveOpts, (objc == 4) ? objv[3] : (Tcl_Obj *) NULL, pwPtr->tkwin); if (resultObj == NULL) { result = TCL_ERROR; } else { Tcl_SetObjResult(interp, resultObj); } break; } } } else { result = ConfigureSlaves(pwPtr, interp, objc, objv); } break; } case PW_PANES: { resultObj = Tcl_NewObj(); Tcl_IncrRefCount(resultObj); for (i = 0; i < pwPtr->numSlaves; i++) { Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj(Tk_PathName(pwPtr->slaves[i]->tkwin), -1)); } Tcl_SetObjResult(interp, resultObj); Tcl_DecrRefCount(resultObj); break; } case PW_PROXY: { result = PanedWindowProxyCommand(pwPtr, interp, objc, objv); break; } case PW_SASH: { result = PanedWindowSashCommand(pwPtr, interp, objc, objv); break; } } Tcl_Release((ClientData) pwPtr); return result; } /* *---------------------------------------------------------------------- * * ConfigureSlaves -- * * Add or alter the configuration options of a slave in a paned * window. * * Results: * Standard Tcl result. * * Side effects: * Depends on options; may add a slave to the paned window, may * alter the geometry management options of a slave. * *---------------------------------------------------------------------- */ static int ConfigureSlaves(pwPtr, interp, objc, objv) PanedWindow *pwPtr; /* Information about paned window. */ Tcl_Interp *interp; /* Current interpreter. */ int objc; /* Number of arguments. */ Tcl_Obj * CONST objv[]; /* Argument objects. */ { int i, firstOptionArg, j, found, doubleBw, index, numNewSlaves, haveLoc; int insertIndex; Tk_Window tkwin = NULL, ancestor, parent; Slave *slavePtr, **inserts, **new; Slave options; char *arg; /* * Find the non-window name arguments; these are the configure options * for the slaves. Also validate that the window names given are * legitimate (ie, they are real windows, they are not the panedwindow * itself, etc.). */ for (i = 2; i < objc; i++) { arg = Tcl_GetString(objv[i]); if (arg[0] == '-') { break; } else { tkwin = Tk_NameToWindow(interp, arg, pwPtr->tkwin); if (tkwin == NULL) { /* * Just a plain old bad window; Tk_NameToWindow filled in an * error message for us. */ return TCL_ERROR; } else if (tkwin == pwPtr->tkwin) { /* * A panedwindow cannot manage itself. */ Tcl_ResetResult(interp); Tcl_AppendResult(interp, "can't add ", arg, " to itself", (char *) NULL); return TCL_ERROR; } else if (Tk_IsTopLevel(tkwin)) { /* * A panedwindow cannot manage a toplevel. */ Tcl_ResetResult(interp); Tcl_AppendResult(interp, "can't add toplevel ", arg, " to ", Tk_PathName(pwPtr->tkwin), (char *) NULL); return TCL_ERROR; } else { /* * Make sure the panedwindow is the parent of the slave, * or a descendant of the slave's parent. */ parent = Tk_Parent(tkwin); for (ancestor = pwPtr->tkwin;;ancestor = Tk_Parent(ancestor)) { if (ancestor == parent) { break; } if (Tk_IsTopLevel(ancestor)) { Tcl_ResetResult(interp); Tcl_AppendResult(interp, "can't add ", arg, " to ", Tk_PathName(pwPtr->tkwin), (char *) NULL); return TCL_ERROR; } } } } } firstOptionArg = i; /* * Pre-parse the configuration options, to get the before/after specifiers * into an easy-to-find location (a local variable). Also, check the * return from Tk_SetOptions once, here, so we can save a little bit of * extra testing in the for loop below. */ memset((void *)&options, 0, sizeof(Slave)); if (Tk_SetOptions(interp, (char *) &options, pwPtr->slaveOpts, objc - firstOptionArg, objv + firstOptionArg, pwPtr->tkwin, NULL, NULL) != TCL_OK) { return TCL_ERROR; } /* * If either -after or -before was given, find the numerical index that * corresponds to the given window. If both -after and -before are * given, the option precedence is: -after, then -before. */ index = -1; haveLoc = 0; if (options.after != None) { tkwin = options.after; haveLoc = 1; for (i = 0; i < pwPtr->numSlaves; i++) { if (options.after == pwPtr->slaves[i]->tkwin) { index = i + 1; break; } } } else if (options.before != None) { tkwin = options.before; haveLoc = 1; for (i = 0; i < pwPtr->numSlaves; i++) { if (options.before == pwPtr->slaves[i]->tkwin) { index = i; break; } } } /* * If a window was given for -after/-before, but it's not a window * managed by the panedwindow, throw an error */ if (haveLoc && index == -1) { Tcl_ResetResult(interp); Tcl_AppendResult(interp, "window \"", Tk_PathName(tkwin), "\" is not managed by ", Tk_PathName(pwPtr->tkwin), (char *) NULL); Tk_FreeConfigOptions((char *) &options, pwPtr->slaveOpts, pwPtr->tkwin); return TCL_ERROR; } /* * Allocate an array to hold, in order, the pointers to the slave * structures corresponding to the windows specified. Some of those * structures may already have existed, some may be new. */ inserts = (Slave **)ckalloc(sizeof(Slave *) * (firstOptionArg - 2)); insertIndex = 0; /* * Populate the inserts array, creating new slave structures as necessary, * applying the options to each structure as we go, and, if necessary, * marking the spot in the original slaves array as empty (for pre-existing * slave structures). */ for (i = 0, numNewSlaves = 0; i < firstOptionArg - 2; i++) { /* * We don't check that tkwin is NULL here, because the pre-pass above * guarantees that the input at this stage is good. */ tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[i + 2]), pwPtr->tkwin); found = 0; for (j = 0; j < pwPtr->numSlaves; j++) { if (pwPtr->slaves[j] != NULL && pwPtr->slaves[j]->tkwin == tkwin) { Tk_SetOptions(interp, (char *) pwPtr->slaves[j], pwPtr->slaveOpts, objc - firstOptionArg, objv + firstOptionArg, pwPtr->tkwin, NULL, NULL); found = 1; /* * If the slave is supposed to move, add it to the inserts * array now; otherwise, leave it where it is. */ if (index != -1) { inserts[insertIndex++] = pwPtr->slaves[j]; pwPtr->slaves[j] = NULL; } break; } } if (found) { continue; } /* * Make sure this slave wasn't already put into the inserts array, * ie, when the user specifies the same window multiple times in * a single add commaned. */ for (j = 0; j < insertIndex; j++) { if (inserts[j]->tkwin == tkwin) { found = 1; break; } } if (found) { continue; } /* * Create a new slave structure and initialize it. All slaves * start out with their "natural" dimensions. */ slavePtr = (Slave *) ckalloc(sizeof(Slave)); memset(slavePtr, 0, sizeof(Slave)); Tk_InitOptions(interp, (char *)slavePtr, pwPtr->slaveOpts, pwPtr->tkwin); Tk_SetOptions(interp, (char *)slavePtr, pwPtr->slaveOpts, objc - firstOptionArg, objv + firstOptionArg, pwPtr->tkwin, NULL, NULL); slavePtr->tkwin = tkwin; slavePtr->masterPtr = pwPtr; doubleBw = 2 * Tk_Changes(slavePtr->tkwin)->border_width; if (slavePtr->width > 0) { slavePtr->paneWidth = slavePtr->width; } else { slavePtr->paneWidth = Tk_ReqWidth(tkwin) + doubleBw; } if (slavePtr->height > 0) { slavePtr->paneHeight = slavePtr->height; } else { slavePtr->paneHeight = Tk_ReqHeight(tkwin) + doubleBw; } /* * Set up the geometry management callbacks for this slave. */ Tk_CreateEventHandler(slavePtr->tkwin, StructureNotifyMask, SlaveStructureProc, (ClientData) slavePtr); Tk_ManageGeometry(slavePtr->tkwin, &panedWindowMgrType, (ClientData) slavePtr); inserts[insertIndex++] = slavePtr; numNewSlaves++; } /* * Allocate the new slaves array, then copy the slaves into it, in * order. */ i = sizeof(Slave *) * (pwPtr->numSlaves+numNewSlaves); new = (Slave **)ckalloc((unsigned) i); memset(new, 0, (size_t) i); if (index == -1) { /* * If none of the existing slaves have to be moved, just copy the old * and append the new. */ memcpy((void *)&(new[0]), pwPtr->slaves, sizeof(Slave *) * pwPtr->numSlaves); memcpy((void *)&(new[pwPtr->numSlaves]), inserts, sizeof(Slave *) * numNewSlaves); } else { /* * If some of the existing slaves were moved, the old slaves array * will be partially populated, with some valid and some invalid * entries. Walk through it, copying valid entries to the new slaves * array as we go; when we get to the insert location for the new * slaves, copy the inserts array over, then finish off the old slaves * array. */ for (i = 0, j = 0; i < index; i++) { if (pwPtr->slaves[i] != NULL) { new[j] = pwPtr->slaves[i]; j++; } } memcpy((void *)&(new[j]), inserts, sizeof(Slave *) * (insertIndex)); j += firstOptionArg - 2; for (i = index; i < pwPtr->numSlaves; i++) { if (pwPtr->slaves[i] != NULL) { new[j] = pwPtr->slaves[i]; j++; } } } /* * Make the new slaves array the paned window's slave array, and clean up. */ ckfree((void *)pwPtr->slaves); ckfree((void *)inserts); pwPtr->slaves = new; /* * Set the paned window's slave count to the new value. */ pwPtr->numSlaves += numNewSlaves; Tk_FreeConfigOptions((char *) &options, pwPtr->slaveOpts, pwPtr->tkwin); ComputeGeometry(pwPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * PanedWindowSashCommand -- * * Implementation of the panedwindow sash subcommand. See the user * documentation for details on what it does. * * Results: * Standard Tcl result. * * Side effects: * Depends on the arguments. * *---------------------------------------------------------------------- */ static int PanedWindowSashCommand(pwPtr, interp, objc, objv) PanedWindow *pwPtr; /* Pointer to paned window information. */ Tcl_Interp *interp; /* Current interpreter. */ int objc; /* Number of arguments. */ Tcl_Obj * CONST objv[]; /* Argument objects. */ { static CONST char *sashOptionStrings[] = { "coord", "dragto", "mark", "place", (char *) NULL }; enum sashOptions { SASH_COORD, SASH_DRAGTO, SASH_MARK, SASH_PLACE }; int index, sash, x, y, diff; Tcl_Obj *coords[2]; Slave *slavePtr; if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "option ?arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[2], sashOptionStrings, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } Tcl_ResetResult(interp); switch ((enum sashOptions) index) { case SASH_COORD: { if (objc != 4) { Tcl_WrongNumArgs(interp, 3, objv, "index"); return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[3], &sash) != TCL_OK) { return TCL_ERROR; } if (!ValidSashIndex(pwPtr, sash)) { Tcl_ResetResult(interp); Tcl_SetResult(interp, "invalid sash index", TCL_STATIC); return TCL_ERROR; } slavePtr = pwPtr->slaves[sash]; coords[0] = Tcl_NewIntObj(slavePtr->sashx); coords[1] = Tcl_NewIntObj(slavePtr->sashy); Tcl_SetListObj(Tcl_GetObjResult(interp), 2, coords); break; } case SASH_MARK: { if (objc != 6 && objc != 4) { Tcl_WrongNumArgs(interp, 3, objv, "index ?x y?"); return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[3], &sash) != TCL_OK) { return TCL_ERROR; } if (!ValidSashIndex(pwPtr, sash)) { Tcl_ResetResult(interp); Tcl_SetResult(interp, "invalid sash index", TCL_STATIC); return TCL_ERROR; } if (objc == 6) { if (Tcl_GetIntFromObj(interp, objv[4], &x) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[5], &y) != TCL_OK) { return TCL_ERROR; } pwPtr->slaves[sash]->markx = x; pwPtr->slaves[sash]->marky = y; } else { coords[0] = Tcl_NewIntObj(pwPtr->slaves[sash]->markx); coords[1] = Tcl_NewIntObj(pwPtr->slaves[sash]->marky); Tcl_SetListObj(Tcl_GetObjResult(interp), 2, coords); } break; } case SASH_DRAGTO: case SASH_PLACE: { if (objc != 6) { Tcl_WrongNumArgs(interp, 3, objv, "index x y"); return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[3], &sash) != TCL_OK) { return TCL_ERROR; } if (!ValidSashIndex(pwPtr, sash)) { Tcl_ResetResult(interp); Tcl_SetResult(interp, "invalid sash index", TCL_STATIC); return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[4], &x) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[5], &y) != TCL_OK) { return TCL_ERROR; } slavePtr = pwPtr->slaves[sash]; if (pwPtr->orient == ORIENT_HORIZONTAL) { if (index == SASH_PLACE) { diff = x - pwPtr->slaves[sash]->sashx; } else { diff = x - pwPtr->slaves[sash]->markx; } } else { if (index == SASH_PLACE) { diff = y - pwPtr->slaves[sash]->sashy; } else { diff = y - pwPtr->slaves[sash]->marky; } } MoveSash(pwPtr, sash, diff); ComputeGeometry(pwPtr); } } return TCL_OK; } /* *---------------------------------------------------------------------- * * ConfigurePanedWindow -- * * This procedure is called to process an argv/argc list in * conjunction with the Tk option database to configure (or * reconfigure) a paned window widget. * * Results: * The return value is a standard Tcl result. If TCL_ERROR is * returned, then the interp's result contains an error message. * * Side effects: * Configuration information, such as colors, border width, * etc. get set for pwPtr; old resources get freed, * if there were any. * *---------------------------------------------------------------------- */ static int ConfigurePanedWindow(interp, pwPtr, objc, objv) Tcl_Interp *interp; /* Used for error reporting. */ PanedWindow *pwPtr; /* Information about widget. */ int objc; /* Number of arguments. */ Tcl_Obj *CONST objv[]; /* Argument values. */ { Tk_SavedOptions savedOptions; int typemask = 0; if (Tk_SetOptions(interp, (char *) pwPtr, pwPtr->optionTable, objc, objv, pwPtr->tkwin, &savedOptions, &typemask) != TCL_OK) { Tk_RestoreSavedOptions(&savedOptions); return TCL_ERROR; } Tk_FreeSavedOptions(&savedOptions); PanedWindowWorldChanged((ClientData) pwPtr); /* * If an option that affects geometry has changed, make a relayout * request. */ if (typemask & GEOMETRY) { ComputeGeometry(pwPtr); } return TCL_OK; } /* *---------------------------------------------------------------------- * * PanedWindowWorldChanged -- * * This procedure is invoked anytime a paned window's world has * changed in some way that causes the widget to have to recompute * graphics contexts and geometry. * * Results: * None. * * Side effects: * Paned window will be relayed out and redisplayed. * *---------------------------------------------------------------------- */ void PanedWindowWorldChanged(instanceData) ClientData instanceData; /* Information about the paned window. */ { XGCValues gcValues; GC newGC; PanedWindow *pwPtr = (PanedWindow *) instanceData; /* * Allocated a graphics context for drawing the paned window widget * elements (background, sashes, etc.). */ gcValues.background = Tk_3DBorderColor(pwPtr->background)->pixel; newGC = Tk_GetGC(pwPtr->tkwin, GCBackground, &gcValues); if (pwPtr->gc != None) { Tk_FreeGC(pwPtr->display, pwPtr->gc); } pwPtr->gc = newGC; /* * Issue geometry size requests to Tk. */ Tk_SetInternalBorder(pwPtr->tkwin, pwPtr->borderWidth); if (pwPtr->width > 0 || pwPtr->height > 0) { Tk_GeometryRequest(pwPtr->tkwin, pwPtr->width, pwPtr->height); } /* * Arrange for the window to be redrawn, if neccessary. */ if (Tk_IsMapped(pwPtr->tkwin) && !(pwPtr->flags & REDRAW_PENDING)) { Tcl_DoWhenIdle(DisplayPanedWindow, (ClientData) pwPtr); pwPtr->flags |= REDRAW_PENDING; } } /* *-------------------------------------------------------------- * * PanedWindowEventProc -- * * This procedure is invoked by the Tk dispatcher for various * events on paned windows. * * Results: * None. * * Side effects: * When the window gets deleted, internal structures get * cleaned up. When it gets exposed, it is redisplayed. * *-------------------------------------------------------------- */ static void PanedWindowEventProc(clientData, eventPtr) ClientData clientData; /* Information about window. */ XEvent *eventPtr; /* Information about event. */ { PanedWindow *pwPtr = (PanedWindow *) clientData; if (eventPtr->type == Expose) { if (pwPtr->tkwin != NULL && !(pwPtr->flags & REDRAW_PENDING)) { Tcl_DoWhenIdle(DisplayPanedWindow, (ClientData) pwPtr); pwPtr->flags |= REDRAW_PENDING; } } else if (eventPtr->type == ConfigureNotify) { pwPtr->flags |= REQUESTED_RELAYOUT; if (pwPtr->tkwin != NULL && !(pwPtr->flags & REDRAW_PENDING)) { Tcl_DoWhenIdle(DisplayPanedWindow, (ClientData) pwPtr); pwPtr->flags |= REDRAW_PENDING; } } else if (eventPtr->type == DestroyNotify) { DestroyPanedWindow(pwPtr); } } /* *---------------------------------------------------------------------- * * PanedWindowCmdDeletedProc -- * * This procedure is invoked when a widget command is deleted. If * the widget isn't already in the process of being destroyed, * this command destroys it. * * Results: * None. * * Side effects: * The widget is destroyed. * *---------------------------------------------------------------------- */ static void PanedWindowCmdDeletedProc(clientData) ClientData clientData; /* Pointer to widget record for widget. */ { PanedWindow *pwPtr = (PanedWindow *) clientData; /* * This procedure could be invoked either because the window was * destroyed and the command was then deleted or because the command was * deleted, and then this procedure destroys the widget. The * WIDGET_DELETED flag distinguishes these cases. */ if (!(pwPtr->flags & WIDGET_DELETED)) { Tk_DestroyWindow(pwPtr->proxywin); Tk_DestroyWindow(pwPtr->tkwin); } } /* *-------------------------------------------------------------- * * DisplayPanedWindow -- * * This procedure redraws the contents of a paned window widget. * It is invoked as a do-when-idle handler, so it only runs * when there's nothing else for the application to do. * * Results: * None. * * Side effects: * Information appears on the screen. * *-------------------------------------------------------------- */ static void DisplayPanedWindow(clientData) ClientData clientData; /* Information about window. */ { PanedWindow *pwPtr = (PanedWindow *) clientData; Pixmap pixmap; Tk_Window tkwin = pwPtr->tkwin; int i, sashWidth, sashHeight; pwPtr->flags &= ~REDRAW_PENDING; if ((pwPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { return; } if (pwPtr->flags & REQUESTED_RELAYOUT) { ArrangePanes(clientData); } /* * Create a pixmap for double-buffering, if necessary. */ pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); /* * Redraw the widget's background and border. */ Tk_Fill3DRectangle(tkwin, pixmap, pwPtr->background, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), pwPtr->borderWidth, pwPtr->relief); /* * Set up boilerplate geometry values for sashes (width, height, common * coordinates). */ if (pwPtr->orient == ORIENT_HORIZONTAL) { sashHeight = Tk_Height(tkwin) - (2 * Tk_InternalBorderWidth(tkwin)); sashWidth = pwPtr->sashWidth; } else { sashWidth = Tk_Width(tkwin) - (2 * Tk_InternalBorderWidth(tkwin)); sashHeight = pwPtr->sashWidth; } /* * Draw the sashes. */ for (i = 0; i < pwPtr->numSlaves - 1; i++) { Tk_Fill3DRectangle(tkwin, pixmap, pwPtr->background, pwPtr->slaves[i]->sashx, pwPtr->slaves[i]->sashy, sashWidth, sashHeight, 1, pwPtr->sashRelief); if (pwPtr->showHandle) { Tk_Fill3DRectangle(tkwin, pixmap, pwPtr->background, pwPtr->slaves[i]->handlex, pwPtr->slaves[i]->handley, pwPtr->handleSize, pwPtr->handleSize, 1, TK_RELIEF_RAISED); } } /* * Copy the information from the off-screen pixmap onto the screen, * then delete the pixmap. */ XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin), pwPtr->gc, 0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin), 0, 0); Tk_FreePixmap(Tk_Display(tkwin), pixmap); } /* *---------------------------------------------------------------------- * * DestroyPanedWindow -- * * This procedure is invoked by PanedWindowEventProc to free the * internal structure of a paned window. * * Results: * None. * * Side effects: * Everything associated with the paned window is freed up. * *---------------------------------------------------------------------- */ static void DestroyPanedWindow(pwPtr) PanedWindow *pwPtr; /* Info about paned window widget. */ { int i; /* * First mark the widget as in the process of being deleted, * so that any code that causes calls to other paned window procedures * will abort. */ pwPtr->flags |= WIDGET_DELETED; /* * Cancel idle callbacks for redrawing the widget and for rearranging * the panes. */ if (pwPtr->flags & REDRAW_PENDING) { Tcl_CancelIdleCall(DisplayPanedWindow, (ClientData) pwPtr); } if (pwPtr->flags & RESIZE_PENDING) { Tcl_CancelIdleCall(ArrangePanes, (ClientData) pwPtr); } /* * Clean up the slave list; foreach slave: * o Cancel the slave's structure notification callback * o Cancel geometry management for the slave. * o Free memory for the slave */ for (i = 0; i < pwPtr->numSlaves; i++) { Tk_DeleteEventHandler(pwPtr->slaves[i]->tkwin, StructureNotifyMask, SlaveStructureProc, (ClientData) pwPtr->slaves[i]); Tk_ManageGeometry(pwPtr->slaves[i]->tkwin, NULL, NULL); Tk_FreeConfigOptions((char *)pwPtr->slaves[i], pwPtr->slaveOpts, pwPtr->tkwin); ckfree((void *)pwPtr->slaves[i]); pwPtr->slaves[i] = NULL; } if (pwPtr->slaves) { ckfree((char *) pwPtr->slaves); } /* * Remove the widget command from the interpreter. */ Tcl_DeleteCommandFromToken(pwPtr->interp, pwPtr->widgetCmd); /* * Let Tk_FreeConfigOptions clean up the rest. */ Tk_FreeConfigOptions((char *) pwPtr, pwPtr->optionTable, pwPtr->tkwin); Tcl_Release((ClientData) pwPtr->tkwin); pwPtr->tkwin = NULL; Tcl_EventuallyFree((ClientData) pwPtr, TCL_DYNAMIC); } /* *-------------------------------------------------------------- * * PanedWindowReqProc -- * * This procedure is invoked by Tk_GeometryRequest for * windows managed by a paned window. * * Results: * None. * * Side effects: * Arranges for tkwin, and all its managed siblings, to * be re-arranged at the next idle point. * *-------------------------------------------------------------- */ static void PanedWindowReqProc(clientData, tkwin) ClientData clientData; /* Paned window's information about * window that got new preferred * geometry. */ Tk_Window tkwin; /* Other Tk-related information * about the window. */ { Slave *panePtr = (Slave *) clientData; PanedWindow *pwPtr = (PanedWindow *) (panePtr->masterPtr); if (Tk_IsMapped(pwPtr->tkwin)) { if (!(pwPtr->flags & RESIZE_PENDING)) { pwPtr->flags |= RESIZE_PENDING; Tcl_DoWhenIdle(ArrangePanes, (ClientData) pwPtr); } } else { ComputeGeometry(pwPtr); } } /* *-------------------------------------------------------------- * * PanedWindowLostSlaveProc -- * * This procedure is invoked by Tk whenever some other geometry * claims control over a slave that used to be managed by us. * * Results: * None. * * Side effects: * Forgets all information about the slave. Causes geometry to * be recomputed for the panedwindow. * *-------------------------------------------------------------- */ static void PanedWindowLostSlaveProc(clientData, tkwin) ClientData clientData; /* Grid structure for slave window that * was stolen away. */ Tk_Window tkwin; /* Tk's handle for the slave window. */ { register Slave *slavePtr = (Slave *) clientData; PanedWindow *pwPtr = (PanedWindow *) (slavePtr->masterPtr); if (pwPtr->tkwin != Tk_Parent(slavePtr->tkwin)) { Tk_UnmaintainGeometry(slavePtr->tkwin, pwPtr->tkwin); } Unlink(slavePtr); Tk_DeleteEventHandler(slavePtr->tkwin, StructureNotifyMask, SlaveStructureProc, (ClientData) slavePtr); Tk_UnmapWindow(slavePtr->tkwin); slavePtr->tkwin = NULL; ckfree((void *)slavePtr); ComputeGeometry(pwPtr); } /* *-------------------------------------------------------------- * * ArrangePanes -- * * This procedure is invoked (using the Tcl_DoWhenIdle * mechanism) to re-layout a set of windows managed by * a paned window. It is invoked at idle time so that a * series of pane requests can be merged into a single * layout operation. * * Results: * None. * * Side effects: * The slaves of masterPtr may get resized or moved. * *-------------------------------------------------------------- */ static void ArrangePanes(clientData) ClientData clientData; /* Structure describing parent whose slaves * are to be re-layed out. */ { register PanedWindow *pwPtr = (PanedWindow *) clientData; register Slave *slavePtr; int i, slaveWidth, slaveHeight, slaveX, slaveY, paneWidth, paneHeight; int doubleBw; pwPtr->flags &= ~(REQUESTED_RELAYOUT|RESIZE_PENDING); /* * If the parent has no slaves anymore, then don't do anything * at all: just leave the parent's size as-is. Otherwise there is * no way to "relinquish" control over the parent so another geometry * manager can take over. */ if (pwPtr->numSlaves == 0) { return; } Tcl_Preserve((ClientData) pwPtr); for (i = 0; i < pwPtr->numSlaves; i++) { slavePtr = pwPtr->slaves[i]; /* * Compute the size of this slave. The algorithm (assuming a * horizontal paned window) is: * * 1. Get "base" dimensions. If a width or height is specified * for this slave, use those values; else use the * ReqWidth/ReqHeight. * 2. Using base dimensions, pane dimensions, and sticky values, * determine the x and y, and actual width and height of the * widget. */ doubleBw = 2 * Tk_Changes(slavePtr->tkwin)->border_width; slaveWidth = (slavePtr->width > 0 ? slavePtr->width : Tk_ReqWidth(slavePtr->tkwin) + doubleBw); slaveHeight = (slavePtr->height > 0 ? slavePtr->height : Tk_ReqHeight(slavePtr->tkwin) + doubleBw); if (pwPtr->orient == ORIENT_HORIZONTAL) { paneWidth = slavePtr->paneWidth; if (i == pwPtr->numSlaves - 1 && Tk_IsMapped(pwPtr->tkwin)) { if (Tk_Width(pwPtr->tkwin) != Tk_ReqWidth(pwPtr->tkwin)) { paneWidth += Tk_Width(pwPtr->tkwin) - Tk_ReqWidth(pwPtr->tkwin); if (paneWidth < 0) { paneWidth = 0; } } } paneHeight = Tk_Height(pwPtr->tkwin) - (2 * slavePtr->pady) - (2 * Tk_InternalBorderWidth(pwPtr->tkwin)); } else { paneHeight = slavePtr->paneHeight; if (i == pwPtr->numSlaves - 1 && Tk_IsMapped(pwPtr->tkwin)) { if (Tk_Height(pwPtr->tkwin) != Tk_ReqHeight(pwPtr->tkwin)) { paneHeight += Tk_Height(pwPtr->tkwin) - Tk_ReqHeight(pwPtr->tkwin); if (paneHeight < 0) { paneHeight = 0; } } } paneWidth = Tk_Width(pwPtr->tkwin) - (2 * slavePtr->padx) - (2 * Tk_InternalBorderWidth(pwPtr->tkwin)); } if (slaveWidth > paneWidth) { slaveWidth = paneWidth; } if (slaveHeight > paneHeight) { slaveHeight = paneHeight; } slaveX = slavePtr->x; slaveY = slavePtr->y; AdjustForSticky(slavePtr->sticky, paneWidth, paneHeight, &slaveX, &slaveY, &slaveWidth, &slaveHeight); slaveX += slavePtr->padx; slaveY += slavePtr->pady; /* * Now put the window in the proper spot. */ if ((slaveWidth <= 0) || (slaveHeight <= 0)) { Tk_UnmaintainGeometry(slavePtr->tkwin, pwPtr->tkwin); Tk_UnmapWindow(slavePtr->tkwin); } else { Tk_MaintainGeometry(slavePtr->tkwin, pwPtr->tkwin, slaveX, slaveY, slaveWidth, slaveHeight); } } Tcl_Release((ClientData) pwPtr); } /* *---------------------------------------------------------------------- * * Unlink -- * * Remove a slave from a paned window. * * Results: * None. * * Side effects: * The paned window will be scheduled for re-arranging and redrawing. * *---------------------------------------------------------------------- */ static void Unlink(slavePtr) register Slave *slavePtr; /* Window to unlink. */ { register PanedWindow *masterPtr; int i, j; masterPtr = slavePtr->masterPtr; if (masterPtr == NULL) { return; } /* * Find the specified slave in the panedwindow's list of slaves, then * remove it from that list. */ for (i = 0; i < masterPtr->numSlaves; i++) { if (masterPtr->slaves[i] == slavePtr) { for (j = i; j < masterPtr->numSlaves - 1; j++) { masterPtr->slaves[j] = masterPtr->slaves[j + 1]; } break; } } masterPtr->flags |= REQUESTED_RELAYOUT; if (!(masterPtr->flags & REDRAW_PENDING)) { masterPtr->flags |= REDRAW_PENDING; Tcl_DoWhenIdle(DisplayPanedWindow, (ClientData) masterPtr); } /* * Set the slave's masterPtr to NULL, so that we can tell that the * slave is no longer attached to any panedwindow. */ slavePtr->masterPtr = NULL; masterPtr->numSlaves--; } /* *---------------------------------------------------------------------- * * GetPane -- * * Given a token to a Tk window, find the pane that corresponds to * that token in a given paned window. * * Results: * Pointer to the slave structure, or NULL if the window is not * managed by this paned window. * * Side effects: * None. * *---------------------------------------------------------------------- */ static Slave * GetPane(pwPtr, tkwin) PanedWindow *pwPtr; /* Pointer to the paned window info. */ Tk_Window tkwin; /* Window to search for. */ { int i; for (i = 0; i < pwPtr->numSlaves; i++) { if (pwPtr->slaves[i]->tkwin == tkwin) { return pwPtr->slaves[i]; } } return NULL; } /* *-------------------------------------------------------------- * * SlaveStructureProc -- * * This procedure is invoked whenever StructureNotify events * occur for a window that's managed by a paned window. This * procedure's only purpose is to clean up when windows are * deleted. * * Results: * None. * * Side effects: * The paned window slave structure associated with the window * is freed, and the slave is disassociated from the paned * window which managed it. * *-------------------------------------------------------------- */ static void SlaveStructureProc(clientData, eventPtr) ClientData clientData; /* Pointer to record describing window item. */ XEvent *eventPtr; /* Describes what just happened. */ { Slave *slavePtr = (Slave *) clientData; PanedWindow *pwPtr = slavePtr->masterPtr; if (eventPtr->type == DestroyNotify) { Unlink(slavePtr); slavePtr->tkwin = NULL; ckfree((void *)slavePtr); ComputeGeometry(pwPtr); } } /* *---------------------------------------------------------------------- * * ComputeGeometry -- * * Compute geometry for the paned window, including coordinates of * all slave windows and each sash. * * Results: * None. * * Side effects: * Recomputes geometry information for a paned window. * *---------------------------------------------------------------------- */ static void ComputeGeometry(pwPtr) PanedWindow *pwPtr; /* Pointer to the Paned Window structure. */ { int i, x, y, doubleBw, internalBw; int reqWidth, reqHeight, sashWidth, sxOff, syOff, hxOff, hyOff, dim; Slave *slavePtr; pwPtr->flags |= REQUESTED_RELAYOUT; x = y = internalBw = Tk_InternalBorderWidth(pwPtr->tkwin); reqWidth = reqHeight = 0; /* * Sashes and handles share space on the display. To simplify * processing below, precompute the x and y offsets of the handles and * sashes within the space occupied by their combination; later, just add * those offsets blindly (avoiding the extra showHandle, etc, checks). */ sxOff = syOff = hxOff = hyOff = 0; if (pwPtr->showHandle && pwPtr->handleSize > pwPtr->sashWidth) { sashWidth = pwPtr->handleSize; if (pwPtr->orient == ORIENT_HORIZONTAL) { sxOff = (pwPtr->handleSize - pwPtr->sashWidth) / 2; hyOff = pwPtr->handlePad; } else { syOff = (pwPtr->handleSize - pwPtr->sashWidth) / 2; hxOff = pwPtr->handlePad; } } else { sashWidth = pwPtr->sashWidth; if (pwPtr->orient == ORIENT_HORIZONTAL) { hxOff = (pwPtr->handleSize - pwPtr->sashWidth) / 2; hyOff = pwPtr->handlePad; } else { hyOff = (pwPtr->handleSize - pwPtr->sashWidth) / 2; hxOff = pwPtr->handlePad; } } for (i = 0; i < pwPtr->numSlaves; i++) { slavePtr = pwPtr->slaves[i]; /* * First set the coordinates for the top left corner of the slave's * parcel. */ slavePtr->x = x; slavePtr->y = y; /* * Make sure the pane's paned dimension is at least minsize. * This check may be redundant, since the only way to change a pane's * size is by moving a sash, and that code checks the minsize. */ if (pwPtr->orient == ORIENT_HORIZONTAL) { if (slavePtr->paneWidth < slavePtr->minSize) { slavePtr->paneWidth = slavePtr->minSize; } } else { if (slavePtr->paneHeight < slavePtr->minSize) { slavePtr->paneHeight = slavePtr->minSize; } } /* * Compute the location of the sash at the right or bottom of the * parcel. */ if (pwPtr->orient == ORIENT_HORIZONTAL) { x += slavePtr->paneWidth + (2 * slavePtr->padx) + pwPtr->sashPad; } else { y += slavePtr->paneHeight + (2 * slavePtr->pady) + pwPtr->sashPad; } slavePtr->sashx = x + sxOff; slavePtr->sashy = y + syOff; slavePtr->handlex = x + hxOff; slavePtr->handley = y + hyOff; /* * Compute the location of the next parcel. */ if (pwPtr->orient == ORIENT_HORIZONTAL) { x += sashWidth + pwPtr->sashPad; } else { y += sashWidth + pwPtr->sashPad; } /* * Find the maximum height/width of the slaves, for computing the * requested height/width of the paned window. */ if (pwPtr->orient == ORIENT_HORIZONTAL) { /* * If the slave has an explicit height set, use that; otherwise, * use the slave's requested height. */ if (slavePtr->height > 0) { dim = slavePtr->height; } else { doubleBw = (2 * Tk_Changes(slavePtr->tkwin)->border_width); dim = Tk_ReqHeight(slavePtr->tkwin) + doubleBw; } dim += (2 * slavePtr->pady); if (dim > reqHeight) { reqHeight = dim; } } else { /* * If the slave has an explicit width set use that; otherwise, * use the slave's requested width. */ if (slavePtr->width > 0) { dim = slavePtr->width; } else { doubleBw = (2 * Tk_Changes(slavePtr->tkwin)->border_width); dim = Tk_ReqWidth(slavePtr->tkwin) + doubleBw; } dim += (2 * slavePtr->padx); if (dim > reqWidth) { reqWidth = dim; } } } /* * The loop above should have left x (or y) equal to the sum of the * widths (or heights) of the widgets, plus the size of one sash and * the sash padding for each widget, plus the width of the left (or top) * border of the paned window. * * The requested width (or height) is therefore x (or y) minus the size of * one sash and padding, plus the width of the right (or bottom) border * of the paned window. * * The height (or width) is equal to the maximum height (or width) of * the slaves, plus the width of the border of the top and bottom (or left * and right) of the paned window. */ if (pwPtr->orient == ORIENT_HORIZONTAL) { reqWidth = x - (sashWidth + (2 * pwPtr->sashPad)) + internalBw; reqHeight += 2 * internalBw; } else { reqHeight = y - (sashWidth + (2 * pwPtr->sashPad)) + internalBw; reqWidth += 2 * internalBw; } if (pwPtr->width <= 0 && pwPtr->height <= 0) { Tk_GeometryRequest(pwPtr->tkwin, reqWidth, reqHeight); } if (Tk_IsMapped(pwPtr->tkwin) && !(pwPtr->flags & REDRAW_PENDING)) { pwPtr->flags |= REDRAW_PENDING; Tcl_DoWhenIdle(DisplayPanedWindow, (ClientData) pwPtr); } } /* *---------------------------------------------------------------------- * * DestroyOptionTables -- * * This procedure is registered as an exit callback when the paned window * command is first called. It cleans up the OptionTables structure * allocated by that command. * * Results: * None. * * Side effects: * Frees memory. * *---------------------------------------------------------------------- */ static void DestroyOptionTables(clientData, interp) ClientData clientData; /* Pointer to the OptionTables struct */ Tcl_Interp *interp; /* Pointer to the calling interp */ { ckfree((char *)clientData); return; } /* *---------------------------------------------------------------------- * * GetSticky - * * Converts an internal boolean combination of "sticky" bits into a * a Tcl string obj containing zero or mor of n, s, e, or w. * * Results: * Tcl_Obj containing the string representation of the sticky value. * * Side effects: * Creates a new Tcl_Obj. * *---------------------------------------------------------------------- */ static Tcl_Obj * GetSticky(clientData, tkwin, recordPtr, internalOffset) ClientData clientData; Tk_Window tkwin; char *recordPtr; /* Pointer to widget record. */ int internalOffset; /* Offset within *recordPtr containing the * sticky value. */ { int sticky = *(int *)(recordPtr + internalOffset); static char buffer[5]; int count = 0; if (sticky & STICK_NORTH) { buffer[count++] = 'n'; } if (sticky & STICK_EAST) { buffer[count++] = 'e'; } if (sticky & STICK_SOUTH) { buffer[count++] = 's'; } if (sticky & STICK_WEST) { buffer[count++] = 'w'; } buffer[count] = '\0'; return Tcl_NewStringObj(buffer, -1); } /* *---------------------------------------------------------------------- * * SetSticky -- * * Converts a Tcl_Obj representing a widgets stickyness into an * integer value. * * Results: * Standard Tcl result. * * Side effects: * May store the integer value into the internal representation * pointer. May change the pointer to the Tcl_Obj to NULL to indicate * that the specified string was empty and that is acceptable. * *---------------------------------------------------------------------- */ static int SetSticky(clientData, interp, tkwin, value, recordPtr, internalOffset, oldInternalPtr, flags) ClientData clientData; Tcl_Interp *interp; /* Current interp; may be used for errors. */ Tk_Window tkwin; /* Window for which option is being set. */ Tcl_Obj **value; /* Pointer to the pointer to the value object. * We use a pointer to the pointer because * we may need to return a value (NULL). */ char *recordPtr; /* Pointer to storage for the widget record. */ int internalOffset; /* Offset within *recordPtr at which the internal value is to be stored. */ char *oldInternalPtr; /* Pointer to storage for the old value. */ int flags; /* Flags for the option, set Tk_SetOptions. */ { int sticky = 0; char c, *string, *internalPtr; internalPtr = ComputeSlotAddress(recordPtr, internalOffset); if (flags & TK_OPTION_NULL_OK && ObjectIsEmpty(*value)) { *value = NULL; } else { /* * Convert the sticky specifier into an integer value. */ string = Tcl_GetString(*value); while ((c = *string++) != '\0') { switch (c) { case 'n': case 'N': sticky |= STICK_NORTH; break; case 'e': case 'E': sticky |= STICK_EAST; break; case 's': case 'S': sticky |= STICK_SOUTH; break; case 'w': case 'W': sticky |= STICK_WEST; break; case ' ': case ',': case '\t': case '\r': case '\n': break; default: { Tcl_ResetResult(interp); Tcl_AppendResult(interp, "bad stickyness value \"", Tcl_GetString(*value), "\": must be a string ", "containing zero or more of n, e, s, and w", (char *)NULL); return TCL_ERROR; } } } } if (internalPtr != NULL) { *((int *) oldInternalPtr) = *((int *) internalPtr); *((int *) internalPtr) = sticky; } return TCL_OK; } /* *---------------------------------------------------------------------- * * RestoreSticky -- * * Restore a sticky option value from a saved value. * * Results: * None. * * Side effects: * Restores the old value. * *---------------------------------------------------------------------- */ static void RestoreSticky(clientData, tkwin, internalPtr, oldInternalPtr) ClientData clientData; Tk_Window tkwin; char *internalPtr; /* Pointer to storage for value. */ char *oldInternalPtr; /* Pointer to old value. */ { *(int *)internalPtr = *(int *)oldInternalPtr; } /* *---------------------------------------------------------------------- * * AdjustForSticky -- * * Given the x,y coords of the top-left corner of a pane, the * dimensions of that pane, and the dimensions of a slave, compute * the x,y coords and actual dimensions of the slave based on the slave's * sticky value. * * Results: * No direct return; sets the x, y, slaveWidth and slaveHeight to * correct values. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void AdjustForSticky(sticky, cavityWidth, cavityHeight, xPtr, yPtr, slaveWidthPtr, slaveHeightPtr) int sticky; /* Sticky value; see top of file for definition. */ int cavityWidth; /* Width of the cavity. */ int cavityHeight; /* Height of the cavity. */ int *xPtr, *yPtr; /* Initially, coordinates of the top-left * corner of cavity; also return values for * actual x, y coords of slave. */ int *slaveWidthPtr; /* Slave width. */ int *slaveHeightPtr; /* Slave height. */ { int diffx=0; /* Cavity width - slave width. */ int diffy=0; /* Cavity hight - slave height. */ if (cavityWidth > *slaveWidthPtr) { diffx = cavityWidth - *slaveWidthPtr; } if (cavityHeight > *slaveHeightPtr) { diffy = cavityHeight - *slaveHeightPtr; } if ((sticky & STICK_EAST) && (sticky & STICK_WEST)) { *slaveWidthPtr += diffx; } if ((sticky & STICK_NORTH) && (sticky & STICK_SOUTH)) { *slaveHeightPtr += diffy; } if (!(sticky & STICK_WEST)) { *xPtr += (sticky & STICK_EAST) ? diffx : diffx/2; } if (!(sticky & STICK_NORTH)) { *yPtr += (sticky & STICK_SOUTH) ? diffy : diffy/2; } } /* *---------------------------------------------------------------------- * * MoveSash -- * * Move the sash given by index the amount given. * * Results: * None. * * Side effects: * Recomputes the sizes of the panes in a panedwindow. * *---------------------------------------------------------------------- */ static void MoveSash(pwPtr, sash, diff) PanedWindow *pwPtr; int sash; int diff; { int diffConsumed = 0, i, extra, maxCoord, currCoord; int *lengthPtr, newLength; Slave *slave; if (diff > 0) { /* * Growing the pane, at the expense of panes to the right. */ /* * First check that moving the sash the requested distance will not * leave it off the screen. If necessary, clip the requested diff * to the maximum possible while remaining visible. */ if (pwPtr->orient == ORIENT_HORIZONTAL) { if (Tk_IsMapped(pwPtr->tkwin)) { maxCoord = Tk_Width(pwPtr->tkwin); } else { maxCoord = Tk_ReqWidth(pwPtr->tkwin); } extra = Tk_Width(pwPtr->tkwin) - Tk_ReqWidth(pwPtr->tkwin); currCoord = pwPtr->slaves[sash]->sashx; } else { if (Tk_IsMapped(pwPtr->tkwin)) { maxCoord = Tk_Height(pwPtr->tkwin); } else { maxCoord = Tk_ReqHeight(pwPtr->tkwin); } extra = Tk_Height(pwPtr->tkwin) - Tk_ReqHeight(pwPtr->tkwin); currCoord = pwPtr->slaves[sash]->sashy; } maxCoord -= (pwPtr->borderWidth + pwPtr->sashWidth + pwPtr->sashPad); if (currCoord + diff >= maxCoord) { diff = maxCoord - currCoord; } for (i = sash + 1; i < pwPtr->numSlaves; i++) { if (diffConsumed == diff) { break; } slave = pwPtr->slaves[i]; if (pwPtr->orient == ORIENT_HORIZONTAL) { lengthPtr = &(slave->paneWidth); } else { lengthPtr = &(slave->paneHeight); } /* * Remove as much space from this pane as possible (constrained * by the minsize value and the visible dimensions of the window). */ if (i == pwPtr->numSlaves - 1 && extra > 0) { /* * The last pane may have some additional "virtual" space, * if the width (or height) of the paned window is bigger * than the requested width (or height). * * That extra space is not included in the paneWidth * (or paneHeight) value, so we have to handle the last * pane specially. */ newLength = (*lengthPtr + extra) - (diff - diffConsumed); if (newLength < slave->minSize) { newLength = slave->minSize; } if (newLength < 0) { newLength = 0; } diffConsumed += (*lengthPtr + extra) - newLength; if (newLength < *lengthPtr) { *lengthPtr = newLength; } } else { newLength = *lengthPtr - (diff - diffConsumed); if (newLength < slave->minSize) { newLength = slave->minSize; } if (newLength < 0) { newLength = 0; } diffConsumed += *lengthPtr - newLength; *lengthPtr = newLength; } } if (pwPtr->orient == ORIENT_HORIZONTAL) { pwPtr->slaves[sash]->paneWidth += diffConsumed; } else { pwPtr->slaves[sash]->paneHeight += diffConsumed; } } else if (diff < 0) { /* * Shrinking the pane; additional space is given to the pane to the * right. */ for (i = sash; i >= 0; i--) { if (diffConsumed == diff) { break; } /* * Remove as much space from this pane as possible. */ slave = pwPtr->slaves[i]; if (pwPtr->orient == ORIENT_HORIZONTAL) { lengthPtr = &(slave->paneWidth); } else { lengthPtr = &(slave->paneHeight); } newLength = *lengthPtr + (diff - diffConsumed); if (newLength < slave->minSize) { newLength = slave->minSize; } if (newLength < 0) { newLength = 0; } diffConsumed -= *lengthPtr - newLength; *lengthPtr = newLength; } if (pwPtr->orient == ORIENT_HORIZONTAL) { pwPtr->slaves[sash + 1]->paneWidth -= diffConsumed; } else { pwPtr->slaves[sash + 1]->paneHeight -= diffConsumed; } } } /* *---------------------------------------------------------------------- * * ProxyWindowEventProc -- * * This procedure is invoked by the Tk dispatcher for various * events on paned window proxy windows. * * Results: * None. * * Side effects: * When the window gets deleted, internal structures get * cleaned up. When it gets exposed, it is redisplayed. * *-------------------------------------------------------------- */ static void ProxyWindowEventProc(clientData, eventPtr) ClientData clientData; /* Information about window. */ XEvent *eventPtr; /* Information about event. */ { PanedWindow *pwPtr = (PanedWindow *) clientData; if (eventPtr->type == Expose) { if (pwPtr->proxywin != NULL &&!(pwPtr->flags & PROXY_REDRAW_PENDING)) { Tcl_DoWhenIdle(DisplayProxyWindow, (ClientData) pwPtr); pwPtr->flags |= PROXY_REDRAW_PENDING; } } } /* *-------------------------------------------------------------- * * DisplayProxyWindow -- * * This procedure redraws a paned window proxy window. * It is invoked as a do-when-idle handler, so it only runs * when there's nothing else for the application to do. * * Results: * None. * * Side effects: * Information appears on the screen. * *-------------------------------------------------------------- */ static void DisplayProxyWindow(clientData) ClientData clientData; /* Information about window. */ { PanedWindow *pwPtr = (PanedWindow *) clientData; Pixmap pixmap; Tk_Window tkwin = pwPtr->proxywin; pwPtr->flags &= ~PROXY_REDRAW_PENDING; if ((tkwin == NULL) || !Tk_IsMapped(tkwin)) { return; } /* * Create a pixmap for double-buffering, if necessary. */ pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); /* * Redraw the widget's background and border. */ Tk_Fill3DRectangle(tkwin, pixmap, pwPtr->background, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 2, pwPtr->sashRelief); /* * Copy the pixmap to the display. */ XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin), pwPtr->gc, 0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin), 0, 0); Tk_FreePixmap(Tk_Display(tkwin), pixmap); } /* *---------------------------------------------------------------------- * * PanedWindowProxyCommand -- * * Handles the panedwindow proxy subcommand. See the user * documentation for details. * * Results: * Standard Tcl result. * * Side effects: * May map or unmap the proxy sash. * *---------------------------------------------------------------------- */ static int PanedWindowProxyCommand(pwPtr, interp, objc, objv) PanedWindow *pwPtr; /* Pointer to paned window information. */ Tcl_Interp *interp; /* Current interpreter. */ int objc; /* Number of arguments. */ Tcl_Obj * CONST objv[]; /* Argument objects. */ { static CONST char *optionStrings[] = { "coord", "forget", "place", (char *) NULL }; enum options { PROXY_COORD, PROXY_FORGET, PROXY_PLACE }; int index, x, y, sashWidth, sashHeight; Tcl_Obj *coords[2]; if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "option ?arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[2], optionStrings, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } switch ((enum options) index) { case PROXY_COORD: if (objc != 3) { Tcl_WrongNumArgs(interp, 3, objv, NULL); return TCL_ERROR; } coords[0] = Tcl_NewIntObj(pwPtr->proxyx); coords[1] = Tcl_NewIntObj(pwPtr->proxyy); Tcl_SetListObj(Tcl_GetObjResult(interp), 2, coords); break; case PROXY_FORGET: if (objc != 3) { Tcl_WrongNumArgs(interp, 3, objv, NULL); return TCL_ERROR; } if (Tk_IsMapped(pwPtr->proxywin)) { Tk_UnmapWindow(pwPtr->proxywin); Tk_UnmaintainGeometry(pwPtr->proxywin, pwPtr->tkwin); } break; case PROXY_PLACE: { if (objc != 5) { Tcl_WrongNumArgs(interp, 3, objv, "x y"); return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) { return TCL_ERROR; } if (pwPtr->orient == ORIENT_HORIZONTAL) { if (x < 0) { x = 0; } y = Tk_InternalBorderWidth(pwPtr->tkwin); sashWidth = pwPtr->sashWidth; sashHeight = Tk_Height(pwPtr->tkwin) - (2 * Tk_InternalBorderWidth(pwPtr->tkwin)); } else { if (y < 0) { y = 0; } x = Tk_InternalBorderWidth(pwPtr->tkwin); sashHeight = pwPtr->sashWidth; sashWidth = Tk_Width(pwPtr->tkwin) - (2 * Tk_InternalBorderWidth(pwPtr->tkwin)); } /* * Stash the proxy coordinates for future "proxy coord" calls. */ pwPtr->proxyx = x; pwPtr->proxyy = y; /* * Make sure the proxy window is higher in the stacking order * than the slaves, so that it will be visible when drawn. * It would be more correct to push the proxy window just high * enough to appear above the highest slave, but it's much easier * to just force it all the way to the top of the stacking order. */ Tk_RestackWindow(pwPtr->proxywin, Above, NULL); /* * Let Tk_MaintainGeometry take care of placing the window at * the right coordinates. */ Tk_MaintainGeometry(pwPtr->proxywin, pwPtr->tkwin, x, y, sashWidth, sashHeight); break; } } return TCL_OK; } /* *---------------------------------------------------------------------- * * ObjectIsEmpty -- * * This procedure tests whether the string value of an object is * empty. * * Results: * The return value is 1 if the string value of objPtr has length * zero, and 0 otherwise. * * Side effects: * May cause object shimmering, since this function can force a * conversion to a string object. * *---------------------------------------------------------------------- */ static int ObjectIsEmpty(objPtr) Tcl_Obj *objPtr; /* Object to test. May be NULL. */ { int length; if (objPtr == NULL) { return 1; } if (objPtr->bytes != NULL) { return (objPtr->length == 0); } Tcl_GetStringFromObj(objPtr, &length); return (length == 0); } /* *---------------------------------------------------------------------- * * ComputeInternalPointer -- * * Given a pointer to the start of a record and the offset of a slot * within that record, compute the address of that slot. * * Results: * If offset is non-negative, returns the computed address; else, * returns NULL. * * Side effects: * None. * *---------------------------------------------------------------------- */ static char * ComputeSlotAddress(recordPtr, offset) char *recordPtr; /* Pointer to the start of a record. */ int offset; /* Offset of a slot within that record; may be < 0. */ { if (offset >= 0) { return recordPtr + offset; } else { return NULL; } } /* *---------------------------------------------------------------------- * * PanedWindowIdentifyCoords -- * * Given a pair of x,y coordinates, identify the panedwindow component * at that point, if any. * * Results: * Standard Tcl result. * * Side effects: * Modifies the interpreter's result to contain either an empty list, * or a two element list of the form {sash n} or {handle n} to indicate * that the point lies within the n'th sash or handle. * *---------------------------------------------------------------------- */ static int PanedWindowIdentifyCoords(pwPtr, interp, x, y) PanedWindow *pwPtr; /* Information about the widget. */ Tcl_Interp *interp; /* Interpreter in which to store result. */ int x, y; /* Coordinates of the point to identify. */ { Tcl_Obj *list; int i, sashHeight, sashWidth, thisx, thisy; int found, isHandle, lpad, rpad, tpad, bpad; list = Tcl_NewObj(); if (pwPtr->orient == ORIENT_HORIZONTAL) { if (Tk_IsMapped(pwPtr->tkwin)) { sashHeight = Tk_Height(pwPtr->tkwin); } else { sashHeight = Tk_ReqHeight(pwPtr->tkwin); } sashHeight -= 2 * Tk_InternalBorderWidth(pwPtr->tkwin); if (pwPtr->showHandle && pwPtr->handleSize > pwPtr->sashWidth) { sashWidth = pwPtr->handleSize; lpad = (pwPtr->handleSize - pwPtr->sashWidth) / 2; rpad = pwPtr->handleSize - lpad; lpad += pwPtr->sashPad; rpad += pwPtr->sashPad; } else { sashWidth = pwPtr->sashWidth; lpad = rpad = pwPtr->sashPad; } tpad = bpad = 0; } else { if (pwPtr->showHandle && pwPtr->handleSize > pwPtr->sashWidth) { sashHeight = pwPtr->handleSize; tpad = (pwPtr->handleSize - pwPtr->sashWidth) / 2; bpad = pwPtr->handleSize - tpad; tpad += pwPtr->sashPad; bpad += pwPtr->sashPad; } else { sashHeight = pwPtr->sashWidth; tpad = bpad = pwPtr->sashPad; } if (Tk_IsMapped(pwPtr->tkwin)) { sashWidth = Tk_Width(pwPtr->tkwin); } else { sashWidth = Tk_ReqWidth(pwPtr->tkwin); } sashWidth -= 2 * Tk_InternalBorderWidth(pwPtr->tkwin); lpad = rpad = 0; } isHandle = 0; found = -1; for (i = 0; i < pwPtr->numSlaves - 1; i++) { thisx = pwPtr->slaves[i]->sashx; thisy = pwPtr->slaves[i]->sashy; if (((thisx - lpad) <= x && x <= (thisx + rpad + sashWidth)) && ((thisy - tpad) <= y && y <= (thisy + bpad + sashHeight))) { found = i; /* * Determine if the point is over the handle or the sash. */ if (pwPtr->showHandle) { thisx = pwPtr->slaves[i]->handlex; thisy = pwPtr->slaves[i]->handley; if (pwPtr->orient == ORIENT_HORIZONTAL) { if (thisy <= y && y <= (thisy + pwPtr->handleSize)) { isHandle = 1; } } else { if (thisx <= x && x <= (thisx + pwPtr->handleSize)) { isHandle = 1; } } } break; } } /* * Set results. */ if (found != -1) { Tcl_ListObjAppendElement(interp, list, Tcl_NewIntObj(found)); if (isHandle) { Tcl_ListObjAppendElement(interp, list, Tcl_NewStringObj("handle", -1)); } else { Tcl_ListObjAppendElement(interp, list, Tcl_NewStringObj("sash", -1)); } } Tcl_SetObjResult(interp, list); return TCL_OK; }