2053 lines
57 KiB
C
2053 lines
57 KiB
C
|
/*
|
||
|
* tkGrid.c --
|
||
|
*
|
||
|
* Grid based geometry manager.
|
||
|
*
|
||
|
* Copyright (c) 1996 by Sun Microsystems, Inc.
|
||
|
*
|
||
|
* See the file "license.terms" for information on usage and redistribution
|
||
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||
|
*
|
||
|
*
|
||
|
* SCCS: @(#) tkGrid.c 1.21 96/02/21 10:50:58
|
||
|
*/
|
||
|
|
||
|
#include "tkInt.h"
|
||
|
|
||
|
/*
|
||
|
* LayoutInfo structure. We shouldn't be using hard-wired limits!
|
||
|
*/
|
||
|
|
||
|
#define MAXGRIDSIZE 128
|
||
|
#ifndef MAXINT
|
||
|
# define MAXINT 0x7fff
|
||
|
#endif
|
||
|
#define MINWEIGHT 0.0001 /* weight totals < this are considered to be zero */
|
||
|
|
||
|
/*
|
||
|
* Special characters to support relative layouts
|
||
|
*/
|
||
|
|
||
|
#define REL_SKIP 'x' /* skip this column */
|
||
|
#define REL_HORIZ '-' /* extend previous widget horizontally */
|
||
|
#define REL_VERT '^' /* extend previous widget verticallly */
|
||
|
|
||
|
/*
|
||
|
* structure to hold collected constraints temporarily:
|
||
|
* needs to use a "Constrain" thingy
|
||
|
*/
|
||
|
|
||
|
typedef struct {
|
||
|
int width, height; /* number of cells horizontally, vertically */
|
||
|
int lastRow; /* last cell with a window in it */
|
||
|
int minWidth[MAXGRIDSIZE]; /* largest minWidth in each column */
|
||
|
int minHeight[MAXGRIDSIZE]; /* largest minHeight in each row */
|
||
|
double weightX[MAXGRIDSIZE]; /* largest weight in each column */
|
||
|
double weightY[MAXGRIDSIZE]; /* largest weight in each row */
|
||
|
} LayoutInfo;
|
||
|
|
||
|
/* structure for holding row and column constraints */
|
||
|
|
||
|
typedef struct {
|
||
|
int used; /* maximum element used */
|
||
|
int max; /* maximum element allocated */
|
||
|
int *minsize; /* array of minimum column/row sizes */
|
||
|
double *weight; /* array of column/row weights */
|
||
|
} Constrain;
|
||
|
|
||
|
/* For each window that the gridbag cares about (either because
|
||
|
* the window is managed by the gridbag or because the window
|
||
|
* has slaves that are managed by the gridbag), there is a
|
||
|
* structure of the following type:
|
||
|
*/
|
||
|
|
||
|
typedef struct GridBag {
|
||
|
Tk_Window tkwin; /* Tk token for window. NULL means that
|
||
|
* the window has been deleted, but the
|
||
|
* packet hasn't had a chance to clean up
|
||
|
* yet because the structure is still in
|
||
|
* use. */
|
||
|
struct GridBag *masterPtr; /* Master window within which this window
|
||
|
* is managed (NULL means this window
|
||
|
* isn't managed by the gridbag). */
|
||
|
struct GridBag *nextPtr; /* Next window managed within same
|
||
|
* parent. List is priority-ordered:
|
||
|
* first on list gets layed out first. */
|
||
|
struct GridBag *slavePtr; /* First in list of slaves managed
|
||
|
* inside this window (NULL means
|
||
|
* no gridbag slaves). */
|
||
|
|
||
|
int gridColumn, gridRow;
|
||
|
int gridWidth, gridHeight;
|
||
|
|
||
|
int tempX, tempY;
|
||
|
int tempWidth, tempHeight;
|
||
|
|
||
|
double weightX, weightY;
|
||
|
int minWidth, minHeight;
|
||
|
|
||
|
int padX, padY; /* Total additional pixels to leave around the
|
||
|
* window (half of this space is left on each
|
||
|
* side). This is space *outside* the window:
|
||
|
* we'll allocate extra space in frame but
|
||
|
* won't enlarge window). */
|
||
|
int iPadX, iPadY; /* Total extra pixels to allocate inside the
|
||
|
* window (half this amount will appear on
|
||
|
* each side). */
|
||
|
int startx, starty; /* starting location of layout */
|
||
|
|
||
|
int doubleBw; /* Twice the window's last known border
|
||
|
* width. If this changes, the window
|
||
|
* must be re-arranged within its parent. */
|
||
|
int *abortPtr; /* If non-NULL, it means that there is a nested
|
||
|
* call to ArrangeGrid already working on
|
||
|
* this window. *abortPtr may be set to 1 to
|
||
|
* abort that nested call. This happens, for
|
||
|
* example, if tkwin or any of its slaves
|
||
|
* is deleted. */
|
||
|
int flags; /* Miscellaneous flags; see below
|
||
|
* for definitions. */
|
||
|
|
||
|
Constrain row, column; /* column and row constraints */
|
||
|
|
||
|
int valid;
|
||
|
LayoutInfo *layoutCache;
|
||
|
} GridBag;
|
||
|
|
||
|
/*
|
||
|
* Flag values for GridBag structures:
|
||
|
*
|
||
|
* REQUESTED_RELAYOUT: 1 means a Tk_DoWhenIdle request
|
||
|
* has already been made to re-arrange
|
||
|
* all the slaves of this window.
|
||
|
* STICK_NORTH 1 means this window sticks to the edgth of its
|
||
|
* STICK_EAST cavity
|
||
|
* STICK_SOUTH
|
||
|
* STICK_WEST
|
||
|
*
|
||
|
* DONT_PROPAGATE: 1 means don't set this window's requested
|
||
|
* size. 0 means if this window is a master
|
||
|
* then Tk will set its requested size to fit
|
||
|
* the needs of its slaves.
|
||
|
*/
|
||
|
|
||
|
#define STICK_NORTH 1
|
||
|
#define STICK_EAST 2
|
||
|
#define STICK_SOUTH 4
|
||
|
#define STICK_WEST 8
|
||
|
#define STICK_ALL (STICK_NORTH|STICK_EAST|STICK_SOUTH|STICK_WEST)
|
||
|
|
||
|
#define REQUESTED_RELAYOUT 16
|
||
|
#define DONT_PROPAGATE 32
|
||
|
|
||
|
/*
|
||
|
* Hash table used to map from Tk_Window tokens to corresponding
|
||
|
* GridBag structures:
|
||
|
*/
|
||
|
|
||
|
static Tcl_HashTable gridBagHashTable;
|
||
|
|
||
|
/*
|
||
|
* Have statics in this module been initialized?
|
||
|
*/
|
||
|
|
||
|
static initialized = 0;
|
||
|
|
||
|
/*
|
||
|
* Prototypes for procedures used only in this file:
|
||
|
*/
|
||
|
|
||
|
static void ArrangeGrid _ANSI_ARGS_((ClientData clientData));
|
||
|
static int ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
|
||
|
Tk_Window tkwin, int argc, char *argv[]));
|
||
|
static void DestroyGridBag _ANSI_ARGS_((char *memPtr));
|
||
|
static void GetCachedLayoutInfo _ANSI_ARGS_((GridBag *masterPtr));
|
||
|
static GridBag * GetGridBag _ANSI_ARGS_((Tk_Window tkwin));
|
||
|
static void GetLayoutInfo _ANSI_ARGS_((GridBag *masterPtr,
|
||
|
LayoutInfo *r));
|
||
|
static void GetMinSize _ANSI_ARGS_((GridBag *masterPtr,
|
||
|
LayoutInfo *info, int *minw, int *minh));
|
||
|
static void GridBagStructureProc _ANSI_ARGS_((
|
||
|
ClientData clientData, XEvent *eventPtr));
|
||
|
static void GridLostSlaveProc _ANSI_ARGS_((ClientData clientData,
|
||
|
Tk_Window tkwin));
|
||
|
static void GridReqProc _ANSI_ARGS_((ClientData clientData,
|
||
|
Tk_Window tkwin));
|
||
|
static void GridBagStructureProc _ANSI_ARGS_((
|
||
|
ClientData clientData, XEvent *eventPtr));
|
||
|
static void StickyToString _ANSI_ARGS_((int flags, char *result));
|
||
|
static int StringToSticky _ANSI_ARGS_((char *string));
|
||
|
static void Unlink _ANSI_ARGS_((GridBag *gridPtr));
|
||
|
|
||
|
static Tk_GeomMgr gridMgrType = {
|
||
|
"grid", /* name */
|
||
|
GridReqProc, /* requestProc */
|
||
|
GridLostSlaveProc, /* lostSlaveProc */
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------
|
||
|
*
|
||
|
* Tk_GridCmd --
|
||
|
*
|
||
|
* This procedure is invoked to process the "grid" Tcl command.
|
||
|
* See the user documentation for details on what it does.
|
||
|
*
|
||
|
* Results:
|
||
|
* A standard Tcl result.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* See the user documentation.
|
||
|
*
|
||
|
*--------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
int
|
||
|
Tk_GridCmd(clientData, interp, argc, argv)
|
||
|
ClientData clientData; /* Main window associated with
|
||
|
* interpreter. */
|
||
|
Tcl_Interp *interp; /* Current interpreter. */
|
||
|
int argc; /* Number of arguments. */
|
||
|
char **argv; /* Argument strings. */
|
||
|
{
|
||
|
Tk_Window tkwin = (Tk_Window) clientData;
|
||
|
size_t length;
|
||
|
char c;
|
||
|
|
||
|
if ((argc >= 2) && (argv[1][0] == '.')) {
|
||
|
return ConfigureSlaves(interp, tkwin, argc-1, argv+1);
|
||
|
}
|
||
|
if (argc < 3) {
|
||
|
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
|
argv[0], " option arg ?arg ...?\"", (char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
c = argv[1][0];
|
||
|
length = strlen(argv[1]);
|
||
|
|
||
|
if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
|
||
|
Tk_Window master;
|
||
|
GridBag *masterPtr;
|
||
|
int row, column;
|
||
|
int i, x, y;
|
||
|
int prevX, prevY;
|
||
|
int width, height;
|
||
|
double weight;
|
||
|
int diff;
|
||
|
|
||
|
if (argc != 5) {
|
||
|
Tcl_AppendResult(interp, "Wrong number of arguments: ",
|
||
|
"must be \"",argv[0],
|
||
|
" bbox <master> <column> <row>\"", (char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
|
||
|
master = Tk_NameToWindow(interp, argv[2], tkwin);
|
||
|
if (master == NULL) {
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
if (Tcl_GetInt(interp, argv[3], &column) != TCL_OK) {
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
if (Tcl_GetInt(interp, argv[4], &row) != TCL_OK) {
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
masterPtr = GetGridBag(master);
|
||
|
|
||
|
/* make sure the grid is up to snuff */
|
||
|
|
||
|
while ((masterPtr->flags & REQUESTED_RELAYOUT)) {
|
||
|
Tk_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
|
||
|
ArrangeGrid((ClientData) masterPtr);
|
||
|
}
|
||
|
GetCachedLayoutInfo(masterPtr);
|
||
|
|
||
|
if (row < 0 || column < 0) {
|
||
|
*interp->result = '\0';
|
||
|
return TCL_OK;
|
||
|
}
|
||
|
if (column >= masterPtr->layoutCache->width ||
|
||
|
row >= masterPtr->layoutCache->height) {
|
||
|
*interp->result = '\0';
|
||
|
return TCL_OK;
|
||
|
}
|
||
|
x = masterPtr->startx;
|
||
|
y = masterPtr->starty;
|
||
|
GetMinSize(masterPtr, masterPtr->layoutCache, &width, &height);
|
||
|
|
||
|
diff = Tk_Width(masterPtr->tkwin) - (width + masterPtr->iPadX);
|
||
|
for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++)
|
||
|
weight += masterPtr->layoutCache->weightX[i];
|
||
|
|
||
|
prevX = 0; /* Needed to prevent gcc warning. */
|
||
|
for (i=0; i<=column; i++) {
|
||
|
int dx = 0;
|
||
|
if (weight > MINWEIGHT) {
|
||
|
dx = (int)((((double)diff) * masterPtr->layoutCache->weightX[i])
|
||
|
/ weight);
|
||
|
}
|
||
|
prevX = x;
|
||
|
x += masterPtr->layoutCache->minWidth[i] + dx;
|
||
|
}
|
||
|
diff = Tk_Height(masterPtr->tkwin) - (height + masterPtr->iPadY);
|
||
|
for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++) {
|
||
|
weight += masterPtr->layoutCache->weightY[i];
|
||
|
}
|
||
|
prevY = 0; /* Needed to prevent gcc warning. */
|
||
|
for (i=0; i<=row; i++) {
|
||
|
int dy = 0;
|
||
|
if (weight > MINWEIGHT) {
|
||
|
dy = (int)((((double)diff) * masterPtr->layoutCache->weightY[i])
|
||
|
/ weight);
|
||
|
}
|
||
|
prevY = y;
|
||
|
y += masterPtr->layoutCache->minHeight[i] + dy;
|
||
|
}
|
||
|
sprintf(interp->result,"%d %d %d %d",prevX,prevY,x - prevX,y - prevY);
|
||
|
} else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
|
||
|
if (argv[2][0] != '.') {
|
||
|
Tcl_AppendResult(interp, "bad argument \"", argv[2],
|
||
|
"\": must be name of window", (char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
return ConfigureSlaves(interp, tkwin, argc-2, argv+2);
|
||
|
} else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
|
||
|
Tk_Window slave;
|
||
|
GridBag *slavePtr;
|
||
|
int i;
|
||
|
|
||
|
for (i = 2; i < argc; i++) {
|
||
|
slave = Tk_NameToWindow(interp, argv[i], tkwin);
|
||
|
if (slave == NULL) {
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
slavePtr = GetGridBag(slave);
|
||
|
if (slavePtr->masterPtr != NULL) {
|
||
|
Tk_ManageGeometry(slave, (Tk_GeomMgr *) NULL,
|
||
|
(ClientData) NULL);
|
||
|
Unlink(slavePtr);
|
||
|
Tk_UnmapWindow(slavePtr->tkwin);
|
||
|
}
|
||
|
}
|
||
|
} else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
|
||
|
register GridBag *slavePtr;
|
||
|
Tk_Window slave;
|
||
|
char buffer[64];
|
||
|
|
||
|
if (argc != 3) {
|
||
|
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
|
argv[0], " info window\"", (char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
slave = Tk_NameToWindow(interp, argv[2], tkwin);
|
||
|
if (slave == NULL) {
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
slavePtr = GetGridBag(slave);
|
||
|
if (slavePtr->masterPtr == NULL) {
|
||
|
interp->result[0] = '\0';
|
||
|
return TCL_OK;
|
||
|
}
|
||
|
|
||
|
Tcl_AppendElement(interp, "-in");
|
||
|
Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin));
|
||
|
sprintf(buffer, " -column %d -row %d -columnspan %d -rowspan %d",
|
||
|
slavePtr->gridColumn, slavePtr->gridRow,
|
||
|
slavePtr->gridWidth, slavePtr->gridHeight);
|
||
|
Tcl_AppendResult(interp, buffer, (char *) NULL);
|
||
|
sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
|
||
|
slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
|
||
|
slavePtr->padY/2);
|
||
|
Tcl_AppendResult(interp, buffer, (char *) NULL);
|
||
|
StickyToString(slavePtr->flags,buffer);
|
||
|
Tcl_AppendResult(interp, " -sticky ", buffer, (char *) NULL);
|
||
|
/*
|
||
|
sprintf(buffer, " -weightx %.2f -weighty %.2f",
|
||
|
slavePtr->weightX, slavePtr->weightY);
|
||
|
Tcl_AppendResult(interp, buffer, (char *) NULL);
|
||
|
*/
|
||
|
} else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
|
||
|
Tk_Window master;
|
||
|
GridBag *masterPtr;
|
||
|
int propagate;
|
||
|
|
||
|
if (argc > 4) {
|
||
|
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
|
argv[0], " propagate window ?boolean?\"",
|
||
|
(char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
master = Tk_NameToWindow(interp, argv[2], tkwin);
|
||
|
if (master == NULL) {
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
masterPtr = GetGridBag(master);
|
||
|
if (argc == 3) {
|
||
|
#ifdef STk_CODE
|
||
|
interp->result = (masterPtr->flags & DONT_PROPAGATE) ? "#f" : "#t";
|
||
|
#else
|
||
|
interp->result = (masterPtr->flags & DONT_PROPAGATE) ? "0" : "1";
|
||
|
#endif
|
||
|
return TCL_OK;
|
||
|
}
|
||
|
if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
if (propagate) {
|
||
|
masterPtr->flags &= ~DONT_PROPAGATE;
|
||
|
|
||
|
/*
|
||
|
* Re-arrange the master to allow new geometry information to
|
||
|
* propagate upwards to the master\'s master.
|
||
|
*/
|
||
|
|
||
|
if (masterPtr->abortPtr != NULL) {
|
||
|
*masterPtr->abortPtr = 1;
|
||
|
}
|
||
|
masterPtr->valid = 0;
|
||
|
if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
|
||
|
masterPtr->flags |= REQUESTED_RELAYOUT;
|
||
|
Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
|
||
|
}
|
||
|
} else {
|
||
|
masterPtr->flags |= DONT_PROPAGATE;
|
||
|
}
|
||
|
} else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)) {
|
||
|
Tk_Window master;
|
||
|
GridBag *masterPtr;
|
||
|
|
||
|
if (argc != 3) {
|
||
|
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
|
argv[0], " size window\"", (char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
master = Tk_NameToWindow(interp, argv[2], tkwin);
|
||
|
if (master == NULL)
|
||
|
return TCL_ERROR;
|
||
|
masterPtr = GetGridBag(master);
|
||
|
GetCachedLayoutInfo(masterPtr);
|
||
|
|
||
|
sprintf(interp->result, "%d %d", masterPtr->layoutCache->width,
|
||
|
masterPtr->layoutCache->height);
|
||
|
} else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
|
||
|
Tk_Window master;
|
||
|
GridBag *masterPtr, *slavePtr;
|
||
|
int i, value;
|
||
|
int row = -1, column = -1;
|
||
|
|
||
|
if (argc < 3 || argc%2 ==0) {
|
||
|
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
|
argv[0], " slaves window ?-option value...?\"",
|
||
|
(char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
|
||
|
for (i=3; i<argc; i+=2) {
|
||
|
if (*argv[i] != '-' || (length = strlen(argv[i])) < 2) {
|
||
|
Tcl_AppendResult(interp, "Invalid args: should be \"",
|
||
|
argv[0], " slaves window ?-option value...?\"",
|
||
|
(char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
if (Tcl_GetInt(interp, argv[i+1], &value) != TCL_OK) {
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
if (value < 0) {
|
||
|
Tcl_AppendResult(interp, argv[i],
|
||
|
" is an invalid value: should NOT be < 0",
|
||
|
(char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
if (strncmp(argv[i], "-column", length) == 0) {
|
||
|
column = value;
|
||
|
} else if (strncmp(argv[i], "-row", length) == 0) {
|
||
|
row = value;
|
||
|
} else {
|
||
|
Tcl_AppendResult(interp, argv[i],
|
||
|
" is an invalid option: should be \"",
|
||
|
"-row, -column\"",
|
||
|
(char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
master = Tk_NameToWindow(interp, argv[2], tkwin);
|
||
|
if (master == NULL) {
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
masterPtr = GetGridBag(master);
|
||
|
#ifdef STk_CODE
|
||
|
Tcl_AppendResult(interp, "(", NULL);
|
||
|
#endif
|
||
|
for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
|
||
|
slavePtr = slavePtr->nextPtr) {
|
||
|
if (column>=0 && (slavePtr->gridColumn > column
|
||
|
|| slavePtr->gridColumn+slavePtr->gridWidth-1 < column)) {
|
||
|
continue;
|
||
|
}
|
||
|
if (row>=0 && (slavePtr->gridRow > row ||
|
||
|
slavePtr->gridRow+slavePtr->gridHeight-1 < row)) {
|
||
|
continue;
|
||
|
}
|
||
|
Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin));
|
||
|
}
|
||
|
#ifdef STk_CODE
|
||
|
Tcl_AppendResult(interp, ")", NULL);
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* grid columnconfigure <master> <index> -option
|
||
|
* grid columnconfigure <master> <index> -option value -option value
|
||
|
* grid rowconfigure <master> <index> -option
|
||
|
* grid rowconfigure <master> <index> -option value -option value
|
||
|
*/
|
||
|
|
||
|
} else if(((c=='c') && (strncmp(argv[1], "columnconfigure", length) == 0)) ||
|
||
|
((c=='r') && (strncmp(argv[1], "rowconfigure", length) == 0))) {
|
||
|
Tk_Window master;
|
||
|
GridBag *masterPtr;
|
||
|
Constrain *con;
|
||
|
int index, i, size;
|
||
|
double weight;
|
||
|
|
||
|
if (argc != 5 && (argc < 5 || argc%2 == 1)) {
|
||
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||
|
" ", argv[1], " master index ?-option value...?\"",
|
||
|
(char *)NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
|
||
|
master = Tk_NameToWindow(interp, argv[2], tkwin);
|
||
|
if (master == NULL) {
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
masterPtr = GetGridBag(master);
|
||
|
con = (c=='c') ? &(masterPtr->column) : &(masterPtr->row);
|
||
|
|
||
|
if (Tcl_GetInt(interp, argv[3], &index) != TCL_OK) {
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
if (index < 0 || index >= MAXGRIDSIZE) {
|
||
|
Tcl_AppendResult(interp, argv[3], " is out of range",
|
||
|
(char *)NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* make sure the row/column constraint array is allocated. This
|
||
|
* Should be changed to avoid hard-wired limits. We'll wimp out
|
||
|
* for now.
|
||
|
*/
|
||
|
|
||
|
if (con->max == 0) {
|
||
|
unsigned int size;
|
||
|
con->max = MAXGRIDSIZE;
|
||
|
con->used = 0;
|
||
|
|
||
|
size = MAXGRIDSIZE * sizeof(con->minsize[0]);
|
||
|
con->minsize = (int *) ckalloc(size);
|
||
|
memset(con->minsize, 0, size);
|
||
|
|
||
|
size = MAXGRIDSIZE * sizeof(con->weight[0]);
|
||
|
con->weight = (double *) ckalloc(size);
|
||
|
memset(con->weight, 0, size);
|
||
|
}
|
||
|
|
||
|
for (i=4; i<argc; i+=2) {
|
||
|
if (*argv[i] != '-' || (length = strlen(argv[i])) < 2) {
|
||
|
Tcl_AppendResult(interp, "Invalid arg: \"",
|
||
|
argv[0], "\" expecting -minsize or -weight",
|
||
|
(char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
if (strncmp(argv[i], "-minsize", length) == 0) {
|
||
|
if (argc == 5) {
|
||
|
size = con->used <= index ? 0 : con->minsize[index];
|
||
|
sprintf(interp->result, "%d", size);
|
||
|
} else if (Tk_GetPixels(interp, master, argv[i+1], &size)
|
||
|
!= TCL_OK) {
|
||
|
return TCL_ERROR;
|
||
|
} else {
|
||
|
con->minsize[index] = size;
|
||
|
if (size > 0 && index >= con->used)
|
||
|
con->used = index+1;
|
||
|
else if (size == 0 && index+1 == con->used) {
|
||
|
while (index >= 0 && (con->minsize[index]==0) &&
|
||
|
(con->weight[index] == 0.0)) {
|
||
|
index--;
|
||
|
}
|
||
|
con->used = index + 1;
|
||
|
}
|
||
|
}
|
||
|
} else if (strncmp(argv[i], "-weight", length) == 0) {
|
||
|
if (argc == 5) {
|
||
|
weight = con->used <= index ? 0 : con->weight[index];
|
||
|
sprintf(interp->result, "%.2f", weight);
|
||
|
} else if (Tcl_GetDouble(interp, argv[i+1], &weight) != TCL_OK) {
|
||
|
return TCL_ERROR;
|
||
|
} else {
|
||
|
con->weight[index] = weight;
|
||
|
if (weight > MINWEIGHT && index >= con->used)
|
||
|
con->used = index+1;
|
||
|
else if (weight == 0.0 && index+1 == con->used) {
|
||
|
while (index >= 0 && (con->minsize[index]==0) &&
|
||
|
(con->weight[index] == 0.0)) {
|
||
|
index--;
|
||
|
}
|
||
|
con->used = index + 1;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
Tcl_AppendResult(interp, argv[i],
|
||
|
" is an invalid option: should be \"",
|
||
|
"-minsize, -weight\"",
|
||
|
(char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* if we changed a property, re-arrange the table */
|
||
|
|
||
|
if (argc != 5) {
|
||
|
if (masterPtr->abortPtr != NULL) {
|
||
|
*masterPtr->abortPtr = 1;
|
||
|
}
|
||
|
masterPtr->valid = 0;
|
||
|
if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
|
||
|
masterPtr->flags |= REQUESTED_RELAYOUT;
|
||
|
Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
|
||
|
}
|
||
|
}
|
||
|
} else if((c == 'l') && (strncmp(argv[1], "location", length) == 0)) {
|
||
|
Tk_Window master;
|
||
|
GridBag *masterPtr;
|
||
|
int x, y, i, j, w, h;
|
||
|
int width, height;
|
||
|
double weight;
|
||
|
int diff;
|
||
|
|
||
|
if (argc != 5) {
|
||
|
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
|
argv[0], " location master x y\"", (char *)NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
|
||
|
master = Tk_NameToWindow(interp, argv[2], tkwin);
|
||
|
if (master == NULL) {
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
masterPtr = GetGridBag(master);
|
||
|
|
||
|
if (Tk_GetPixels(interp, master, argv[3], &x) != TCL_OK) {
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
if (Tk_GetPixels(interp, master, argv[4], &y) != TCL_OK) {
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
|
||
|
/* make sure the grid is up to snuff */
|
||
|
|
||
|
while ((masterPtr->flags & REQUESTED_RELAYOUT)) {
|
||
|
Tk_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
|
||
|
ArrangeGrid((ClientData) masterPtr);
|
||
|
}
|
||
|
GetCachedLayoutInfo(masterPtr);
|
||
|
GetMinSize(masterPtr, masterPtr->layoutCache, &width, &height);
|
||
|
|
||
|
diff = Tk_Width(masterPtr->tkwin) - (width + masterPtr->iPadX);
|
||
|
for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++) {
|
||
|
weight += masterPtr->layoutCache->weightX[i];
|
||
|
}
|
||
|
w = masterPtr->startx;
|
||
|
if (w > x) {
|
||
|
i = -1;
|
||
|
} else {
|
||
|
for (i=0; i<masterPtr->layoutCache->width; i++) {
|
||
|
int dx = 0;
|
||
|
if (weight > MINWEIGHT) {
|
||
|
dx = (int)((((double)diff) * masterPtr->layoutCache->weightX[i])
|
||
|
/ weight);
|
||
|
}
|
||
|
w += masterPtr->layoutCache->minWidth[i] + dx;
|
||
|
if (w > x) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
diff = Tk_Height(masterPtr->tkwin) - (height + masterPtr->iPadY);
|
||
|
for (weight=0.0, j = 0; j < masterPtr->layoutCache->height; j++)
|
||
|
weight += masterPtr->layoutCache->weightY[j];
|
||
|
h = masterPtr->starty;
|
||
|
if (h > y) {
|
||
|
j = -1;
|
||
|
} else {
|
||
|
for (j=0; j<masterPtr->layoutCache->height; j++) {
|
||
|
int dy = 0;
|
||
|
if (weight > MINWEIGHT) {
|
||
|
dy = (int)((((double)diff) * masterPtr->layoutCache->weightY[j])
|
||
|
/ weight);
|
||
|
}
|
||
|
h += masterPtr->layoutCache->minHeight[j] + dy;
|
||
|
if (h > y) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
sprintf(interp->result, "%d %d", i, j);
|
||
|
} else {
|
||
|
Tcl_AppendResult(interp, "bad option \"", argv[1],
|
||
|
"\": must be bbox, columnconfigure, configure, forget, info, ",
|
||
|
"location, propagate, rowconfigure, size, or slaves",
|
||
|
(char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
return TCL_OK;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------
|
||
|
*
|
||
|
* GridReqProc --
|
||
|
*
|
||
|
* This procedure is invoked by Tk_GeometryRequest for
|
||
|
* windows managed by the gridbag.
|
||
|
*
|
||
|
* Results:
|
||
|
* None.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* Arranges for tkwin, and all its managed siblings, to
|
||
|
* be re-arranged at the next idle point.
|
||
|
*
|
||
|
*--------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
/* ARGSUSED */
|
||
|
static void
|
||
|
GridReqProc(clientData, tkwin)
|
||
|
ClientData clientData; /* GridBag's information about
|
||
|
* window that got new preferred
|
||
|
* geometry. */
|
||
|
Tk_Window tkwin; /* Other Tk-related information
|
||
|
* about the window. */
|
||
|
{
|
||
|
register GridBag *gridPtr = (GridBag *) clientData;
|
||
|
|
||
|
gridPtr = gridPtr->masterPtr;
|
||
|
gridPtr->valid = 0;
|
||
|
if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
|
||
|
gridPtr->flags |= REQUESTED_RELAYOUT;
|
||
|
Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------
|
||
|
*
|
||
|
* GridLostSlaveProc --
|
||
|
*
|
||
|
* This procedure is invoked by Tk whenever some other geometry
|
||
|
* claims control over a slave that used to be managed by us.
|
||
|
*
|
||
|
* Results:
|
||
|
* None.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* Forgets all grid-related information about the slave.
|
||
|
*
|
||
|
*--------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
/* ARGSUSED */
|
||
|
static void
|
||
|
GridLostSlaveProc(clientData, tkwin)
|
||
|
ClientData clientData; /* GridBag structure for slave window that
|
||
|
* was stolen away. */
|
||
|
Tk_Window tkwin; /* Tk's handle for the slave window. */
|
||
|
{
|
||
|
register GridBag *slavePtr = (GridBag *) clientData;
|
||
|
|
||
|
if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
|
||
|
Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
|
||
|
}
|
||
|
Unlink(slavePtr);
|
||
|
Tk_UnmapWindow(slavePtr->tkwin);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Fill in an instance of the above structure for the current set
|
||
|
* of managed children. This requires two passes through the
|
||
|
* set of children, first to figure out what cells they occupy
|
||
|
* and how many rows and columns there are, and then to distribute
|
||
|
* the weights and min sizes amoung the rows/columns.
|
||
|
*
|
||
|
* This also caches the minsizes for all the children when they are
|
||
|
* first encountered.
|
||
|
*/
|
||
|
|
||
|
static void
|
||
|
GetLayoutInfo(masterPtr, r)
|
||
|
GridBag *masterPtr;
|
||
|
LayoutInfo *r;
|
||
|
{
|
||
|
register GridBag *slavePtr;
|
||
|
int i, k, px, py, pixels_diff, nextSize;
|
||
|
double weight_diff, weight;
|
||
|
register int curX, curY, curWidth, curHeight, curRow, curCol;
|
||
|
int xMax[MAXGRIDSIZE];
|
||
|
int yMax[MAXGRIDSIZE];
|
||
|
|
||
|
/*
|
||
|
* Pass #1
|
||
|
*
|
||
|
* Figure out the dimensions of the layout grid.
|
||
|
*/
|
||
|
|
||
|
r->width = r->height = 0;
|
||
|
curRow = curCol = -1;
|
||
|
memset(xMax, 0, sizeof(int) * MAXGRIDSIZE);
|
||
|
memset(yMax, 0, sizeof(int) * MAXGRIDSIZE);
|
||
|
|
||
|
for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
|
||
|
slavePtr = slavePtr->nextPtr) {
|
||
|
|
||
|
curX = slavePtr->gridColumn;
|
||
|
curY = slavePtr->gridRow;
|
||
|
curWidth = slavePtr->gridWidth;
|
||
|
curHeight = slavePtr->gridHeight;
|
||
|
|
||
|
/* Adjust the grid width and height */
|
||
|
for (px = curX + curWidth; r->width < px; r->width++) {
|
||
|
/* Null body. */
|
||
|
}
|
||
|
for (py = curY + curHeight; r->height < py; r->height++) {
|
||
|
/* Null body. */
|
||
|
}
|
||
|
|
||
|
/* Adjust the xMax and yMax arrays */
|
||
|
for (i = curX; i < (curX + curWidth); i++) {
|
||
|
yMax[i] = py;
|
||
|
}
|
||
|
for (i = curY; i < (curY + curHeight); i++) {
|
||
|
xMax[i] = px;
|
||
|
}
|
||
|
|
||
|
/* Cache the current slave's size. */
|
||
|
slavePtr->minWidth = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw;
|
||
|
slavePtr->minHeight = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Apply minimum row/column dimensions
|
||
|
*/
|
||
|
if (r->width < masterPtr->column.used) {
|
||
|
r->width = masterPtr->column.used;
|
||
|
}
|
||
|
r->lastRow = r->height;
|
||
|
if (r->height < masterPtr->row.used) {
|
||
|
r->height = masterPtr->row.used;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Pass #2
|
||
|
*/
|
||
|
|
||
|
curRow = curCol = -1;
|
||
|
memset(xMax, 0, sizeof(int) * MAXGRIDSIZE);
|
||
|
memset(yMax, 0, sizeof(int) * MAXGRIDSIZE);
|
||
|
|
||
|
for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
|
||
|
slavePtr = slavePtr->nextPtr) {
|
||
|
curX = slavePtr->gridColumn;
|
||
|
curY = slavePtr->gridRow;
|
||
|
curWidth = slavePtr->gridWidth;
|
||
|
curHeight = slavePtr->gridHeight;
|
||
|
|
||
|
px = curX + curWidth;
|
||
|
py = curY + curHeight;
|
||
|
|
||
|
for (i = curX; i < (curX + curWidth); i++) {
|
||
|
yMax[i] = py;
|
||
|
}
|
||
|
for (i = curY; i < (curY + curHeight); i++) {
|
||
|
xMax[i] = px;
|
||
|
}
|
||
|
|
||
|
/* Assign the new values to the gridbag slave */
|
||
|
slavePtr->tempX = curX;
|
||
|
slavePtr->tempY = curY;
|
||
|
slavePtr->tempWidth = curWidth;
|
||
|
slavePtr->tempHeight = curHeight;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Pass #3
|
||
|
*
|
||
|
* Distribute the minimun widths and weights:
|
||
|
*/
|
||
|
|
||
|
/* Initialize arrays to zero */
|
||
|
memset(r->minWidth, 0, r->width * sizeof(int));
|
||
|
memset(r->minHeight, 0, r->height * sizeof(int));
|
||
|
memset(r->weightX, 0, r->width * sizeof(double));
|
||
|
memset(r->weightY, 0, r->height * sizeof(double));
|
||
|
nextSize = MAXINT;
|
||
|
|
||
|
for (i = 1; i != MAXINT; i = nextSize, nextSize = MAXINT) {
|
||
|
for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
|
||
|
slavePtr = slavePtr->nextPtr) {
|
||
|
|
||
|
if (slavePtr->tempWidth == i) {
|
||
|
px = slavePtr->tempX + slavePtr->tempWidth; /* right column */
|
||
|
|
||
|
/*
|
||
|
* Figure out if we should use this slave\'s weight. If the weight
|
||
|
* is less than the total weight spanned by the width of the cell,
|
||
|
* then discard the weight. Otherwise split it the difference
|
||
|
* according to the existing weights.
|
||
|
*/
|
||
|
|
||
|
weight_diff = slavePtr->weightX;
|
||
|
for (k = slavePtr->tempX; k < px; k++)
|
||
|
weight_diff -= r->weightX[k];
|
||
|
if (weight_diff > 0.0) {
|
||
|
weight = 0.0;
|
||
|
for (k = slavePtr->tempX; k < px; k++)
|
||
|
weight += r->weightX[k];
|
||
|
for (k = slavePtr->tempX; weight > MINWEIGHT; k++) {
|
||
|
double wt = r->weightX[k];
|
||
|
double dx = (wt * weight_diff) / weight;
|
||
|
r->weightX[k] += dx;
|
||
|
weight_diff -= dx;
|
||
|
weight -= wt;
|
||
|
}
|
||
|
/* Assign the remainder to the rightmost cell */
|
||
|
r->weightX[px-1] += weight_diff;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Calculate the minWidth array values.
|
||
|
* First, figure out how wide the current slave needs to be.
|
||
|
* Then, see if it will fit within the current minWidth values.
|
||
|
* If it won\'t fit, add the difference according to the weightX array.
|
||
|
*/
|
||
|
|
||
|
pixels_diff = slavePtr->minWidth + slavePtr->padX + slavePtr->iPadX;
|
||
|
for (k = slavePtr->tempX; k < px; k++)
|
||
|
pixels_diff -= r->minWidth[k];
|
||
|
if (pixels_diff > 0) {
|
||
|
weight = 0.0;
|
||
|
for (k = slavePtr->tempX; k < px; k++)
|
||
|
weight += r->weightX[k];
|
||
|
for (k = slavePtr->tempX; weight > MINWEIGHT; k++) {
|
||
|
double wt = r->weightX[k];
|
||
|
int dx = (int)((wt * ((double)pixels_diff)) / weight);
|
||
|
r->minWidth[k] += dx;
|
||
|
pixels_diff -= dx;
|
||
|
weight -= wt;
|
||
|
}
|
||
|
/* Any leftovers go into the rightmost cell */
|
||
|
r->minWidth[px-1] += pixels_diff;
|
||
|
}
|
||
|
}
|
||
|
else if (slavePtr->tempWidth > i && slavePtr->tempWidth < nextSize)
|
||
|
nextSize = slavePtr->tempWidth;
|
||
|
|
||
|
|
||
|
if (slavePtr->tempHeight == i) {
|
||
|
py = slavePtr->tempY + slavePtr->tempHeight; /* bottom row */
|
||
|
|
||
|
/*
|
||
|
* Figure out if we should use this slave\'s weight. If the weight
|
||
|
* is less than the total weight spanned by the height of the cell,
|
||
|
* then discard the weight. Otherwise split it the difference
|
||
|
* according to the existing weights.
|
||
|
*/
|
||
|
|
||
|
weight_diff = slavePtr->weightY;
|
||
|
for (k = slavePtr->tempY; k < py; k++)
|
||
|
weight_diff -= r->weightY[k];
|
||
|
if (weight_diff > 0.0) {
|
||
|
weight = 0.0;
|
||
|
for (k = slavePtr->tempY; k < py; k++)
|
||
|
weight += r->weightY[k];
|
||
|
for (k = slavePtr->tempY; weight > MINWEIGHT; k++) {
|
||
|
double wt = r->weightY[k];
|
||
|
double dy = (wt * weight_diff) / weight;
|
||
|
r->weightY[k] += dy;
|
||
|
weight_diff -= dy;
|
||
|
weight -= wt;
|
||
|
}
|
||
|
/* Assign the remainder to the bottom cell */
|
||
|
r->weightY[py-1] += weight_diff;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Calculate the minHeight array values.
|
||
|
* First, figure out how tall the current slave needs to be.
|
||
|
* Then, see if it will fit within the current minHeight values.
|
||
|
* If it won\'t fit, add the difference according to the weightY array.
|
||
|
*/
|
||
|
|
||
|
pixels_diff = slavePtr->minHeight + slavePtr->padY + slavePtr->iPadY;
|
||
|
for (k = slavePtr->tempY; k < py; k++)
|
||
|
pixels_diff -= r->minHeight[k];
|
||
|
if (pixels_diff > 0) {
|
||
|
weight = 0.0;
|
||
|
for (k = slavePtr->tempY; k < py; k++)
|
||
|
weight += r->weightY[k];
|
||
|
for (k = slavePtr->tempY; weight > MINWEIGHT; k++) {
|
||
|
double wt = r->weightY[k];
|
||
|
int dy = (int)((wt * ((double)pixels_diff)) / weight);
|
||
|
r->minHeight[k] += dy;
|
||
|
pixels_diff -= dy;
|
||
|
weight -= wt;
|
||
|
}
|
||
|
/* Any leftovers go into the bottom cell */
|
||
|
r->minHeight[py-1] += pixels_diff;
|
||
|
}
|
||
|
}
|
||
|
else if (slavePtr->tempHeight > i && slavePtr->tempHeight < nextSize)
|
||
|
nextSize = slavePtr->tempHeight;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Apply minimum row/column dimensions
|
||
|
*/
|
||
|
for (i=0; i<masterPtr->column.used; i++) {
|
||
|
if (r->minWidth[i] < masterPtr->column.minsize[i])
|
||
|
r->minWidth[i] = masterPtr->column.minsize[i];
|
||
|
if (r->weightX[i] < masterPtr->column.weight[i])
|
||
|
r->weightX[i] = masterPtr->column.weight[i];
|
||
|
}
|
||
|
for (i=0; i<masterPtr->row.used; i++) {
|
||
|
if (r->minHeight[i] < masterPtr->row.minsize[i])
|
||
|
r->minHeight[i] = masterPtr->row.minsize[i];
|
||
|
if (r->weightY[i] < masterPtr->row.weight[i])
|
||
|
r->weightY[i] = masterPtr->row.weight[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Cache the layout info after it is calculated.
|
||
|
*/
|
||
|
static void
|
||
|
GetCachedLayoutInfo(masterPtr)
|
||
|
GridBag *masterPtr;
|
||
|
{
|
||
|
if (masterPtr->valid == 0) {
|
||
|
if (!masterPtr->layoutCache)
|
||
|
masterPtr->layoutCache = (LayoutInfo *)ckalloc(sizeof(LayoutInfo));
|
||
|
|
||
|
GetLayoutInfo(masterPtr, masterPtr->layoutCache);
|
||
|
masterPtr->valid = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Adjusts the x, y, width, and height fields to the correct
|
||
|
* values depending on the constraint geometry and pads.
|
||
|
*/
|
||
|
|
||
|
static void
|
||
|
AdjustForGravity(gridPtr, x, y, width, height)
|
||
|
GridBag *gridPtr;
|
||
|
int *x;
|
||
|
int *y;
|
||
|
int *width;
|
||
|
int *height;
|
||
|
{
|
||
|
int diffx=0, diffy=0;
|
||
|
int sticky = gridPtr->flags&STICK_ALL;
|
||
|
|
||
|
*x += gridPtr->padX/2;
|
||
|
*width -= gridPtr->padX;
|
||
|
*y += gridPtr->padY/2;
|
||
|
*height -= gridPtr->padY;
|
||
|
|
||
|
if (*width > (gridPtr->minWidth + gridPtr->iPadX)) {
|
||
|
diffx = *width - (gridPtr->minWidth + gridPtr->iPadX);
|
||
|
*width = gridPtr->minWidth + gridPtr->iPadX;
|
||
|
}
|
||
|
|
||
|
if (*height > (gridPtr->minHeight + gridPtr->iPadY)) {
|
||
|
diffy = *height - (gridPtr->minHeight + gridPtr->iPadY);
|
||
|
*height = gridPtr->minHeight + gridPtr->iPadY;
|
||
|
}
|
||
|
|
||
|
if (sticky&STICK_EAST && sticky&STICK_WEST)
|
||
|
*width += diffx;
|
||
|
if (sticky&STICK_NORTH && sticky&STICK_SOUTH)
|
||
|
*height += diffy;
|
||
|
if (!(sticky&STICK_WEST)) {
|
||
|
if (sticky&STICK_EAST)
|
||
|
*x += diffx;
|
||
|
else
|
||
|
*x += diffx/2;
|
||
|
}
|
||
|
if (!(sticky&STICK_NORTH)) {
|
||
|
if (sticky&STICK_SOUTH)
|
||
|
*y += diffy;
|
||
|
else
|
||
|
*y += diffy/2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Figure out the minimum size (not counting the X border) of the
|
||
|
* master based on the information from GetLayoutInfo()
|
||
|
*/
|
||
|
|
||
|
static void
|
||
|
GetMinSize(masterPtr, info, minw, minh)
|
||
|
GridBag *masterPtr;
|
||
|
LayoutInfo *info;
|
||
|
int *minw;
|
||
|
int *minh;
|
||
|
{
|
||
|
int i, t;
|
||
|
int intBWidth; /* Width of internal border in parent window,
|
||
|
* if any. */
|
||
|
|
||
|
intBWidth = Tk_InternalBorderWidth(masterPtr->tkwin);
|
||
|
|
||
|
t = 0;
|
||
|
for(i = 0; i < info->width; i++)
|
||
|
t += info->minWidth[i];
|
||
|
*minw = t + 2*intBWidth;
|
||
|
|
||
|
t = 0;
|
||
|
for(i = 0; i < info->height; i++)
|
||
|
t += info->minHeight[i];
|
||
|
*minh = t + 2*intBWidth;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------
|
||
|
*
|
||
|
* ArrangeGrid --
|
||
|
*
|
||
|
* This procedure is invoked (using the Tk_DoWhenIdle
|
||
|
* mechanism) to re-layout a set of windows managed by
|
||
|
* the gridbag. It is invoked at idle time so that a
|
||
|
* series of gridbag requests can be merged into a single
|
||
|
* layout operation.
|
||
|
*
|
||
|
* Results:
|
||
|
* None.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* The slaves of masterPtr may get resized or moved.
|
||
|
*
|
||
|
*--------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
static void
|
||
|
ArrangeGrid(clientData)
|
||
|
ClientData clientData; /* Structure describing parent whose slaves
|
||
|
* are to be re-layed out. */
|
||
|
{
|
||
|
register GridBag *masterPtr = (GridBag *) clientData;
|
||
|
register GridBag *slavePtr;
|
||
|
int abort;
|
||
|
int i, x, y, width, height;
|
||
|
int diffw, diffh;
|
||
|
double weight;
|
||
|
Tk_Window parent, ancestor;
|
||
|
LayoutInfo info;
|
||
|
int intBWidth; /* Width of internal border in parent window,
|
||
|
* if any. */
|
||
|
int iPadX, iPadY;
|
||
|
|
||
|
masterPtr->flags &= ~REQUESTED_RELAYOUT;
|
||
|
|
||
|
/*
|
||
|
* If the parent has no slaves anymore, then don't do anything
|
||
|
* at all: just leave the parent's size as-is.
|
||
|
* Even if row and column constraints have been set!
|
||
|
*/
|
||
|
|
||
|
if (masterPtr->slavePtr == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Abort any nested call to ArrangeGrid for this window, since
|
||
|
* we'll do everything necessary here, and set up so this call
|
||
|
* can be aborted if necessary.
|
||
|
*/
|
||
|
|
||
|
if (masterPtr->abortPtr != NULL) {
|
||
|
*masterPtr->abortPtr = 1;
|
||
|
}
|
||
|
masterPtr->abortPtr = &abort;
|
||
|
abort = 0;
|
||
|
Tk_Preserve((ClientData) masterPtr);
|
||
|
|
||
|
/*
|
||
|
* Pass #1: scan all the slaves to figure out the total amount
|
||
|
* of space needed.
|
||
|
*/
|
||
|
|
||
|
GetLayoutInfo(masterPtr, &info);
|
||
|
GetMinSize(masterPtr, &info, &width, &height);
|
||
|
|
||
|
if (((width != Tk_ReqWidth(masterPtr->tkwin))
|
||
|
|| (height != Tk_ReqHeight(masterPtr->tkwin)))
|
||
|
&& !(masterPtr->flags & DONT_PROPAGATE)) {
|
||
|
Tk_GeometryRequest(masterPtr->tkwin, width, height);
|
||
|
masterPtr->flags |= REQUESTED_RELAYOUT;
|
||
|
masterPtr->valid = 0;
|
||
|
Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If the parent isn't mapped then don't do anything more: wait
|
||
|
* until it gets mapped again. Need to get at least to here to
|
||
|
* reflect size needs up the window hierarchy, but there's no
|
||
|
* point in actually mapping the slaves.
|
||
|
*/
|
||
|
|
||
|
if (!Tk_IsMapped(masterPtr->tkwin)) {
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* If the current dimensions of the window don't match the desired
|
||
|
* dimensions, then adjust the minWidth and minHeight arrays
|
||
|
* according to the weights.
|
||
|
*/
|
||
|
|
||
|
diffw = Tk_Width(masterPtr->tkwin) - (width + masterPtr->iPadX);
|
||
|
if (diffw != 0) {
|
||
|
weight = 0.0;
|
||
|
for (i = 0; i < info.width; i++)
|
||
|
weight += info.weightX[i];
|
||
|
if (weight > MINWEIGHT) {
|
||
|
for (i = 0; i < info.width; i++) {
|
||
|
int dx = (int)(( ((double)diffw) * info.weightX[i]) / weight);
|
||
|
info.minWidth[i] += dx;
|
||
|
width += dx;
|
||
|
if (info.minWidth[i] < 0) {
|
||
|
width -= info.minWidth[i];
|
||
|
info.minWidth[i] = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
diffw = Tk_Width(masterPtr->tkwin) - (width + masterPtr->iPadX);
|
||
|
}
|
||
|
else {
|
||
|
diffw = 0;
|
||
|
}
|
||
|
|
||
|
diffh = Tk_Height(masterPtr->tkwin) - (height + masterPtr->iPadY);
|
||
|
if (diffh != 0) {
|
||
|
weight = 0.0;
|
||
|
for (i = 0; i < info.height; i++)
|
||
|
weight += info.weightY[i];
|
||
|
if (weight > MINWEIGHT) {
|
||
|
for (i = 0; i < info.height; i++) {
|
||
|
int dy = (int)(( ((double)diffh) * info.weightY[i]) / weight);
|
||
|
info.minHeight[i] += dy;
|
||
|
height += dy;
|
||
|
if (info.minHeight[i] < 0) {
|
||
|
height -= info.minHeight[i];
|
||
|
info.minHeight[i] = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
diffh = Tk_Height(masterPtr->tkwin) - (height + masterPtr->iPadY);
|
||
|
}
|
||
|
else {
|
||
|
diffh = 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Now do the actual layout of the slaves using the layout information
|
||
|
* that has been collected.
|
||
|
*/
|
||
|
|
||
|
iPadX = masterPtr->iPadX/2;
|
||
|
iPadY = masterPtr->iPadY/2;
|
||
|
intBWidth = Tk_InternalBorderWidth(masterPtr->tkwin);
|
||
|
|
||
|
for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
|
||
|
slavePtr = slavePtr->nextPtr) {
|
||
|
|
||
|
masterPtr->startx = x = diffw/2 + intBWidth + iPadX;
|
||
|
for(i = 0; i < slavePtr->tempX; i++)
|
||
|
x += info.minWidth[i];
|
||
|
|
||
|
masterPtr->starty = y = diffh/2 + intBWidth + iPadY;
|
||
|
for(i = 0; i < slavePtr->tempY; i++)
|
||
|
y += info.minHeight[i];
|
||
|
|
||
|
width = 0;
|
||
|
for(i = slavePtr->tempX; i < (slavePtr->tempX + slavePtr->tempWidth); i++)
|
||
|
width += info.minWidth[i];
|
||
|
|
||
|
height = 0;
|
||
|
for(i = slavePtr->tempY; i < (slavePtr->tempY + slavePtr->tempHeight); i++)
|
||
|
height += info.minHeight[i];
|
||
|
|
||
|
AdjustForGravity(slavePtr, &x, &y, &width, &height);
|
||
|
|
||
|
/*
|
||
|
* If the window in which slavePtr is managed is not its
|
||
|
* parent in the window hierarchy, translate the coordinates
|
||
|
* to the coordinate system of the real X parent.
|
||
|
*/
|
||
|
|
||
|
parent = Tk_Parent(slavePtr->tkwin);
|
||
|
for (ancestor = masterPtr->tkwin; ancestor != parent;
|
||
|
ancestor = Tk_Parent(ancestor)) {
|
||
|
x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width;
|
||
|
y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If the window is too small to be interesting then
|
||
|
* unmap it. Otherwise configure it and then make sure
|
||
|
* it's mapped.
|
||
|
*/
|
||
|
|
||
|
if ((width <= 0) || (height <= 0)) {
|
||
|
Tk_UnmapWindow(slavePtr->tkwin);
|
||
|
}
|
||
|
else {
|
||
|
if ((x != Tk_X(slavePtr->tkwin))
|
||
|
|| (y != Tk_Y(slavePtr->tkwin))
|
||
|
|| (width != Tk_Width(slavePtr->tkwin))
|
||
|
|| (height != Tk_Height(slavePtr->tkwin))) {
|
||
|
Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height);
|
||
|
}
|
||
|
if (abort) {
|
||
|
goto done;
|
||
|
}
|
||
|
Tk_MapWindow(slavePtr->tkwin);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Changes to the window's structure could cause almost anything
|
||
|
* to happen, including deleting the parent or child. If this
|
||
|
* happens, we'll be told to abort.
|
||
|
*/
|
||
|
|
||
|
if (abort) {
|
||
|
goto done;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
masterPtr->abortPtr = NULL;
|
||
|
Tk_Release((ClientData) masterPtr);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------
|
||
|
*
|
||
|
* GetGridBag --
|
||
|
*
|
||
|
* This internal procedure is used to locate a GridBag
|
||
|
* structure for a given window, creating one if one
|
||
|
* doesn't exist already.
|
||
|
*
|
||
|
* Results:
|
||
|
* The return value is a pointer to the GridBag structure
|
||
|
* corresponding to tkwin.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* A new gridbag structure may be created. If so, then
|
||
|
* a callback is set up to clean things up when the
|
||
|
* window is deleted.
|
||
|
*
|
||
|
*--------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
static GridBag *
|
||
|
GetGridBag(tkwin)
|
||
|
Tk_Window tkwin; /* Token for window for which
|
||
|
* gridbag structure is desired. */
|
||
|
{
|
||
|
register GridBag *gridPtr;
|
||
|
Tcl_HashEntry *hPtr;
|
||
|
int new;
|
||
|
|
||
|
if (!initialized) {
|
||
|
initialized = 1;
|
||
|
Tcl_InitHashTable(&gridBagHashTable, TCL_ONE_WORD_KEYS);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* See if there's already gridbag for this window. If not,
|
||
|
* then create a new one.
|
||
|
*/
|
||
|
|
||
|
hPtr = Tcl_CreateHashEntry(&gridBagHashTable, (char *) tkwin, &new);
|
||
|
if (!new) {
|
||
|
return (GridBag *) Tcl_GetHashValue(hPtr);
|
||
|
}
|
||
|
gridPtr = (GridBag *) ckalloc(sizeof(GridBag));
|
||
|
gridPtr->tkwin = tkwin;
|
||
|
gridPtr->masterPtr = NULL;
|
||
|
gridPtr->nextPtr = NULL;
|
||
|
gridPtr->slavePtr = NULL;
|
||
|
|
||
|
gridPtr->gridColumn = gridPtr->gridRow = -1;
|
||
|
gridPtr->gridWidth = gridPtr->gridHeight = 1;
|
||
|
gridPtr->weightX = gridPtr->weightY = 0.0;
|
||
|
gridPtr->minWidth = gridPtr->minHeight = 0;
|
||
|
|
||
|
gridPtr->padX = gridPtr->padY = 0;
|
||
|
gridPtr->iPadX = gridPtr->iPadY = 0;
|
||
|
gridPtr->startx = gridPtr->starty = 0;
|
||
|
gridPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
|
||
|
gridPtr->abortPtr = NULL;
|
||
|
gridPtr->flags = 0;
|
||
|
|
||
|
gridPtr->column.max = 0;
|
||
|
gridPtr->row.max = 0;
|
||
|
gridPtr->column.used = 0;
|
||
|
gridPtr->row.used = 0;
|
||
|
|
||
|
gridPtr->valid = 0;
|
||
|
gridPtr->layoutCache = NULL;
|
||
|
|
||
|
Tcl_SetHashValue(hPtr, gridPtr);
|
||
|
Tk_CreateEventHandler(tkwin, StructureNotifyMask,
|
||
|
GridBagStructureProc, (ClientData) gridPtr);
|
||
|
return gridPtr;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*----------------------------------------------------------------------
|
||
|
*
|
||
|
* Unlink --
|
||
|
*
|
||
|
* Remove a gridbag from its parent's list of slaves.
|
||
|
*
|
||
|
* Results:
|
||
|
* None.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* The parent will be scheduled for re-arranging.
|
||
|
*
|
||
|
*----------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
static void
|
||
|
Unlink(gridPtr)
|
||
|
register GridBag *gridPtr; /* Window to unlink. */
|
||
|
{
|
||
|
register GridBag *masterPtr, *gridPtr2;
|
||
|
|
||
|
masterPtr = gridPtr->masterPtr;
|
||
|
if (masterPtr == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
if (masterPtr->slavePtr == gridPtr) {
|
||
|
masterPtr->slavePtr = gridPtr->nextPtr;
|
||
|
}
|
||
|
else {
|
||
|
for (gridPtr2 = masterPtr->slavePtr; ; gridPtr2 = gridPtr2->nextPtr) {
|
||
|
if (gridPtr2 == NULL) {
|
||
|
panic("Unlink couldn't find previous window");
|
||
|
}
|
||
|
if (gridPtr2->nextPtr == gridPtr) {
|
||
|
gridPtr2->nextPtr = gridPtr->nextPtr;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
masterPtr->valid = 0;
|
||
|
if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
|
||
|
masterPtr->flags |= REQUESTED_RELAYOUT;
|
||
|
Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
|
||
|
}
|
||
|
if (masterPtr->abortPtr != NULL) {
|
||
|
*masterPtr->abortPtr = 1;
|
||
|
}
|
||
|
|
||
|
gridPtr->masterPtr = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
*----------------------------------------------------------------------
|
||
|
*
|
||
|
* DestroyGridBag --
|
||
|
*
|
||
|
* This procedure is invoked by Tk_EventuallyFree or Tk_Release
|
||
|
* to clean up the internal structure of a gridbag at a safe time
|
||
|
* (when no-one is using it anymore).
|
||
|
*
|
||
|
* Results:
|
||
|
* None.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* Everything associated with the gridbag is freed up.
|
||
|
*
|
||
|
*----------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
static void
|
||
|
DestroyGridBag(memPtr)
|
||
|
char *memPtr; /* Info about window that is now dead. */
|
||
|
{
|
||
|
register GridBag *gridPtr = (GridBag *) memPtr;
|
||
|
|
||
|
if (gridPtr->column.max) {
|
||
|
ckfree((char *) gridPtr->column.minsize);
|
||
|
ckfree((char *) gridPtr->column.weight);
|
||
|
}
|
||
|
if (gridPtr->row.max) {
|
||
|
ckfree((char *) gridPtr->row.minsize);
|
||
|
ckfree((char *) gridPtr->row.weight);
|
||
|
}
|
||
|
if (gridPtr->layoutCache)
|
||
|
ckfree((char *) gridPtr->layoutCache);
|
||
|
|
||
|
ckfree((char *) gridPtr);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*----------------------------------------------------------------------
|
||
|
*
|
||
|
* GridBagStructureProc --
|
||
|
*
|
||
|
* This procedure is invoked by the Tk event dispatcher in response
|
||
|
* to StructureNotify events.
|
||
|
*
|
||
|
* Results:
|
||
|
* None.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* If a window was just deleted, clean up all its gridbag-related
|
||
|
* information. If it was just resized, re-configure its slaves, if
|
||
|
* any.
|
||
|
*
|
||
|
*----------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
static void
|
||
|
GridBagStructureProc(clientData, eventPtr)
|
||
|
ClientData clientData; /* Our information about window
|
||
|
* referred to by eventPtr. */
|
||
|
XEvent *eventPtr; /* Describes what just happened. */
|
||
|
{
|
||
|
register GridBag *gridPtr = (GridBag *) clientData;
|
||
|
|
||
|
if (eventPtr->type == ConfigureNotify) {
|
||
|
gridPtr->valid = 0;
|
||
|
if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
|
||
|
gridPtr->flags |= REQUESTED_RELAYOUT;
|
||
|
Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
|
||
|
}
|
||
|
if (gridPtr->doubleBw != 2*Tk_Changes(gridPtr->tkwin)->border_width) {
|
||
|
if ((gridPtr->masterPtr != NULL) &&
|
||
|
!(gridPtr->masterPtr->flags & REQUESTED_RELAYOUT)) {
|
||
|
gridPtr->doubleBw = 2*Tk_Changes(gridPtr->tkwin)->border_width;
|
||
|
gridPtr->masterPtr->flags |= REQUESTED_RELAYOUT;
|
||
|
Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr->masterPtr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (eventPtr->type == DestroyNotify) {
|
||
|
register GridBag *gridPtr2, *nextPtr;
|
||
|
|
||
|
if (gridPtr->masterPtr != NULL) {
|
||
|
Unlink(gridPtr);
|
||
|
}
|
||
|
for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
|
||
|
gridPtr2 = nextPtr) {
|
||
|
Tk_UnmapWindow(gridPtr2->tkwin);
|
||
|
gridPtr2->masterPtr = NULL;
|
||
|
nextPtr = gridPtr2->nextPtr;
|
||
|
gridPtr2->nextPtr = NULL;
|
||
|
}
|
||
|
Tcl_DeleteHashEntry(Tcl_FindHashEntry(&gridBagHashTable,
|
||
|
(char *) gridPtr->tkwin));
|
||
|
if (gridPtr->flags & REQUESTED_RELAYOUT) {
|
||
|
Tk_CancelIdleCall(ArrangeGrid, (ClientData) gridPtr);
|
||
|
}
|
||
|
gridPtr->tkwin = NULL;
|
||
|
Tk_EventuallyFree((ClientData) gridPtr, DestroyGridBag);
|
||
|
}
|
||
|
else if (eventPtr->type == MapNotify) {
|
||
|
gridPtr->valid = 0;
|
||
|
if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
|
||
|
gridPtr->flags |= REQUESTED_RELAYOUT;
|
||
|
Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
|
||
|
}
|
||
|
}
|
||
|
else if (eventPtr->type == UnmapNotify) {
|
||
|
register GridBag *gridPtr2;
|
||
|
|
||
|
for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
|
||
|
gridPtr2 = gridPtr2->nextPtr) {
|
||
|
Tk_UnmapWindow(gridPtr2->tkwin);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
*----------------------------------------------------------------------
|
||
|
*
|
||
|
* ConfigureSlaves --
|
||
|
*
|
||
|
* This implements the guts of the "grid configure" command. Given
|
||
|
* a list of slaves and configuration options, it arranges for the
|
||
|
* gridbag to manage the slaves and sets the specified options.
|
||
|
*
|
||
|
* Results:
|
||
|
* TCL_OK is returned if all went well. Otherwise, TCL_ERROR is
|
||
|
* returned and interp->result is set to contain an error message.
|
||
|
*
|
||
|
* Side effects:
|
||
|
* Slave windows get taken over by the gridbag.
|
||
|
*
|
||
|
*----------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
static int
|
||
|
ConfigureSlaves(interp, tkwin, argc, argv)
|
||
|
Tcl_Interp *interp; /* Interpreter for error reporting. */
|
||
|
Tk_Window tkwin; /* Any window in application containing
|
||
|
* slaves. Used to look up slave names. */
|
||
|
int argc; /* Numb = 0er of elements in argv. */
|
||
|
char *argv[]; /* Argument strings: contains one or more
|
||
|
* window names followed by any number
|
||
|
* of "option value" pairs. Caller must
|
||
|
* make sure that there is at least one
|
||
|
* window name. */
|
||
|
{
|
||
|
GridBag *masterPtr, *slavePtr, *prevPtr;
|
||
|
Tk_Window other, slave, parent, ancestor;
|
||
|
int i, j, numWindows, c, length, tmp, positionGiven;
|
||
|
int currentColumn=0, numColumns=1;
|
||
|
int gotLayout = 0;
|
||
|
int gotWidth = 0;
|
||
|
int width;
|
||
|
|
||
|
/*
|
||
|
* Find out how many windows are specified. (shouldn't use harwired symbols)
|
||
|
*/
|
||
|
|
||
|
for (numWindows = 0; numWindows < argc; numWindows++) {
|
||
|
if (argv[numWindows][0] != '.'
|
||
|
&& strcmp(argv[numWindows],"-")!=0
|
||
|
&& strcmp(argv[numWindows],"^")!=0
|
||
|
&& strcmp(argv[numWindows],"x")!=0) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
slave = NULL;
|
||
|
|
||
|
/*
|
||
|
* Iterate over all of the slave windows, parsing the configuration
|
||
|
* options for each slave. It's a bit wasteful to re-parse the
|
||
|
* options for each slave, but things get too messy if we try to
|
||
|
* parse the arguments just once at the beginning. For example,
|
||
|
* if a slave already is managed we want to just change a few
|
||
|
* existing values without resetting everything. If there are
|
||
|
* multiple windows, the -in option only gets processed for the
|
||
|
* first window.
|
||
|
*/
|
||
|
|
||
|
masterPtr = NULL;
|
||
|
prevPtr = NULL;
|
||
|
positionGiven = 0;
|
||
|
for (j = 0; j < numWindows; j++) {
|
||
|
|
||
|
/* adjust default widget location for non-widgets */
|
||
|
if (*argv[j] != '.') {
|
||
|
switch (*argv[j]) {
|
||
|
case '^': /* extend the widget in the previous row
|
||
|
* Since we don't know who the master is yet,
|
||
|
* handle these in a separate pass at the end
|
||
|
*/
|
||
|
/* no break */
|
||
|
case REL_SKIP: /* skip over the next column */
|
||
|
currentColumn++;
|
||
|
break;
|
||
|
case REL_HORIZ: /* increase the span, already dealt with */
|
||
|
/* not quite right */
|
||
|
if (j>0 && (*argv[j-1] == REL_SKIP || *argv[j-1] == '^')) {
|
||
|
Tcl_AppendResult(interp, "Invalid grid combination:",
|
||
|
" \"-\" can't follow \"", argv[j-1], "\"",NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
panic("Invalid grid position indicator");
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
for (numColumns=1; j+numColumns < numWindows && *argv[j+numColumns] == REL_HORIZ;
|
||
|
numColumns++) {
|
||
|
/* null body */
|
||
|
}
|
||
|
slave = Tk_NameToWindow(interp, argv[j], tkwin);
|
||
|
if (slave == NULL) {
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
if (Tk_IsTopLevel(slave)) {
|
||
|
Tcl_AppendResult(interp, "can't manage \"", argv[j],
|
||
|
"\": it's a top-level window", (char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
slavePtr = GetGridBag(slave);
|
||
|
|
||
|
/*
|
||
|
* The following statement is taken from tkPack.c:
|
||
|
*
|
||
|
* "If the slave isn't currently managed, reset all of its
|
||
|
* configuration information to default values (there could
|
||
|
* be old values left from a previous packer)."
|
||
|
*
|
||
|
* I disagree with this statement. If a slave is disabled (using
|
||
|
* "forget") and then re-enabled, I submit that 90% of the time the
|
||
|
* programmer will want it to retain its old configuration information.
|
||
|
* If the programmer doesn't want this behavior, then she can reset the
|
||
|
* defaults for herself, but she will never have to worry about keeping
|
||
|
* track of the old state.
|
||
|
*/
|
||
|
|
||
|
for (i = numWindows; i < argc; i+=2) {
|
||
|
if ((i+2) > argc) {
|
||
|
Tcl_AppendResult(interp, "extra option \"", argv[i],
|
||
|
"\" (option with no value?)", (char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
length = strlen(argv[i]);
|
||
|
if (length < 2) {
|
||
|
goto badOption;
|
||
|
}
|
||
|
c = argv[i][1];
|
||
|
if ((c == 'i') && (strcmp(argv[i], "-in") == 0)) {
|
||
|
if (j == 0) {
|
||
|
other = Tk_NameToWindow(interp, argv[i+1], tkwin);
|
||
|
if (other == NULL) {
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
if (other == slave) {
|
||
|
sprintf(interp->result,"Window can't be managed in itself");
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
masterPtr = GetGridBag(other);
|
||
|
prevPtr = masterPtr->slavePtr;
|
||
|
if (prevPtr != NULL) {
|
||
|
while (prevPtr->nextPtr != NULL) {
|
||
|
prevPtr = prevPtr->nextPtr;
|
||
|
}
|
||
|
}
|
||
|
positionGiven = 1;
|
||
|
}
|
||
|
} else if ((c == 'i') && (strcmp(argv[i], "-ipadx") == 0)) {
|
||
|
if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
|
||
|
|| (tmp < 0)) {
|
||
|
Tcl_ResetResult(interp);
|
||
|
Tcl_AppendResult(interp, "bad ipadx value \"", argv[i+1],
|
||
|
"\": must be positive screen distance",
|
||
|
(char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
slavePtr->iPadX = tmp*2;
|
||
|
} else if ((c == 'i') && (strcmp(argv[i], "-ipady") == 0)) {
|
||
|
if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
|
||
|
|| (tmp< 0)) {
|
||
|
Tcl_ResetResult(interp);
|
||
|
Tcl_AppendResult(interp, "bad ipady value \"", argv[i+1],
|
||
|
"\": must be positive screen distance",
|
||
|
(char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
slavePtr->iPadY = tmp*2;
|
||
|
} else if ((c == 'p') && (strcmp(argv[i], "-padx") == 0)) {
|
||
|
if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
|
||
|
|| (tmp< 0)) {
|
||
|
Tcl_ResetResult(interp);
|
||
|
Tcl_AppendResult(interp, "bad padx value \"", argv[i+1],
|
||
|
"\": must be positive screen distance",
|
||
|
(char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
slavePtr->padX = tmp*2;
|
||
|
} else if ((c == 'p') && (strcmp(argv[i], "-pady") == 0)) {
|
||
|
if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
|
||
|
|| (tmp< 0)) {
|
||
|
Tcl_ResetResult(interp);
|
||
|
Tcl_AppendResult(interp, "bad pady value \"", argv[i+1],
|
||
|
"\": must be positive screen distance",
|
||
|
(char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
slavePtr->padY = tmp*2;
|
||
|
} else if ((c == 'c') && (strcmp(argv[i], "-column") == 0)) {
|
||
|
if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
|
||
|
Tcl_ResetResult(interp);
|
||
|
Tcl_AppendResult(interp, "bad column value \"", argv[i+1],
|
||
|
"\": must be a non-negative integer", (char *)NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
slavePtr->gridColumn = tmp;
|
||
|
} else if ((c == 'r') && (strcmp(argv[i], "-row") == 0)) {
|
||
|
if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
|
||
|
Tcl_ResetResult(interp);
|
||
|
Tcl_AppendResult(interp, "bad grid value \"", argv[i+1],
|
||
|
"\": must be a non-negative integer", (char *)NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
slavePtr->gridRow = tmp;
|
||
|
} else if ((c == 'c') && (strcmp(argv[i], "-columnspan") == 0)) {
|
||
|
if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp <= 0) {
|
||
|
Tcl_ResetResult(interp);
|
||
|
Tcl_AppendResult(interp, "bad columnspan value \"", argv[i+1],
|
||
|
"\": must be a positive integer", (char *)NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
slavePtr->gridWidth = tmp;
|
||
|
gotWidth++;
|
||
|
} else if ((c == 'r') && (strcmp(argv[i], "-rowspan") == 0)) {
|
||
|
if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK) {
|
||
|
Tcl_ResetResult(interp);
|
||
|
Tcl_AppendResult(interp, "bad rowspan value \"", argv[i+1],
|
||
|
"\": must be a positive integer", (char *)NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
slavePtr->gridHeight = tmp;
|
||
|
/*
|
||
|
} else if ((c == 'w') &&
|
||
|
(!strcmp(argv[i], "-weightx") || !strcmp(argv[i], "-wx"))) {
|
||
|
if (Tcl_GetDouble(interp, argv[i+1], &tmp_dbl) != TCL_OK) {
|
||
|
Tcl_ResetResult(interp);
|
||
|
Tcl_AppendResult(interp, "bad weight value \"", argv[i+1],
|
||
|
"\": must be a double", (char *)NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
slavePtr->weightX = tmp_dbl;
|
||
|
}
|
||
|
else if ((c == 'w') &&
|
||
|
(!strcmp(argv[i], "-weighty") || !strcmp(argv[i], "-wy"))) {
|
||
|
if (Tcl_GetDouble(interp, argv[i+1], &tmp_dbl) != TCL_OK) {
|
||
|
Tcl_ResetResult(interp);
|
||
|
Tcl_AppendResult(interp, "bad weight value \"", argv[i+1],
|
||
|
"\": must be a double", (char *)NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
slavePtr->weightY = tmp_dbl;
|
||
|
*/
|
||
|
} else if ((c == 's') && strcmp(argv[i], "-sticky") == 0) {
|
||
|
int sticky = StringToSticky(argv[i+1]);
|
||
|
if (sticky == -1) {
|
||
|
Tcl_AppendResult(interp, "bad stickyness value \"", argv[i+1],
|
||
|
"\": must be a string containing n, e, s, and/or w", (char *)NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
slavePtr->flags = sticky | (slavePtr->flags & ~STICK_ALL);
|
||
|
} else {
|
||
|
badOption:
|
||
|
Tcl_AppendResult(interp, "unknown or ambiguous option \"",
|
||
|
argv[i], "\": must be -in, -sticky, ",
|
||
|
"-row, -column, -rowspan, -columnspan, ",
|
||
|
"-ipadx, -ipady, -padx or -pady.",
|
||
|
(char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If no position in a gridbag list was specified and the slave
|
||
|
* is already managed, then leave it in its current location in
|
||
|
* its current gridbag list.
|
||
|
*/
|
||
|
|
||
|
if (!positionGiven && (slavePtr->masterPtr != NULL)) {
|
||
|
masterPtr = slavePtr->masterPtr;
|
||
|
goto scheduleLayout;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If the slave is going to be put back after itself then
|
||
|
* skip the whole operation, since it won't work anyway.
|
||
|
*/
|
||
|
|
||
|
if (prevPtr == slavePtr) {
|
||
|
masterPtr = slavePtr->masterPtr;
|
||
|
goto scheduleLayout;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If the "-in" option has not been specified, arrange for the
|
||
|
* slave to go at the end of the order for its parent.
|
||
|
*/
|
||
|
|
||
|
if (!positionGiven) {
|
||
|
masterPtr = GetGridBag(Tk_Parent(slave));
|
||
|
prevPtr = masterPtr->slavePtr;
|
||
|
if (prevPtr != NULL) {
|
||
|
while (prevPtr->nextPtr != NULL) {
|
||
|
prevPtr = prevPtr->nextPtr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Make sure that the slave's parent is either the master or
|
||
|
* an ancestor of the master.
|
||
|
*/
|
||
|
|
||
|
parent = Tk_Parent(slave);
|
||
|
for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
|
||
|
if (ancestor == parent) {
|
||
|
break;
|
||
|
}
|
||
|
if (Tk_IsTopLevel(ancestor)) {
|
||
|
Tcl_AppendResult(interp, "can't put ", argv[j],
|
||
|
" inside ", Tk_PathName(masterPtr->tkwin),
|
||
|
(char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Unlink the slave if it's currently managed, then position it
|
||
|
* after prevPtr.
|
||
|
*/
|
||
|
|
||
|
if (slavePtr->masterPtr != NULL) {
|
||
|
Unlink(slavePtr);
|
||
|
}
|
||
|
slavePtr->masterPtr = masterPtr;
|
||
|
if (prevPtr == NULL) {
|
||
|
slavePtr->nextPtr = masterPtr->slavePtr;
|
||
|
masterPtr->slavePtr = slavePtr;
|
||
|
} else {
|
||
|
slavePtr->nextPtr = prevPtr->nextPtr;
|
||
|
prevPtr->nextPtr = slavePtr;
|
||
|
}
|
||
|
Tk_ManageGeometry(slave, &gridMgrType, (ClientData) slavePtr);
|
||
|
prevPtr = slavePtr;
|
||
|
|
||
|
/* assign default row and column */
|
||
|
|
||
|
if (slavePtr->gridColumn == -1) {
|
||
|
slavePtr->gridColumn = currentColumn;
|
||
|
}
|
||
|
slavePtr->gridWidth += numColumns - 1;
|
||
|
if (slavePtr->gridRow == -1) {
|
||
|
if (!gotLayout++) GetCachedLayoutInfo(masterPtr);
|
||
|
slavePtr->gridRow = masterPtr->layoutCache->lastRow;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Arrange for the parent to be re-arranged at the first
|
||
|
* idle moment.
|
||
|
*/
|
||
|
|
||
|
scheduleLayout:
|
||
|
if (masterPtr->abortPtr != NULL) {
|
||
|
*masterPtr->abortPtr = 1;
|
||
|
}
|
||
|
masterPtr->valid = 0;
|
||
|
if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
|
||
|
masterPtr->flags |= REQUESTED_RELAYOUT;
|
||
|
Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
|
||
|
}
|
||
|
currentColumn += slavePtr->gridWidth;
|
||
|
numColumns = 1;
|
||
|
}
|
||
|
|
||
|
/* now look for all the "^"'s */
|
||
|
|
||
|
for (j = 0; j < numWindows; j++) {
|
||
|
struct GridBag *otherPtr;
|
||
|
char *lastWindow; /* use this window to base current row/col on */
|
||
|
int match; /* found a match for the ^ */
|
||
|
|
||
|
if (*argv[j] == '.') {
|
||
|
lastWindow = argv[j];
|
||
|
}
|
||
|
if (*argv[j] != '^') {
|
||
|
continue;
|
||
|
}
|
||
|
for (width=1; width+j < numWindows && *argv[j+width] == '^'; width++) {
|
||
|
/* Null Body */
|
||
|
}
|
||
|
other = Tk_NameToWindow(interp, lastWindow, tkwin);
|
||
|
otherPtr = GetGridBag(other);
|
||
|
if (!gotLayout++) GetCachedLayoutInfo(masterPtr);
|
||
|
|
||
|
for (match=0, slavePtr = masterPtr->slavePtr; slavePtr != NULL;
|
||
|
slavePtr = slavePtr->nextPtr) {
|
||
|
|
||
|
if (slavePtr->gridWidth == width
|
||
|
&& slavePtr->gridColumn == otherPtr->gridColumn + otherPtr->gridWidth
|
||
|
&& slavePtr->gridRow + slavePtr->gridHeight == otherPtr->gridRow) {
|
||
|
slavePtr->gridHeight++;
|
||
|
match++;
|
||
|
}
|
||
|
lastWindow = Tk_PathName(slavePtr->tkwin);
|
||
|
}
|
||
|
if (!match) {
|
||
|
Tcl_AppendResult(interp, "can't find slave to extend with \"^\"",
|
||
|
" after ",lastWindow,
|
||
|
(char *) NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
j += width - 1;
|
||
|
}
|
||
|
return TCL_OK;
|
||
|
}
|
||
|
|
||
|
/* convert "Sticky" bits into a string */
|
||
|
|
||
|
static void
|
||
|
StickyToString(flags, result)
|
||
|
int flags; /* the sticky flags */
|
||
|
char *result; /* where to put the result */
|
||
|
{
|
||
|
int count = 0;
|
||
|
if (flags&STICK_NORTH) result[count++] = 'n';
|
||
|
if (flags&STICK_EAST) result[count++] = 'e';
|
||
|
if (flags&STICK_SOUTH) result[count++] = 's';
|
||
|
if (flags&STICK_WEST) result[count++] = 'w';
|
||
|
if (count) {
|
||
|
result[count] = '\0';
|
||
|
} else {
|
||
|
sprintf(result,"{}");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* convert sticky string to flags */
|
||
|
|
||
|
static int
|
||
|
StringToSticky(string)
|
||
|
char *string;
|
||
|
{
|
||
|
int sticky = 0;
|
||
|
char c;
|
||
|
|
||
|
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: return -1;
|
||
|
}
|
||
|
}
|
||
|
return sticky;
|
||
|
}
|