3157 lines
86 KiB
C
3157 lines
86 KiB
C
/*
|
||
* 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.
|
||
*/
|
||
|
||
#include "tkInt.h"
|
||
#include "default.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 const char *const orientStrings[] = {
|
||
"horizontal", "vertical", NULL
|
||
};
|
||
|
||
enum orient { ORIENT_HORIZONTAL, ORIENT_VERTICAL };
|
||
|
||
/*
|
||
* The following table defines the legal values for the -stretch option.
|
||
*/
|
||
|
||
static const char *const stretchStrings[] = {
|
||
"always", "first", "last", "middle", "never", NULL
|
||
};
|
||
|
||
enum stretch {
|
||
STRETCH_ALWAYS, /* Always give extra space to this pane. */
|
||
STRETCH_FIRST, /* Give extra space to pane if it is first. */
|
||
STRETCH_LAST, /* Give extra space to pane if it is last. */
|
||
STRETCH_MIDDLE, /* Give extra space to pane only if it is
|
||
* neither first nor last. */
|
||
STRETCH_NEVER /* Never give extra space to this pane. */
|
||
};
|
||
|
||
/*
|
||
* Codify the stretchiness rule in one place.
|
||
*/
|
||
|
||
#define IsStretchable(stretch,index,first,last) \
|
||
(((stretch) == STRETCH_ALWAYS) || \
|
||
((stretch) == STRETCH_FIRST && (index) == (first)) || \
|
||
((stretch) == STRETCH_LAST && (index) == (last)) || \
|
||
((stretch) == STRETCH_MIDDLE && (index) != (first) && (index) != (last)))
|
||
|
||
typedef struct {
|
||
Tk_OptionTable pwOptions; /* Token for paned window option table. */
|
||
Tk_OptionTable paneOpts; /* Token for pane cget option table. */
|
||
} OptionTables;
|
||
|
||
/*
|
||
* One structure of the following type is kept for each window
|
||
* managed by a paned window widget.
|
||
*/
|
||
|
||
typedef struct Pane {
|
||
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 pane, in
|
||
* the x dimension. */
|
||
int pady; /* Additional padding requested for pane, in
|
||
* the y dimension. */
|
||
Tcl_Obj *widthPtr, *heightPtr;
|
||
/* Tcl_Obj rep's of pane width/height, to
|
||
* allow for null values. */
|
||
int width; /* Pane width. */
|
||
int height; /* Pane height. */
|
||
int sticky; /* Sticky string. */
|
||
int x, y; /* Coordinates of the widget. */
|
||
int paneWidth, paneHeight; /* Pane dimensions (may be different from
|
||
* pane 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. */
|
||
enum stretch stretch; /* Controls how pane grows/shrinks */
|
||
int hide; /* Controls visibility of pane */
|
||
struct PanedWindow *containerPtr;
|
||
/* Paned window managing the window. */
|
||
Tk_Window after; /* Placeholder for parsing options. */
|
||
Tk_Window before; /* Placeholder for parsing options. */
|
||
} Pane;
|
||
|
||
/*
|
||
* 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 paneOpts; /* Token for pane 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. */
|
||
Tk_3DBorder proxyBackground;/* Background color used to draw proxy. If NULL, use background. */
|
||
Tcl_Obj *proxyBorderWidthPtr; /* Tcl_Obj rep for proxyBorderWidth */
|
||
int proxyBorderWidth; /* Borderwidth used to draw proxy. */
|
||
int proxyRelief; /* Relief used to draw proxy, if TK_RELIEF_NULL then use relief. */
|
||
Pane **panes; /* Pointer to array of Panes. */
|
||
int numPanes; /* Number of panes. */
|
||
int sizeofPanes; /* Number of elements in the panes 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 functions defined later in this file:
|
||
*/
|
||
|
||
int Tk_PanedWindowObjCmd(ClientData clientData,
|
||
Tcl_Interp *interp, int objc,
|
||
Tcl_Obj *const objv[]);
|
||
static void PanedWindowCmdDeletedProc(ClientData clientData);
|
||
static int ConfigurePanedWindow(Tcl_Interp *interp,
|
||
PanedWindow *pwPtr, int objc,
|
||
Tcl_Obj *const objv[]);
|
||
static void DestroyPanedWindow(PanedWindow *pwPtr);
|
||
static void DisplayPanedWindow(ClientData clientData);
|
||
static void PanedWindowEventProc(ClientData clientData,
|
||
XEvent *eventPtr);
|
||
static void ProxyWindowEventProc(ClientData clientData,
|
||
XEvent *eventPtr);
|
||
static void DisplayProxyWindow(ClientData clientData);
|
||
static void PanedWindowWorldChanged(ClientData instanceData);
|
||
static int PanedWindowWidgetObjCmd(ClientData clientData,
|
||
Tcl_Interp *, int objc, Tcl_Obj * const objv[]);
|
||
static void PanedWindowLostPaneProc(ClientData clientData,
|
||
Tk_Window tkwin);
|
||
static void PanedWindowReqProc(ClientData clientData,
|
||
Tk_Window tkwin);
|
||
static void ArrangePanes(ClientData clientData);
|
||
static void Unlink(Pane *panePtr);
|
||
static Pane * GetPane(PanedWindow *pwPtr, Tk_Window tkwin);
|
||
static void GetFirstLastVisiblePane(PanedWindow *pwPtr,
|
||
int *firstPtr, int *lastPtr);
|
||
static void PaneStructureProc(ClientData clientData,
|
||
XEvent *eventPtr);
|
||
static int PanedWindowSashCommand(PanedWindow *pwPtr,
|
||
Tcl_Interp *interp, int objc,
|
||
Tcl_Obj * const objv[]);
|
||
static int PanedWindowProxyCommand(PanedWindow *pwPtr,
|
||
Tcl_Interp *interp, int objc,
|
||
Tcl_Obj * const objv[]);
|
||
static void ComputeGeometry(PanedWindow *pwPtr);
|
||
static int ConfigurePanes(PanedWindow *pwPtr,
|
||
Tcl_Interp *interp, int objc,
|
||
Tcl_Obj * const objv[]);
|
||
static void DestroyOptionTables(ClientData clientData,
|
||
Tcl_Interp *interp);
|
||
static int SetSticky(ClientData clientData, Tcl_Interp *interp,
|
||
Tk_Window tkwin, Tcl_Obj **value, char *recordPtr,
|
||
int internalOffset, char *oldInternalPtr,
|
||
int flags);
|
||
static Tcl_Obj * GetSticky(ClientData clientData, Tk_Window tkwin,
|
||
char *recordPtr, int internalOffset);
|
||
static void RestoreSticky(ClientData clientData, Tk_Window tkwin,
|
||
char *internalPtr, char *oldInternalPtr);
|
||
static void AdjustForSticky(int sticky, int cavityWidth,
|
||
int cavityHeight, int *xPtr, int *yPtr,
|
||
int *paneWidthPtr, int *paneHeightPtr);
|
||
static void MoveSash(PanedWindow *pwPtr, int sash, int diff);
|
||
static int ObjectIsEmpty(Tcl_Obj *objPtr);
|
||
static char * ComputeSlotAddress(char *recordPtr, int offset);
|
||
static int PanedWindowIdentifyCoords(PanedWindow *pwPtr,
|
||
Tcl_Interp *interp, int x, int y);
|
||
|
||
/*
|
||
* Sashes are between panes only, so there is one less sash than panes
|
||
*/
|
||
|
||
#define ValidSashIndex(pwPtr, sash) \
|
||
(((sash) >= 0) && ((sash) < ((pwPtr)->numPanes-1)))
|
||
|
||
static const Tk_GeomMgr panedWindowMgrType = {
|
||
"panedwindow", /* name */
|
||
PanedWindowReqProc, /* requestProc */
|
||
PanedWindowLostPaneProc, /* lostPaneProc */
|
||
};
|
||
|
||
/*
|
||
* Information used for objv parsing.
|
||
*/
|
||
|
||
#define GEOMETRY 0x0001
|
||
|
||
/*
|
||
* The following structure contains pointers to functions used for processing
|
||
* the custom "-sticky" option for panes.
|
||
*/
|
||
|
||
static const Tk_ObjCustomOption stickyOption = {
|
||
"sticky", /* name */
|
||
SetSticky, /* setProc */
|
||
GetSticky, /* getProc */
|
||
RestoreSticky, /* restoreProc */
|
||
NULL, /* freeProc */
|
||
0
|
||
};
|
||
|
||
static const Tk_OptionSpec optionSpecs[] = {
|
||
{TK_OPTION_BORDER, "-background", "background", "Background",
|
||
DEF_PANEDWINDOW_BG_COLOR, -1, Tk_Offset(PanedWindow, background), 0,
|
||
DEF_PANEDWINDOW_BG_MONO, 0},
|
||
{TK_OPTION_SYNONYM, "-bd", NULL, NULL,
|
||
NULL, 0, -1, 0, "-borderwidth", 0},
|
||
{TK_OPTION_SYNONYM, "-bg", NULL, NULL,
|
||
NULL, 0, -1, 0, "-background", 0},
|
||
{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, GEOMETRY},
|
||
{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, orientStrings, GEOMETRY},
|
||
{TK_OPTION_BORDER, "-proxybackground", "proxyBackground", "ProxyBackground",
|
||
0, -1, Tk_Offset(PanedWindow, proxyBackground), TK_OPTION_NULL_OK,
|
||
(ClientData) DEF_PANEDWINDOW_BG_MONO, 0},
|
||
{TK_OPTION_PIXELS, "-proxyborderwidth", "proxyBorderWidth", "ProxyBorderWidth",
|
||
DEF_PANEDWINDOW_PROXYBORDER, Tk_Offset(PanedWindow, proxyBorderWidthPtr),
|
||
Tk_Offset(PanedWindow, proxyBorderWidth), 0, 0, GEOMETRY},
|
||
{TK_OPTION_RELIEF, "-proxyrelief", "proxyRelief", "Relief",
|
||
0, -1, Tk_Offset(PanedWindow, proxyRelief),
|
||
TK_OPTION_NULL_OK, 0, 0},
|
||
{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, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0}
|
||
};
|
||
|
||
static const Tk_OptionSpec paneOptionSpecs[] = {
|
||
{TK_OPTION_WINDOW, "-after", NULL, NULL,
|
||
DEF_PANEDWINDOW_PANE_AFTER, -1, Tk_Offset(Pane, after),
|
||
TK_OPTION_NULL_OK, 0, 0},
|
||
{TK_OPTION_WINDOW, "-before", NULL, NULL,
|
||
DEF_PANEDWINDOW_PANE_BEFORE, -1, Tk_Offset(Pane, before),
|
||
TK_OPTION_NULL_OK, 0, 0},
|
||
{TK_OPTION_PIXELS, "-height", NULL, NULL,
|
||
DEF_PANEDWINDOW_PANE_HEIGHT, Tk_Offset(Pane, heightPtr),
|
||
Tk_Offset(Pane, height), TK_OPTION_NULL_OK, 0, 0},
|
||
{TK_OPTION_BOOLEAN, "-hide", "hide", "Hide",
|
||
DEF_PANEDWINDOW_PANE_HIDE, -1, Tk_Offset(Pane, hide), 0,0,GEOMETRY},
|
||
{TK_OPTION_PIXELS, "-minsize", NULL, NULL,
|
||
DEF_PANEDWINDOW_PANE_MINSIZE, -1, Tk_Offset(Pane, minSize), 0, 0, 0},
|
||
{TK_OPTION_PIXELS, "-padx", NULL, NULL,
|
||
DEF_PANEDWINDOW_PANE_PADX, -1, Tk_Offset(Pane, padx), 0, 0, 0},
|
||
{TK_OPTION_PIXELS, "-pady", NULL, NULL,
|
||
DEF_PANEDWINDOW_PANE_PADY, -1, Tk_Offset(Pane, pady), 0, 0, 0},
|
||
{TK_OPTION_CUSTOM, "-sticky", NULL, NULL,
|
||
DEF_PANEDWINDOW_PANE_STICKY, -1, Tk_Offset(Pane, sticky), 0,
|
||
&stickyOption, 0},
|
||
{TK_OPTION_STRING_TABLE, "-stretch", "stretch", "Stretch",
|
||
DEF_PANEDWINDOW_PANE_STRETCH, -1, Tk_Offset(Pane, stretch), 0,
|
||
(ClientData) stretchStrings, 0},
|
||
{TK_OPTION_PIXELS, "-width", NULL, NULL,
|
||
DEF_PANEDWINDOW_PANE_WIDTH, Tk_Offset(Pane, widthPtr),
|
||
Tk_Offset(Pane, width), TK_OPTION_NULL_OK, 0, 0},
|
||
{TK_OPTION_END, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0}
|
||
};
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* Tk_PanedWindowObjCmd --
|
||
*
|
||
* This function 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(
|
||
TCL_UNUSED(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;
|
||
XSetWindowAttributes atts;
|
||
|
||
if (objc < 2) {
|
||
Tcl_WrongNumArgs(interp, 1, objv, "pathName ?-option value ...?");
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp),
|
||
Tcl_GetString(objv[1]), NULL);
|
||
if (tkwin == NULL) {
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
pwOpts = (OptionTables *)
|
||
Tcl_GetAssocData(interp, "PanedWindowOptionTables", NULL);
|
||
if (pwOpts == NULL) {
|
||
/*
|
||
* The first time this function 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 = ckalloc(sizeof(OptionTables));
|
||
|
||
/*
|
||
* Set up an exit handler to free the optionTables struct.
|
||
*/
|
||
|
||
Tcl_SetAssocData(interp, "PanedWindowOptionTables",
|
||
DestroyOptionTables, pwOpts);
|
||
|
||
/*
|
||
* Create the paned window option tables.
|
||
*/
|
||
|
||
pwOpts->pwOptions = Tk_CreateOptionTable(interp, optionSpecs);
|
||
pwOpts->paneOpts = Tk_CreateOptionTable(interp, paneOptionSpecs);
|
||
}
|
||
|
||
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, pwPtr,
|
||
PanedWindowCmdDeletedProc);
|
||
pwPtr->optionTable = pwOpts->pwOptions;
|
||
pwPtr->paneOpts = pwOpts->paneOpts;
|
||
pwPtr->relief = TK_RELIEF_RAISED;
|
||
pwPtr->gc = NULL;
|
||
pwPtr->cursor = NULL;
|
||
pwPtr->sashCursor = NULL;
|
||
|
||
/*
|
||
* Keep a hold of the associated tkwin until we destroy the widget,
|
||
* otherwise Tk might free it while we still need it.
|
||
*/
|
||
|
||
Tcl_Preserve(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, 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
|
||
* panes 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, NULL);
|
||
|
||
/*
|
||
* The proxy window has to be able to share GCs with the main panedwindow
|
||
* despite being children of windows with potentially different
|
||
* characteristics, and it looks better that way too. [Bug 702230] Also
|
||
* set the X window save under attribute to avoid expose events as the
|
||
* proxy sash is dragged across the panes. [Bug 1036963]
|
||
*/
|
||
|
||
Tk_SetWindowVisual(pwPtr->proxywin,
|
||
Tk_Visual(tkwin), Tk_Depth(tkwin), Tk_Colormap(tkwin));
|
||
Tk_CreateEventHandler(pwPtr->proxywin, ExposureMask, ProxyWindowEventProc,
|
||
pwPtr);
|
||
atts.save_under = True;
|
||
Tk_ChangeWindowAttributes(pwPtr->proxywin, CWSaveUnder, &atts);
|
||
|
||
if (ConfigurePanedWindow(interp, pwPtr, objc - 2, objv + 2) != TCL_OK) {
|
||
Tk_DestroyWindow(pwPtr->proxywin);
|
||
Tk_DestroyWindow(pwPtr->tkwin);
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
Tcl_SetObjResult(interp, TkNewWindowObj(pwPtr->tkwin));
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* PanedWindowWidgetObjCmd --
|
||
*
|
||
* This function 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 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 *const optionStrings[] = {
|
||
"add", "cget", "configure", "forget", "identify", "panecget",
|
||
"paneconfigure", "panes", "proxy", "sash", 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;
|
||
Pane *panePtr;
|
||
|
||
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(pwPtr);
|
||
|
||
switch ((enum options) index) {
|
||
case PW_ADD:
|
||
if (objc < 3) {
|
||
Tcl_WrongNumArgs(interp, 2, objv, "widget ?widget ...?");
|
||
result = TCL_ERROR;
|
||
break;
|
||
}
|
||
result = ConfigurePanes(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] : 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: {
|
||
|
||
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++) {
|
||
Tk_Window pane = Tk_NameToWindow(interp, Tcl_GetString(objv[i]),
|
||
pwPtr->tkwin);
|
||
|
||
if (pane == NULL) {
|
||
continue;
|
||
}
|
||
panePtr = GetPane(pwPtr, pane);
|
||
if ((panePtr != NULL) && (panePtr->containerPtr != NULL)) {
|
||
count++;
|
||
Tk_ManageGeometry(pane, NULL, NULL);
|
||
Tk_UnmaintainGeometry(panePtr->tkwin, pwPtr->tkwin);
|
||
Tk_DeleteEventHandler(panePtr->tkwin, StructureNotifyMask,
|
||
PaneStructureProc, panePtr);
|
||
Tk_UnmapWindow(panePtr->tkwin);
|
||
Unlink(panePtr);
|
||
}
|
||
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->numPanes; i++) {
|
||
if (pwPtr->panes[i]->tkwin == tkwin) {
|
||
resultObj = Tk_GetOptionValue(interp,
|
||
(char *) pwPtr->panes[i], pwPtr->paneOpts,
|
||
objv[3], tkwin);
|
||
}
|
||
}
|
||
if (resultObj == NULL) {
|
||
if (i == pwPtr->numPanes) {
|
||
Tcl_SetObjResult(interp, Tcl_NewStringObj(
|
||
"not managed by this window", -1));
|
||
Tcl_SetErrorCode(interp, "TK", "PANEDWINDOW", "UNMANAGED",
|
||
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);
|
||
if (tkwin == NULL) {
|
||
/*
|
||
* Just a plain old bad window; Tk_NameToWindow filled in an
|
||
* error message for us.
|
||
*/
|
||
|
||
result = TCL_ERROR;
|
||
break;
|
||
}
|
||
for (i = 0; i < pwPtr->numPanes; i++) {
|
||
if (pwPtr->panes[i]->tkwin == tkwin) {
|
||
resultObj = Tk_GetOptionInfo(interp,
|
||
(char *) pwPtr->panes[i], pwPtr->paneOpts,
|
||
(objc == 4) ? objv[3] : NULL,
|
||
pwPtr->tkwin);
|
||
if (resultObj == NULL) {
|
||
result = TCL_ERROR;
|
||
} else {
|
||
Tcl_SetObjResult(interp, resultObj);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
result = ConfigurePanes(pwPtr, interp, objc, objv);
|
||
}
|
||
break;
|
||
|
||
case PW_PANES:
|
||
resultObj = Tcl_NewObj();
|
||
for (i = 0; i < pwPtr->numPanes; i++) {
|
||
Tcl_ListObjAppendElement(NULL, resultObj,
|
||
TkNewWindowObj(pwPtr->panes[i]->tkwin));
|
||
}
|
||
Tcl_SetObjResult(interp, resultObj);
|
||
break;
|
||
|
||
case PW_PROXY:
|
||
result = PanedWindowProxyCommand(pwPtr, interp, objc, objv);
|
||
break;
|
||
|
||
case PW_SASH:
|
||
result = PanedWindowSashCommand(pwPtr, interp, objc, objv);
|
||
break;
|
||
}
|
||
Tcl_Release(pwPtr);
|
||
return result;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* ConfigurePanes --
|
||
*
|
||
* Add or alter the configuration options of a pane in a paned window.
|
||
*
|
||
* Results:
|
||
* Standard Tcl result.
|
||
*
|
||
* Side effects:
|
||
* Depends on options; may add a pane to the paned window, may alter the
|
||
* geometry management options of a pane.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static int
|
||
ConfigurePanes(
|
||
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, numNewPanes, haveLoc;
|
||
int insertIndex;
|
||
Tk_Window tkwin = NULL, ancestor, parent;
|
||
Pane *panePtr, **inserts, **newPanes;
|
||
Pane options;
|
||
const char *arg;
|
||
|
||
/*
|
||
* Find the non-window name arguments; these are the configure options for
|
||
* the panes. 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_SetObjResult(interp, Tcl_ObjPrintf(
|
||
"can't add %s to itself", arg));
|
||
Tcl_SetErrorCode(interp, "TK", "GEOMETRY", "SELF", NULL);
|
||
return TCL_ERROR;
|
||
} else if (Tk_IsTopLevel(tkwin)) {
|
||
/*
|
||
* A panedwindow cannot manage a toplevel.
|
||
*/
|
||
|
||
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
|
||
"can't add toplevel %s to %s", arg,
|
||
Tk_PathName(pwPtr->tkwin)));
|
||
Tcl_SetErrorCode(interp, "TK", "GEOMETRY", "TOPLEVEL", NULL);
|
||
return TCL_ERROR;
|
||
} else {
|
||
/*
|
||
* Make sure the panedwindow is the parent of the pane,
|
||
* or a descendant of the pane's parent.
|
||
*/
|
||
|
||
parent = Tk_Parent(tkwin);
|
||
for (ancestor = pwPtr->tkwin;;ancestor = Tk_Parent(ancestor)) {
|
||
if (ancestor == parent) {
|
||
break;
|
||
}
|
||
if (Tk_IsTopLevel(ancestor)) {
|
||
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
|
||
"can't add %s to %s", arg,
|
||
Tk_PathName(pwPtr->tkwin)));
|
||
Tcl_SetErrorCode(interp, "TK", "GEOMETRY",
|
||
"HIERARCHY", 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(Pane));
|
||
if (Tk_SetOptions(interp, (char *) &options, pwPtr->paneOpts,
|
||
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 != NULL) {
|
||
tkwin = options.after;
|
||
haveLoc = 1;
|
||
for (i = 0; i < pwPtr->numPanes; i++) {
|
||
if (options.after == pwPtr->panes[i]->tkwin) {
|
||
index = i + 1;
|
||
break;
|
||
}
|
||
}
|
||
} else if (options.before != NULL) {
|
||
tkwin = options.before;
|
||
haveLoc = 1;
|
||
for (i = 0; i < pwPtr->numPanes; i++) {
|
||
if (options.before == pwPtr->panes[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_SetObjResult(interp, Tcl_ObjPrintf(
|
||
"window \"%s\" is not managed by %s",
|
||
Tk_PathName(tkwin), Tk_PathName(pwPtr->tkwin)));
|
||
Tcl_SetErrorCode(interp, "TK", "PANEDWINDOW", "UNMANAGED", NULL);
|
||
Tk_FreeConfigOptions((char *) &options, pwPtr->paneOpts,
|
||
pwPtr->tkwin);
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
/*
|
||
* Allocate an array to hold, in order, the pointers to the pane
|
||
* structures corresponding to the windows specified. Some of those
|
||
* structures may already have existed, some may be new.
|
||
*/
|
||
|
||
inserts = (Pane **)ckalloc(sizeof(Pane *) * (firstOptionArg - 2));
|
||
insertIndex = 0;
|
||
|
||
/*
|
||
* Populate the inserts array, creating new pane structures as necessary,
|
||
* applying the options to each structure as we go, and, if necessary,
|
||
* marking the spot in the original panes array as empty (for
|
||
* pre-existing pane structures).
|
||
*/
|
||
|
||
for (i = 0, numNewPanes = 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->numPanes; j++) {
|
||
if (pwPtr->panes[j] != NULL && pwPtr->panes[j]->tkwin == tkwin) {
|
||
Tk_SetOptions(interp, (char *) pwPtr->panes[j],
|
||
pwPtr->paneOpts, objc - firstOptionArg,
|
||
objv + firstOptionArg, pwPtr->tkwin, NULL, NULL);
|
||
if (pwPtr->panes[j]->minSize < 0) {
|
||
pwPtr->panes[j]->minSize = 0;
|
||
}
|
||
found = 1;
|
||
|
||
/*
|
||
* If the pane is supposed to move, add it to the inserts
|
||
* array now; otherwise, leave it where it is.
|
||
*/
|
||
|
||
if (index != -1) {
|
||
inserts[insertIndex++] = pwPtr->panes[j];
|
||
pwPtr->panes[j] = NULL;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (found) {
|
||
continue;
|
||
}
|
||
|
||
/*
|
||
* Make sure this pane wasn't already put into the inserts array,
|
||
* i.e., 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 pane structure and initialize it. All panes start
|
||
* out with their "natural" dimensions.
|
||
*/
|
||
|
||
panePtr = (Pane *)ckalloc(sizeof(Pane));
|
||
memset(panePtr, 0, sizeof(Pane));
|
||
Tk_InitOptions(interp, (char *)panePtr, pwPtr->paneOpts,
|
||
pwPtr->tkwin);
|
||
Tk_SetOptions(interp, (char *)panePtr, pwPtr->paneOpts,
|
||
objc - firstOptionArg, objv + firstOptionArg,
|
||
pwPtr->tkwin, NULL, NULL);
|
||
panePtr->tkwin = tkwin;
|
||
panePtr->containerPtr = pwPtr;
|
||
doubleBw = 2 * Tk_Changes(panePtr->tkwin)->border_width;
|
||
if (panePtr->width > 0) {
|
||
panePtr->paneWidth = panePtr->width;
|
||
} else {
|
||
panePtr->paneWidth = Tk_ReqWidth(tkwin) + doubleBw;
|
||
}
|
||
if (panePtr->height > 0) {
|
||
panePtr->paneHeight = panePtr->height;
|
||
} else {
|
||
panePtr->paneHeight = Tk_ReqHeight(tkwin) + doubleBw;
|
||
}
|
||
if (panePtr->minSize < 0) {
|
||
panePtr->minSize = 0;
|
||
}
|
||
|
||
/*
|
||
* Set up the geometry management callbacks for this pane.
|
||
*/
|
||
|
||
Tk_CreateEventHandler(panePtr->tkwin, StructureNotifyMask,
|
||
PaneStructureProc, panePtr);
|
||
Tk_ManageGeometry(panePtr->tkwin, &panedWindowMgrType, panePtr);
|
||
inserts[insertIndex++] = panePtr;
|
||
numNewPanes++;
|
||
}
|
||
|
||
/*
|
||
* Allocate the new panes array, then copy the panes into it, in order.
|
||
*/
|
||
|
||
i = sizeof(Pane *) * (pwPtr->numPanes + numNewPanes);
|
||
newPanes = (Pane **)ckalloc(i);
|
||
memset(newPanes, 0, i);
|
||
if (index == -1) {
|
||
/*
|
||
* If none of the existing panes have to be moved, just copy the old
|
||
* and append the new.
|
||
*/
|
||
memcpy((void *)&(newPanes[0]), pwPtr->panes,
|
||
sizeof(Pane *) * pwPtr->numPanes);
|
||
memcpy((void *)&(newPanes[pwPtr->numPanes]), inserts,
|
||
sizeof(Pane *) * numNewPanes);
|
||
} else {
|
||
/*
|
||
* If some of the existing panes were moved, the old panes array
|
||
* will be partially populated, with some valid and some invalid
|
||
* entries. Walk through it, copying valid entries to the new panes
|
||
* array as we go; when we get to the insert location for the new
|
||
* panes, copy the inserts array over, then finish off the old panes
|
||
* array.
|
||
*/
|
||
|
||
for (i = 0, j = 0; i < index; i++) {
|
||
if (pwPtr->panes[i] != NULL) {
|
||
newPanes[j] = pwPtr->panes[i];
|
||
j++;
|
||
}
|
||
}
|
||
|
||
memcpy((void *)&(newPanes[j]), inserts, sizeof(Pane *)*insertIndex);
|
||
j += firstOptionArg - 2;
|
||
|
||
for (i = index; i < pwPtr->numPanes; i++) {
|
||
if (pwPtr->panes[i] != NULL) {
|
||
newPanes[j] = pwPtr->panes[i];
|
||
j++;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Make the new panes array the paned window's pane array, and clean up.
|
||
*/
|
||
|
||
ckfree(pwPtr->panes);
|
||
ckfree(inserts);
|
||
pwPtr->panes = newPanes;
|
||
|
||
/*
|
||
* Set the paned window's pane count to the new value.
|
||
*/
|
||
|
||
pwPtr->numPanes += numNewPanes;
|
||
|
||
Tk_FreeConfigOptions((char *) &options, pwPtr->paneOpts, 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(
|
||
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 *const sashOptionStrings[] = {
|
||
"coord", "dragto", "mark", "place", NULL
|
||
};
|
||
enum sashOptions {
|
||
SASH_COORD, SASH_DRAGTO, SASH_MARK, SASH_PLACE
|
||
};
|
||
int index, sash, x, y, diff;
|
||
Tcl_Obj *coords[2];
|
||
Pane *panePtr;
|
||
|
||
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;
|
||
}
|
||
|
||
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_SetObjResult(interp, Tcl_NewStringObj(
|
||
"invalid sash index", -1));
|
||
Tcl_SetErrorCode(interp, "TK", "VALUE", "SASH_INDEX", NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
panePtr = pwPtr->panes[sash];
|
||
|
||
coords[0] = Tcl_NewIntObj(panePtr->sashx);
|
||
coords[1] = Tcl_NewIntObj(panePtr->sashy);
|
||
Tcl_SetObjResult(interp, Tcl_NewListObj(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_SetObjResult(interp, Tcl_NewStringObj(
|
||
"invalid sash index", -1));
|
||
Tcl_SetErrorCode(interp, "TK", "VALUE", "SASH_INDEX", NULL);
|
||
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->panes[sash]->markx = x;
|
||
pwPtr->panes[sash]->marky = y;
|
||
} else {
|
||
coords[0] = Tcl_NewIntObj(pwPtr->panes[sash]->markx);
|
||
coords[1] = Tcl_NewIntObj(pwPtr->panes[sash]->marky);
|
||
Tcl_SetObjResult(interp, Tcl_NewListObj(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_SetObjResult(interp, Tcl_NewStringObj(
|
||
"invalid sash index", -1));
|
||
Tcl_SetErrorCode(interp, "TK", "VALUE", "SASH_INDEX", NULL);
|
||
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;
|
||
}
|
||
|
||
panePtr = pwPtr->panes[sash];
|
||
if (pwPtr->orient == ORIENT_HORIZONTAL) {
|
||
if (index == SASH_PLACE) {
|
||
diff = x - pwPtr->panes[sash]->sashx;
|
||
} else {
|
||
diff = x - pwPtr->panes[sash]->markx;
|
||
}
|
||
} else {
|
||
if (index == SASH_PLACE) {
|
||
diff = y - pwPtr->panes[sash]->sashy;
|
||
} else {
|
||
diff = y - pwPtr->panes[sash]->marky;
|
||
}
|
||
}
|
||
|
||
MoveSash(pwPtr, sash, diff);
|
||
ComputeGeometry(pwPtr);
|
||
}
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* ConfigurePanedWindow --
|
||
*
|
||
* This function 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(
|
||
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(pwPtr);
|
||
|
||
/*
|
||
* If an option that affects geometry has changed, make a re-layout
|
||
* request.
|
||
*/
|
||
|
||
if (typemask & GEOMETRY) {
|
||
ComputeGeometry(pwPtr);
|
||
}
|
||
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* PanedWindowWorldChanged --
|
||
*
|
||
* This function 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.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
PanedWindowWorldChanged(
|
||
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.) and set the window background.
|
||
*/
|
||
|
||
gcValues.background = Tk_3DBorderColor(pwPtr->background)->pixel;
|
||
newGC = Tk_GetGC(pwPtr->tkwin, GCBackground, &gcValues);
|
||
if (pwPtr->gc != NULL) {
|
||
Tk_FreeGC(pwPtr->display, pwPtr->gc);
|
||
}
|
||
pwPtr->gc = newGC;
|
||
Tk_SetWindowBackground(pwPtr->tkwin, gcValues.background);
|
||
|
||
/*
|
||
* 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, pwPtr);
|
||
pwPtr->flags |= REDRAW_PENDING;
|
||
}
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* PanedWindowEventProc --
|
||
*
|
||
* This function 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 clientData, /* Information about window. */
|
||
XEvent *eventPtr) /* Information about event. */
|
||
{
|
||
PanedWindow *pwPtr = (PanedWindow *)clientData;
|
||
int i;
|
||
|
||
if (eventPtr->type == Expose) {
|
||
if (pwPtr->tkwin != NULL && !(pwPtr->flags & REDRAW_PENDING)) {
|
||
Tcl_DoWhenIdle(DisplayPanedWindow, 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, pwPtr);
|
||
pwPtr->flags |= REDRAW_PENDING;
|
||
}
|
||
} else if (eventPtr->type == DestroyNotify) {
|
||
DestroyPanedWindow(pwPtr);
|
||
} else if (eventPtr->type == UnmapNotify) {
|
||
for (i = 0; i < pwPtr->numPanes; i++) {
|
||
if (!pwPtr->panes[i]->hide) {
|
||
Tk_UnmapWindow(pwPtr->panes[i]->tkwin);
|
||
}
|
||
}
|
||
} else if (eventPtr->type == MapNotify) {
|
||
for (i = 0; i < pwPtr->numPanes; i++) {
|
||
if (!pwPtr->panes[i]->hide) {
|
||
Tk_MapWindow(pwPtr->panes[i]->tkwin);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* PanedWindowCmdDeletedProc --
|
||
*
|
||
* This function 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) /* Pointer to widget record for widget. */
|
||
{
|
||
PanedWindow *pwPtr = (PanedWindow *)clientData;
|
||
|
||
/*
|
||
* This function could be invoked either because the window was destroyed
|
||
* and the command was then deleted or because the command was deleted,
|
||
* and then this function 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 function 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) /* Information about window. */
|
||
{
|
||
PanedWindow *pwPtr = (PanedWindow *)clientData;
|
||
Pane *panePtr;
|
||
Pixmap pixmap;
|
||
Tk_Window tkwin = pwPtr->tkwin;
|
||
int i, sashWidth, sashHeight;
|
||
const int horizontal = (pwPtr->orient == ORIENT_HORIZONTAL);
|
||
int first, last;
|
||
|
||
pwPtr->flags &= ~REDRAW_PENDING;
|
||
if ((pwPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
|
||
return;
|
||
}
|
||
|
||
if (pwPtr->flags & REQUESTED_RELAYOUT) {
|
||
ArrangePanes(clientData);
|
||
}
|
||
|
||
#ifndef TK_NO_DOUBLE_BUFFERING
|
||
/*
|
||
* 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));
|
||
#else
|
||
pixmap = Tk_WindowId(tkwin);
|
||
#endif /* TK_NO_DOUBLE_BUFFERING */
|
||
|
||
/*
|
||
* 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 (horizontal) {
|
||
sashHeight = Tk_Height(tkwin) - (2 * Tk_InternalBorderLeft(tkwin));
|
||
sashWidth = pwPtr->sashWidth;
|
||
} else {
|
||
sashWidth = Tk_Width(tkwin) - (2 * Tk_InternalBorderLeft(tkwin));
|
||
sashHeight = pwPtr->sashWidth;
|
||
}
|
||
|
||
/*
|
||
* Draw the sashes.
|
||
*/
|
||
|
||
GetFirstLastVisiblePane(pwPtr, &first, &last);
|
||
for (i = 0; i < pwPtr->numPanes - 1; i++) {
|
||
panePtr = pwPtr->panes[i];
|
||
if (panePtr->hide || i == last) {
|
||
continue;
|
||
}
|
||
if (sashWidth > 0 && sashHeight > 0) {
|
||
Tk_Fill3DRectangle(tkwin, pixmap, pwPtr->background,
|
||
panePtr->sashx, panePtr->sashy, sashWidth, sashHeight,
|
||
1, pwPtr->sashRelief);
|
||
}
|
||
if (pwPtr->showHandle) {
|
||
Tk_Fill3DRectangle(tkwin, pixmap, pwPtr->background,
|
||
panePtr->handlex, panePtr->handley,
|
||
pwPtr->handleSize, pwPtr->handleSize, 1,
|
||
TK_RELIEF_RAISED);
|
||
}
|
||
}
|
||
|
||
#ifndef TK_NO_DOUBLE_BUFFERING
|
||
/*
|
||
* 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);
|
||
#endif /* TK_NO_DOUBLE_BUFFERING */
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* DestroyPanedWindow --
|
||
*
|
||
* This function 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(
|
||
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 functions 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, pwPtr);
|
||
}
|
||
if (pwPtr->flags & RESIZE_PENDING) {
|
||
Tcl_CancelIdleCall(ArrangePanes, pwPtr);
|
||
}
|
||
|
||
/*
|
||
* Clean up the pane list; foreach pane:
|
||
* o Cancel the pane's structure notification callback
|
||
* o Cancel geometry management for the pane.
|
||
* o Free memory for the pane
|
||
*/
|
||
|
||
for (i = 0; i < pwPtr->numPanes; i++) {
|
||
Tk_DeleteEventHandler(pwPtr->panes[i]->tkwin, StructureNotifyMask,
|
||
PaneStructureProc, pwPtr->panes[i]);
|
||
Tk_ManageGeometry(pwPtr->panes[i]->tkwin, NULL, NULL);
|
||
Tk_FreeConfigOptions((char *) pwPtr->panes[i], pwPtr->paneOpts,
|
||
pwPtr->tkwin);
|
||
ckfree(pwPtr->panes[i]);
|
||
pwPtr->panes[i] = NULL;
|
||
}
|
||
if (pwPtr->panes) {
|
||
ckfree(pwPtr->panes);
|
||
}
|
||
|
||
/*
|
||
* 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(pwPtr->tkwin);
|
||
pwPtr->tkwin = NULL;
|
||
|
||
Tcl_EventuallyFree(pwPtr, TCL_DYNAMIC);
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* PanedWindowReqProc --
|
||
*
|
||
* This function 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 clientData, /* Paned window's information about window
|
||
* that got new preferred geometry. */
|
||
TCL_UNUSED(Tk_Window)) /* Other Tk-related information about the
|
||
* window. */
|
||
{
|
||
Pane *panePtr = (Pane *)clientData;
|
||
PanedWindow *pwPtr = (PanedWindow *) panePtr->containerPtr;
|
||
|
||
if (Tk_IsMapped(pwPtr->tkwin)) {
|
||
if (!(pwPtr->flags & RESIZE_PENDING)) {
|
||
pwPtr->flags |= RESIZE_PENDING;
|
||
Tcl_DoWhenIdle(ArrangePanes, pwPtr);
|
||
}
|
||
} else {
|
||
int doubleBw = 2 * Tk_Changes(panePtr->tkwin)->border_width;
|
||
|
||
if (panePtr->width <= 0) {
|
||
panePtr->paneWidth = Tk_ReqWidth(panePtr->tkwin) + doubleBw;
|
||
}
|
||
if (panePtr->height <= 0) {
|
||
panePtr->paneHeight = Tk_ReqHeight(panePtr->tkwin) + doubleBw;
|
||
}
|
||
ComputeGeometry(pwPtr);
|
||
}
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* PanedWindowLostPaneProc --
|
||
*
|
||
* This function is invoked by Tk whenever some other geometry claims
|
||
* control over a pane that used to be managed by us.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Forgets all information about the pane. Causes geometry to be
|
||
* recomputed for the panedwindow.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
PanedWindowLostPaneProc(
|
||
ClientData clientData, /* Grid structure for the pane that was
|
||
* stolen away. */
|
||
TCL_UNUSED(Tk_Window)) /* Tk's handle for the pane. */
|
||
{
|
||
Pane *panePtr = (Pane *)clientData;
|
||
PanedWindow *pwPtr = (PanedWindow *) panePtr->containerPtr;
|
||
|
||
if (pwPtr->tkwin != Tk_Parent(panePtr->tkwin)) {
|
||
Tk_UnmaintainGeometry(panePtr->tkwin, pwPtr->tkwin);
|
||
}
|
||
Unlink(panePtr);
|
||
Tk_DeleteEventHandler(panePtr->tkwin, StructureNotifyMask,
|
||
PaneStructureProc, panePtr);
|
||
Tk_UnmapWindow(panePtr->tkwin);
|
||
panePtr->tkwin = NULL;
|
||
ckfree(panePtr);
|
||
ComputeGeometry(pwPtr);
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* ArrangePanes --
|
||
*
|
||
* This function 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 panes of containerPtr may get resized or moved.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
ArrangePanes(
|
||
ClientData clientData) /* Structure describing parent whose panes
|
||
* are to be re-layed out. */
|
||
{
|
||
PanedWindow *pwPtr = (PanedWindow *)clientData;
|
||
Pane *panePtr;
|
||
int i, newPaneWidth, newPaneHeight, paneX, paneY;
|
||
int paneWidth, paneHeight, paneSize, paneMinSize;
|
||
int doubleBw;
|
||
int x, y;
|
||
int sashWidth, sashOffset, sashCount, handleOffset;
|
||
int sashReserve, sxReserve, syReserve;
|
||
int internalBW;
|
||
int paneDynSize, paneDynMinSize, pwHeight, pwWidth, pwSize;
|
||
int first, last;
|
||
int stretchReserve, stretchAmount;
|
||
const int horizontal = (pwPtr->orient == ORIENT_HORIZONTAL);
|
||
|
||
pwPtr->flags &= ~(REQUESTED_RELAYOUT|RESIZE_PENDING);
|
||
|
||
/*
|
||
* If the parent has no panes 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->numPanes == 0) {
|
||
return;
|
||
}
|
||
|
||
Tcl_Preserve(pwPtr);
|
||
|
||
/*
|
||
* Find index of first and last visible panes.
|
||
*/
|
||
|
||
GetFirstLastVisiblePane(pwPtr, &first, &last);
|
||
|
||
/*
|
||
* First pass; compute sizes
|
||
*/
|
||
|
||
paneDynSize = paneDynMinSize = 0;
|
||
internalBW = Tk_InternalBorderLeft(pwPtr->tkwin);
|
||
pwHeight = Tk_Height(pwPtr->tkwin) - (2 * internalBW);
|
||
pwWidth = Tk_Width(pwPtr->tkwin) - (2 * internalBW);
|
||
x = y = internalBW;
|
||
stretchReserve = (horizontal ? pwWidth : pwHeight);
|
||
|
||
/*
|
||
* Calculate the sash width, including handle and padding, and the sash
|
||
* and handle offsets.
|
||
*/
|
||
|
||
sashOffset = handleOffset = pwPtr->sashPad;
|
||
if (pwPtr->showHandle && pwPtr->handleSize > pwPtr->sashWidth) {
|
||
sashWidth = (2 * pwPtr->sashPad) + pwPtr->handleSize;
|
||
sashOffset = ((pwPtr->handleSize - pwPtr->sashWidth) / 2)
|
||
+ pwPtr->sashPad;
|
||
} else {
|
||
sashWidth = (2 * pwPtr->sashPad) + pwPtr->sashWidth;
|
||
handleOffset = ((pwPtr->sashWidth - pwPtr->handleSize) / 2)
|
||
+ pwPtr->sashPad;
|
||
}
|
||
|
||
for (i = sashCount = 0; i < pwPtr->numPanes; i++) {
|
||
panePtr = pwPtr->panes[i];
|
||
|
||
if (panePtr->hide) {
|
||
continue;
|
||
}
|
||
|
||
/*
|
||
* Compute the total size needed by all the panes and the left-over,
|
||
* or shortage of space available.
|
||
*/
|
||
|
||
if (horizontal) {
|
||
if (panePtr->width > 0) {
|
||
paneSize = panePtr->width;
|
||
} else {
|
||
paneSize = panePtr->paneWidth;
|
||
}
|
||
stretchReserve -= paneSize + (2 * panePtr->padx);
|
||
} else {
|
||
if (panePtr->height > 0) {
|
||
paneSize = panePtr->height;
|
||
} else {
|
||
paneSize = panePtr->paneHeight;
|
||
}
|
||
stretchReserve -= paneSize + (2 * panePtr->pady);
|
||
}
|
||
if (IsStretchable(panePtr->stretch,i,first,last)
|
||
&& Tk_IsMapped(pwPtr->tkwin)) {
|
||
paneDynSize += paneSize;
|
||
paneDynMinSize += panePtr->minSize;
|
||
}
|
||
if (i != last) {
|
||
stretchReserve -= sashWidth;
|
||
sashCount++;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Second pass; adjust/arrange panes.
|
||
*/
|
||
|
||
for (i = 0; i < pwPtr->numPanes; i++) {
|
||
panePtr = pwPtr->panes[i];
|
||
|
||
if (panePtr->hide) {
|
||
Tk_UnmaintainGeometry(panePtr->tkwin, pwPtr->tkwin);
|
||
Tk_UnmapWindow(panePtr->tkwin);
|
||
continue;
|
||
}
|
||
|
||
/*
|
||
* Compute the size of this pane. The algorithm (assuming a
|
||
* horizontal paned window) is:
|
||
*
|
||
* 1. Get "base" dimensions. If a width or height is specified for
|
||
* this pane, 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(panePtr->tkwin)->border_width;
|
||
newPaneWidth = (panePtr->width > 0 ? panePtr->width :
|
||
Tk_ReqWidth(panePtr->tkwin) + doubleBw);
|
||
newPaneHeight = (panePtr->height > 0 ? panePtr->height :
|
||
Tk_ReqHeight(panePtr->tkwin) + doubleBw);
|
||
paneMinSize = panePtr->minSize;
|
||
|
||
/*
|
||
* Calculate pane width and height.
|
||
*/
|
||
|
||
if (horizontal) {
|
||
if (panePtr->width > 0) {
|
||
paneSize = panePtr->width;
|
||
} else {
|
||
paneSize = panePtr->paneWidth;
|
||
}
|
||
pwSize = pwWidth;
|
||
} else {
|
||
if (panePtr->height > 0) {
|
||
paneSize = panePtr->height;
|
||
} else {
|
||
paneSize = panePtr->paneHeight;
|
||
}
|
||
pwSize = pwHeight;
|
||
}
|
||
if (IsStretchable(panePtr->stretch, i, first, last)) {
|
||
double frac;
|
||
|
||
if (paneDynSize > 0) {
|
||
frac = (double)paneSize / (double)paneDynSize;
|
||
} else {
|
||
frac = (double)paneSize / (double)pwSize;
|
||
}
|
||
|
||
paneDynSize -= paneSize;
|
||
paneDynMinSize -= panePtr->minSize;
|
||
stretchAmount = (int) (frac * stretchReserve);
|
||
if (paneSize + stretchAmount >= paneMinSize) {
|
||
stretchReserve -= stretchAmount;
|
||
paneSize += stretchAmount;
|
||
} else {
|
||
stretchReserve += paneSize - paneMinSize;
|
||
paneSize = paneMinSize;
|
||
}
|
||
if (i == last && stretchReserve > 0) {
|
||
paneSize += stretchReserve;
|
||
stretchReserve = 0;
|
||
}
|
||
} else if (paneDynSize - paneDynMinSize + stretchReserve < 0) {
|
||
if (paneSize + paneDynSize - paneDynMinSize + stretchReserve
|
||
<= paneMinSize) {
|
||
stretchReserve += paneSize - paneMinSize;
|
||
paneSize = paneMinSize;
|
||
} else {
|
||
paneSize += paneDynSize - paneDynMinSize + stretchReserve;
|
||
stretchReserve = paneDynMinSize - paneDynSize;
|
||
}
|
||
}
|
||
if (horizontal) {
|
||
paneWidth = paneSize;
|
||
paneHeight = pwHeight - (2 * panePtr->pady);
|
||
} else {
|
||
paneWidth = pwWidth - (2 * panePtr->padx);
|
||
paneHeight = paneSize;
|
||
}
|
||
|
||
/*
|
||
* Adjust for area reserved for sashes.
|
||
*/
|
||
|
||
if (sashCount) {
|
||
sashReserve = sashWidth * sashCount;
|
||
if (horizontal) {
|
||
sxReserve = sashReserve;
|
||
syReserve = 0;
|
||
} else {
|
||
sxReserve = 0;
|
||
syReserve = sashReserve;
|
||
}
|
||
} else {
|
||
sxReserve = syReserve = 0;
|
||
}
|
||
|
||
if (pwWidth - sxReserve < x + paneWidth - internalBW) {
|
||
paneWidth = pwWidth - sxReserve - x + internalBW;
|
||
}
|
||
if (pwHeight - syReserve < y + paneHeight - internalBW) {
|
||
paneHeight = pwHeight - syReserve - y + internalBW;
|
||
}
|
||
|
||
if (newPaneWidth > paneWidth) {
|
||
newPaneWidth = paneWidth;
|
||
}
|
||
if (newPaneHeight > paneHeight) {
|
||
newPaneHeight = paneHeight;
|
||
}
|
||
|
||
panePtr->x = x;
|
||
panePtr->y = y;
|
||
|
||
/*
|
||
* Compute the location of the sash at the right or bottom of the
|
||
* parcel and the location of the next parcel.
|
||
*/
|
||
|
||
if (horizontal) {
|
||
x += paneWidth + (2 * panePtr->padx);
|
||
if (x < internalBW) {
|
||
x = internalBW;
|
||
}
|
||
panePtr->sashx = x + sashOffset;
|
||
panePtr->sashy = y;
|
||
panePtr->handlex = x + handleOffset;
|
||
panePtr->handley = y + pwPtr->handlePad;
|
||
x += sashWidth;
|
||
} else {
|
||
y += paneHeight + (2 * panePtr->pady);
|
||
if (y < internalBW) {
|
||
y = internalBW;
|
||
}
|
||
panePtr->sashx = x;
|
||
panePtr->sashy = y + sashOffset;
|
||
panePtr->handlex = x + pwPtr->handlePad;
|
||
panePtr->handley = y + handleOffset;
|
||
y += sashWidth;
|
||
}
|
||
|
||
/*
|
||
* Compute the actual dimensions of the pane in the pane.
|
||
*/
|
||
|
||
paneX = panePtr->x;
|
||
paneY = panePtr->y;
|
||
AdjustForSticky(panePtr->sticky, paneWidth, paneHeight,
|
||
&paneX, &paneY, &newPaneWidth, &newPaneHeight);
|
||
|
||
paneX += panePtr->padx;
|
||
paneY += panePtr->pady;
|
||
|
||
/*
|
||
* Now put the window in the proper spot.
|
||
*/
|
||
|
||
if (newPaneWidth <= 0 || newPaneHeight <= 0 ||
|
||
(horizontal ? paneX - internalBW > pwWidth :
|
||
paneY - internalBW > pwHeight)) {
|
||
Tk_UnmaintainGeometry(panePtr->tkwin, pwPtr->tkwin);
|
||
Tk_UnmapWindow(panePtr->tkwin);
|
||
} else {
|
||
Tk_MaintainGeometry(panePtr->tkwin, pwPtr->tkwin,
|
||
paneX, paneY, newPaneWidth, newPaneHeight);
|
||
}
|
||
sashCount--;
|
||
}
|
||
Tcl_Release(pwPtr);
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* Unlink --
|
||
*
|
||
* Remove a pane from a paned window.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* The paned window will be scheduled for re-arranging and redrawing.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
Unlink(
|
||
Pane *panePtr) /* Window to unlink. */
|
||
{
|
||
PanedWindow *containerPtr;
|
||
int i, j;
|
||
|
||
containerPtr = panePtr->containerPtr;
|
||
if (containerPtr == NULL) {
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* Find the specified pane in the panedwindow's list of panes, then
|
||
* remove it from that list.
|
||
*/
|
||
|
||
for (i = 0; i < containerPtr->numPanes; i++) {
|
||
if (containerPtr->panes[i] == panePtr) {
|
||
for (j = i; j < containerPtr->numPanes - 1; j++) {
|
||
containerPtr->panes[j] = containerPtr->panes[j + 1];
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Clean out any -after or -before references to this pane
|
||
*/
|
||
|
||
for (i = 0; i < containerPtr->numPanes; i++) {
|
||
if (containerPtr->panes[i]->before == panePtr->tkwin) {
|
||
containerPtr->panes[i]->before = NULL;
|
||
}
|
||
if (containerPtr->panes[i]->after == panePtr->tkwin) {
|
||
containerPtr->panes[i]->after = NULL;
|
||
}
|
||
}
|
||
|
||
containerPtr->flags |= REQUESTED_RELAYOUT;
|
||
if (!(containerPtr->flags & REDRAW_PENDING)) {
|
||
containerPtr->flags |= REDRAW_PENDING;
|
||
Tcl_DoWhenIdle(DisplayPanedWindow, containerPtr);
|
||
}
|
||
|
||
/*
|
||
* Set the pane's containerPtr to NULL, so that we can tell that the pane
|
||
* is no longer attached to any panedwindow.
|
||
*/
|
||
|
||
panePtr->containerPtr = NULL;
|
||
|
||
containerPtr->numPanes--;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* 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 pane structure, or NULL if the window is not managed
|
||
* by this paned window.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static Pane *
|
||
GetPane(
|
||
PanedWindow *pwPtr, /* Pointer to the paned window info. */
|
||
Tk_Window tkwin) /* Window to search for. */
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < pwPtr->numPanes; i++) {
|
||
if (pwPtr->panes[i]->tkwin == tkwin) {
|
||
return pwPtr->panes[i];
|
||
}
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* GetFirstLastVisiblePane --
|
||
*
|
||
* Given panedwindow, find the index of the first and last visible panes
|
||
* of that paned window.
|
||
*
|
||
* Results:
|
||
* Index of the first and last visible panes.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
GetFirstLastVisiblePane(
|
||
PanedWindow *pwPtr, /* Pointer to the paned window info. */
|
||
int *firstPtr, /* Returned index for first. */
|
||
int *lastPtr) /* Returned index for last. */
|
||
{
|
||
int i;
|
||
|
||
for (i = 0, *lastPtr = 0, *firstPtr = -1; i < pwPtr->numPanes; i++) {
|
||
if (pwPtr->panes[i]->hide == 0) {
|
||
if (*firstPtr < 0) {
|
||
*firstPtr = i;
|
||
}
|
||
*lastPtr = i;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* PaneStructureProc --
|
||
*
|
||
* This function is invoked whenever StructureNotify events occur for a
|
||
* window that's managed by a paned window. This function's only purpose
|
||
* is to clean up when windows are deleted.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* The paned window pane structure associated with the window
|
||
* is freed, and the pane is disassociated from the paned
|
||
* window which managed it.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
PaneStructureProc(
|
||
ClientData clientData, /* Pointer to record describing window item. */
|
||
XEvent *eventPtr) /* Describes what just happened. */
|
||
{
|
||
Pane *panePtr = (Pane *)clientData;
|
||
PanedWindow *pwPtr = panePtr->containerPtr;
|
||
|
||
if (eventPtr->type == DestroyNotify) {
|
||
Unlink(panePtr);
|
||
panePtr->tkwin = NULL;
|
||
ckfree(panePtr);
|
||
ComputeGeometry(pwPtr);
|
||
}
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* ComputeGeometry --
|
||
*
|
||
* Compute geometry for the paned window, including coordinates of all
|
||
* panes and each sash.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Recomputes geometry information for a paned window.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
ComputeGeometry(
|
||
PanedWindow *pwPtr) /* Pointer to the Paned Window structure. */
|
||
{
|
||
int i, x, y, doubleBw, internalBw;
|
||
int sashWidth, sashOffset, handleOffset;
|
||
int reqWidth, reqHeight, dim;
|
||
Pane *panePtr;
|
||
const int horizontal = (pwPtr->orient == ORIENT_HORIZONTAL);
|
||
|
||
pwPtr->flags |= REQUESTED_RELAYOUT;
|
||
|
||
x = y = internalBw = Tk_InternalBorderLeft(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).
|
||
*/
|
||
|
||
sashOffset = handleOffset = pwPtr->sashPad;
|
||
if (pwPtr->showHandle && pwPtr->handleSize > pwPtr->sashWidth) {
|
||
sashWidth = (2 * pwPtr->sashPad) + pwPtr->handleSize;
|
||
sashOffset = ((pwPtr->handleSize - pwPtr->sashWidth) / 2)
|
||
+ pwPtr->sashPad;
|
||
} else {
|
||
sashWidth = (2 * pwPtr->sashPad) + pwPtr->sashWidth;
|
||
handleOffset = ((pwPtr->sashWidth - pwPtr->handleSize) / 2)
|
||
+ pwPtr->sashPad;
|
||
}
|
||
|
||
for (i = 0; i < pwPtr->numPanes; i++) {
|
||
panePtr = pwPtr->panes[i];
|
||
|
||
if (panePtr->hide) {
|
||
continue;
|
||
}
|
||
|
||
/*
|
||
* First set the coordinates for the top left corner of the pane's
|
||
* parcel.
|
||
*/
|
||
|
||
panePtr->x = x;
|
||
panePtr->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 (horizontal) {
|
||
if (panePtr->paneWidth < panePtr->minSize) {
|
||
panePtr->paneWidth = panePtr->minSize;
|
||
}
|
||
} else {
|
||
if (panePtr->paneHeight < panePtr->minSize) {
|
||
panePtr->paneHeight = panePtr->minSize;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Compute the location of the sash at the right or bottom of the
|
||
* parcel.
|
||
*/
|
||
|
||
if (horizontal) {
|
||
x += panePtr->paneWidth + (2 * panePtr->padx);
|
||
panePtr->sashx = x + sashOffset;
|
||
panePtr->sashy = y;
|
||
panePtr->handlex = x + handleOffset;
|
||
panePtr->handley = y + pwPtr->handlePad;
|
||
x += sashWidth;
|
||
} else {
|
||
y += panePtr->paneHeight + (2 * panePtr->pady);
|
||
panePtr->sashx = x;
|
||
panePtr->sashy = y + sashOffset;
|
||
panePtr->handlex = x + pwPtr->handlePad;
|
||
panePtr->handley = y + handleOffset;
|
||
y += sashWidth;
|
||
}
|
||
|
||
/*
|
||
* Find the maximum height/width of the panes, for computing the
|
||
* requested height/width of the paned window.
|
||
*/
|
||
|
||
if (horizontal) {
|
||
/*
|
||
* If the pane has an explicit height set, use that; otherwise,
|
||
* use the pane's requested height.
|
||
*/
|
||
|
||
if (panePtr->height > 0) {
|
||
dim = panePtr->height;
|
||
} else {
|
||
doubleBw = 2 * Tk_Changes(panePtr->tkwin)->border_width;
|
||
dim = Tk_ReqHeight(panePtr->tkwin) + doubleBw;
|
||
}
|
||
dim += 2 * panePtr->pady;
|
||
if (dim > reqHeight) {
|
||
reqHeight = dim;
|
||
}
|
||
} else {
|
||
/*
|
||
* If the pane has an explicit width set use that; otherwise, use
|
||
* the pane's requested width.
|
||
*/
|
||
|
||
if (panePtr->width > 0) {
|
||
dim = panePtr->width;
|
||
} else {
|
||
doubleBw = 2 * Tk_Changes(panePtr->tkwin)->border_width;
|
||
dim = Tk_ReqWidth(panePtr->tkwin) + doubleBw;
|
||
}
|
||
dim += 2 * panePtr->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
|
||
* panes, plus the width of the border of the top and bottom (or left and
|
||
* right) of the paned window.
|
||
*
|
||
* If the panedwindow has an explicit width/height set use that;
|
||
* otherwise, use the requested width/height.
|
||
*/
|
||
|
||
if (horizontal) {
|
||
reqWidth = (pwPtr->width > 0 ?
|
||
pwPtr->width : x - sashWidth + internalBw);
|
||
reqHeight = (pwPtr->height > 0 ?
|
||
pwPtr->height : reqHeight + (2 * internalBw));
|
||
} else {
|
||
reqWidth = (pwPtr->width > 0 ?
|
||
pwPtr->width : reqWidth + (2 * internalBw));
|
||
reqHeight = (pwPtr->height > 0 ?
|
||
pwPtr->height : y - sashWidth + internalBw);
|
||
}
|
||
Tk_GeometryRequest(pwPtr->tkwin, reqWidth, reqHeight);
|
||
if (Tk_IsMapped(pwPtr->tkwin) && !(pwPtr->flags & REDRAW_PENDING)) {
|
||
pwPtr->flags |= REDRAW_PENDING;
|
||
Tcl_DoWhenIdle(DisplayPanedWindow, pwPtr);
|
||
}
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* DestroyOptionTables --
|
||
*
|
||
* This function 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 clientData, /* Pointer to the OptionTables struct */
|
||
TCL_UNUSED(Tcl_Interp *)) /* Pointer to the calling interp */
|
||
{
|
||
ckfree(clientData);
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* GetSticky -
|
||
*
|
||
* Converts an internal boolean combination of "sticky" bits into a Tcl
|
||
* string obj containing zero or more 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(
|
||
TCL_UNUSED(void *),
|
||
TCL_UNUSED(Tk_Window),
|
||
char *recordPtr, /* Pointer to widget record. */
|
||
int internalOffset) /* Offset within *recordPtr containing the
|
||
* sticky value. */
|
||
{
|
||
int sticky = *(int *)(recordPtr + internalOffset);
|
||
char buffer[5];
|
||
char *p = &buffer[0];
|
||
|
||
if (sticky & STICK_NORTH) {
|
||
*p++ = 'n';
|
||
}
|
||
if (sticky & STICK_EAST) {
|
||
*p++ = 'e';
|
||
}
|
||
if (sticky & STICK_SOUTH) {
|
||
*p++ = 's';
|
||
}
|
||
if (sticky & STICK_WEST) {
|
||
*p++ = 'w';
|
||
}
|
||
*p = '\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(
|
||
TCL_UNUSED(void *),
|
||
Tcl_Interp *interp, /* Current interp; may be used for errors. */
|
||
TCL_UNUSED(Tk_Window), /* 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, *internalPtr;
|
||
const char *string;
|
||
|
||
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_SetObjResult(interp, Tcl_ObjPrintf(
|
||
"bad stickyness value \"%s\": must be a string"
|
||
" containing zero or more of n, e, s, and w",
|
||
Tcl_GetString(*value)));
|
||
Tcl_SetErrorCode(interp, "TK", "VALUE", "STICKY", 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(
|
||
TCL_UNUSED(void *),
|
||
TCL_UNUSED(Tk_Window),
|
||
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 pane, compute the x,y coords
|
||
* and actual dimensions of the pane based on the pane's sticky value.
|
||
*
|
||
* Results:
|
||
* No direct return; sets the x, y, paneWidth and paneHeight to correct
|
||
* values.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
AdjustForSticky(
|
||
int sticky, /* Sticky value; see top of file for
|
||
* definition. */
|
||
int cavityWidth, /* Width of the cavity. */
|
||
int cavityHeight, /* Height of the cavity. */
|
||
int *xPtr, int *yPtr, /* Initially, coordinates of the top-left
|
||
* corner of cavity; also return values for
|
||
* actual x, y coords of pane. */
|
||
int *paneWidthPtr, /* Pane width. */
|
||
int *paneHeightPtr) /* Pane height. */
|
||
{
|
||
int diffx = 0; /* Cavity width - pane width. */
|
||
int diffy = 0; /* Cavity hight - pane height. */
|
||
|
||
if (cavityWidth > *paneWidthPtr) {
|
||
diffx = cavityWidth - *paneWidthPtr;
|
||
}
|
||
|
||
if (cavityHeight > *paneHeightPtr) {
|
||
diffy = cavityHeight - *paneHeightPtr;
|
||
}
|
||
|
||
if ((sticky & STICK_EAST) && (sticky & STICK_WEST)) {
|
||
*paneWidthPtr += diffx;
|
||
}
|
||
if ((sticky & STICK_NORTH) && (sticky & STICK_SOUTH)) {
|
||
*paneHeightPtr += 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(
|
||
PanedWindow *pwPtr,
|
||
int sash,
|
||
int diff)
|
||
{
|
||
int i;
|
||
int expandPane, reduceFirst, reduceLast, reduceIncr, paneSize, sashOffset;
|
||
Pane *panePtr;
|
||
int stretchReserve = 0;
|
||
int nextSash = sash + 1;
|
||
const int horizontal = (pwPtr->orient == ORIENT_HORIZONTAL);
|
||
|
||
if (diff == 0)
|
||
return;
|
||
|
||
/*
|
||
* Update the pane sizes with their real sizes.
|
||
*/
|
||
|
||
if (pwPtr->showHandle && pwPtr->handleSize > pwPtr->sashWidth) {
|
||
sashOffset = ((pwPtr->handleSize - pwPtr->sashWidth) / 2)
|
||
+ pwPtr->sashPad;
|
||
} else {
|
||
sashOffset = pwPtr->sashPad;
|
||
}
|
||
for (i = 0; i < pwPtr->numPanes; i++) {
|
||
panePtr = pwPtr->panes[i];
|
||
if (panePtr->hide) {
|
||
continue;
|
||
}
|
||
if (horizontal) {
|
||
panePtr->paneWidth = panePtr->width = panePtr->sashx
|
||
- sashOffset - panePtr->x - (2 * panePtr->padx);
|
||
} else {
|
||
panePtr->paneHeight = panePtr->height = panePtr->sashy
|
||
- sashOffset - panePtr->y - (2 * panePtr->pady);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* There must be a next sash since it is only possible to enter this
|
||
* routine when moving an actual sash which implies there exists a visible
|
||
* pane to either side of the sash.
|
||
*/
|
||
|
||
while (nextSash < pwPtr->numPanes-1 && pwPtr->panes[nextSash]->hide) {
|
||
nextSash++;
|
||
}
|
||
|
||
/*
|
||
* Consolidate +/-diff variables to reduce duplicate code.
|
||
*/
|
||
|
||
if (diff > 0) {
|
||
expandPane = sash;
|
||
reduceFirst = nextSash;
|
||
reduceLast = pwPtr->numPanes;
|
||
reduceIncr = 1;
|
||
} else {
|
||
diff = abs(diff);
|
||
expandPane = nextSash;
|
||
reduceFirst = sash;
|
||
reduceLast = -1;
|
||
reduceIncr = -1;
|
||
}
|
||
|
||
/*
|
||
* Calculate how much room we have to stretch in and adjust diff value
|
||
* accordingly.
|
||
*/
|
||
|
||
for (i = reduceFirst; i != reduceLast; i += reduceIncr) {
|
||
panePtr = pwPtr->panes[i];
|
||
if (panePtr->hide) {
|
||
continue;
|
||
}
|
||
if (horizontal) {
|
||
stretchReserve += panePtr->width - panePtr->minSize;
|
||
} else {
|
||
stretchReserve += panePtr->height - panePtr->minSize;
|
||
}
|
||
}
|
||
if (stretchReserve <= 0) {
|
||
return;
|
||
}
|
||
if (diff > stretchReserve) {
|
||
diff = stretchReserve;
|
||
}
|
||
|
||
/*
|
||
* Expand pane by diff amount.
|
||
*/
|
||
|
||
panePtr = pwPtr->panes[expandPane];
|
||
if (horizontal) {
|
||
panePtr->paneWidth = panePtr->width += diff;
|
||
} else {
|
||
panePtr->paneHeight = panePtr->height += diff;
|
||
}
|
||
|
||
/*
|
||
* Reduce panes, respecting minsize, until diff amount has been used.
|
||
*/
|
||
|
||
for (i = reduceFirst; i != reduceLast; i += reduceIncr) {
|
||
panePtr = pwPtr->panes[i];
|
||
if (panePtr->hide) {
|
||
continue;
|
||
}
|
||
if (horizontal) {
|
||
paneSize = panePtr->width;
|
||
} else {
|
||
paneSize = panePtr->height;
|
||
}
|
||
if (diff > (paneSize - panePtr->minSize)) {
|
||
diff -= paneSize - panePtr->minSize;
|
||
paneSize = panePtr->minSize;
|
||
} else {
|
||
paneSize -= diff;
|
||
i = reduceLast - reduceIncr;
|
||
}
|
||
if (horizontal) {
|
||
panePtr->paneWidth = panePtr->width = paneSize;
|
||
} else {
|
||
panePtr->paneHeight = panePtr->height = paneSize;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* ProxyWindowEventProc --
|
||
*
|
||
* This function 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 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, pwPtr);
|
||
pwPtr->flags |= PROXY_REDRAW_PENDING;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* DisplayProxyWindow --
|
||
*
|
||
* This function 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) /* 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;
|
||
}
|
||
|
||
#ifndef TK_NO_DOUBLE_BUFFERING
|
||
/*
|
||
* 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));
|
||
#else
|
||
pixmap = Tk_WindowId(tkwin);
|
||
#endif /* TK_NO_DOUBLE_BUFFERING */
|
||
|
||
/*
|
||
* Redraw the widget's background and border.
|
||
*/
|
||
|
||
Tk_Fill3DRectangle(tkwin, pixmap,
|
||
pwPtr->proxyBackground ? pwPtr->proxyBackground : pwPtr->background,
|
||
0, 0, Tk_Width(tkwin), Tk_Height(tkwin), pwPtr->proxyBorderWidth,
|
||
(pwPtr->proxyRelief != TK_RELIEF_NULL) ? pwPtr->proxyRelief : pwPtr->sashRelief);
|
||
|
||
#ifndef TK_NO_DOUBLE_BUFFERING
|
||
/*
|
||
* 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);
|
||
#endif /* TK_NO_DOUBLE_BUFFERING */
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* 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(
|
||
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 *const optionStrings[] = {
|
||
"coord", "forget", "place", NULL
|
||
};
|
||
enum options {
|
||
PROXY_COORD, PROXY_FORGET, PROXY_PLACE
|
||
};
|
||
int index, x, y, sashWidth, sashHeight;
|
||
int internalBW, pwWidth, pwHeight;
|
||
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_SetObjResult(interp, Tcl_NewListObj(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;
|
||
}
|
||
|
||
internalBW = Tk_InternalBorderLeft(pwPtr->tkwin);
|
||
if (pwPtr->orient == ORIENT_HORIZONTAL) {
|
||
if (x < 0) {
|
||
x = 0;
|
||
}
|
||
pwWidth = Tk_Width(pwPtr->tkwin) - (2 * internalBW);
|
||
if (x > pwWidth) {
|
||
x = pwWidth;
|
||
}
|
||
y = Tk_InternalBorderLeft(pwPtr->tkwin);
|
||
sashWidth = pwPtr->sashWidth;
|
||
sashHeight = Tk_Height(pwPtr->tkwin) -
|
||
(2 * Tk_InternalBorderLeft(pwPtr->tkwin));
|
||
} else {
|
||
if (y < 0) {
|
||
y = 0;
|
||
}
|
||
pwHeight = Tk_Height(pwPtr->tkwin) - (2 * internalBW);
|
||
if (y > pwHeight) {
|
||
y = pwHeight;
|
||
}
|
||
x = Tk_InternalBorderLeft(pwPtr->tkwin);
|
||
sashHeight = pwPtr->sashWidth;
|
||
sashWidth = Tk_Width(pwPtr->tkwin) -
|
||
(2 * Tk_InternalBorderLeft(pwPtr->tkwin));
|
||
}
|
||
|
||
if (sashWidth < 1) {
|
||
sashWidth = 1;
|
||
}
|
||
if (sashHeight < 1) {
|
||
sashHeight = 1;
|
||
}
|
||
|
||
/*
|
||
* 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
|
||
* panes, 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 pane, 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 function 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(
|
||
Tcl_Obj *objPtr) /* Object to test. May be NULL. */
|
||
{
|
||
if (objPtr == NULL) {
|
||
return 1;
|
||
}
|
||
if (objPtr->bytes == NULL) {
|
||
Tcl_GetString(objPtr);
|
||
}
|
||
return (objPtr->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(
|
||
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(
|
||
PanedWindow *pwPtr, /* Information about the widget. */
|
||
Tcl_Interp *interp, /* Interpreter in which to store result. */
|
||
int x, int y) /* Coordinates of the point to identify. */
|
||
{
|
||
int i, sashHeight, sashWidth, thisx, thisy;
|
||
int found, isHandle, lpad, rpad, tpad, bpad;
|
||
int first, last;
|
||
|
||
if (pwPtr->orient == ORIENT_HORIZONTAL) {
|
||
if (Tk_IsMapped(pwPtr->tkwin)) {
|
||
sashHeight = Tk_Height(pwPtr->tkwin);
|
||
} else {
|
||
sashHeight = Tk_ReqHeight(pwPtr->tkwin);
|
||
}
|
||
sashHeight -= 2 * Tk_InternalBorderLeft(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_InternalBorderLeft(pwPtr->tkwin);
|
||
lpad = rpad = 0;
|
||
}
|
||
|
||
GetFirstLastVisiblePane(pwPtr, &first, &last);
|
||
isHandle = 0;
|
||
found = -1;
|
||
for (i = 0; i < pwPtr->numPanes - 1; i++) {
|
||
if (pwPtr->panes[i]->hide || i == last) {
|
||
continue;
|
||
}
|
||
thisx = pwPtr->panes[i]->sashx;
|
||
thisy = pwPtr->panes[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->panes[i]->handlex;
|
||
thisy = pwPtr->panes[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. Note that the empty string is the default (this function
|
||
* is called inside the implementation of a command).
|
||
*/
|
||
|
||
if (found != -1) {
|
||
Tcl_Obj *list[2];
|
||
|
||
list[0] = Tcl_NewIntObj(found);
|
||
list[1] = Tcl_NewStringObj((isHandle ? "handle" : "sash"), -1);
|
||
Tcl_SetObjResult(interp, Tcl_NewListObj(2, list));
|
||
}
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
* Local Variables:
|
||
* mode: c
|
||
* c-basic-offset: 4
|
||
* fill-column: 78
|
||
* End:
|
||
*/
|