Parent Directory | Revision Log | Patch
projs/trunk/shared_source/tk_base/tkgrid.c revision 42 by dashley, Fri Oct 14 01:50:00 2016 UTC | projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkgrid.c revision 71 by dashley, Sat Nov 5 11:07:06 2016 UTC | |
---|---|---|
# | Line 1 | Line 1 |
/* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tk_base/tkgrid.c,v 1.1.1.1 2001/06/13 05:02:05 dtashley Exp $ */ | ||
/* | ||
* tkGrid.c -- | ||
* | ||
* Grid based geometry manager. | ||
* | ||
* Copyright (c) 1996-1997 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. | ||
* | ||
* RCS: @(#) $Id: tkgrid.c,v 1.1.1.1 2001/06/13 05:02:05 dtashley Exp $ | ||
*/ | ||
#include "tkInt.h" | ||
/* | ||
* Convenience Macros | ||
*/ | ||
#ifdef MAX | ||
# undef MAX | ||
#endif | ||
#define MAX(x,y) ((x) > (y) ? (x) : (y)) | ||
#ifdef MIN | ||
# undef MIN | ||
#endif | ||
#define MIN(x,y) ((x) > (y) ? (y) : (x)) | ||
#define COLUMN (1) /* working on column offsets */ | ||
#define ROW (2) /* working on row offsets */ | ||
#define CHECK_ONLY (1) /* check max slot constraint */ | ||
#define CHECK_SPACE (2) /* alloc more space, don't change max */ | ||
/* | ||
* Pre-allocate enough row and column slots for "typical" sized tables | ||
* this value should be chosen so by the time the extra malloc's are | ||
* required, the layout calculations overwehlm them. [A "slot" contains | ||
* information for either a row or column, depending upon the context.] | ||
*/ | ||
#define TYPICAL_SIZE 25 /* (arbitrary guess) */ | ||
#define PREALLOC 10 /* extra slots to allocate */ | ||
/* | ||
* Data structures are allocated dynamically to support arbitrary sized tables. | ||
* However, the space is proportional to the highest numbered slot with | ||
* some non-default property. This limit is used to head off mistakes and | ||
* denial of service attacks by limiting the amount of storage required. | ||
*/ | ||
#define MAX_ELEMENT 10000 | ||
/* | ||
* Special characters to support relative layouts. | ||
*/ | ||
#define REL_SKIP 'x' /* Skip this column. */ | ||
#define REL_HORIZ '-' /* Extend previous widget horizontally. */ | ||
#define REL_VERT '^' /* Extend widget from row above. */ | ||
/* | ||
* Structure to hold information for grid masters. A slot is either | ||
* a row or column. | ||
*/ | ||
typedef struct SlotInfo { | ||
int minSize; /* The minimum size of this slot (in pixels). | ||
* It is set via the rowconfigure or | ||
* columnconfigure commands. */ | ||
int weight; /* The resize weight of this slot. (0) means | ||
* this slot doesn't resize. Extra space in | ||
* the layout is given distributed among slots | ||
* inproportion to their weights. */ | ||
int pad; /* Extra padding, in pixels, required for | ||
* this slot. This amount is "added" to the | ||
* largest slave in the slot. */ | ||
int offset; /* This is a cached value used for | ||
* introspection. It is the pixel | ||
* offset of the right or bottom edge | ||
* of this slot from the beginning of the | ||
* layout. */ | ||
int temp; /* This is a temporary value used for | ||
* calculating adjusted weights when | ||
* shrinking the layout below its | ||
* nominal size. */ | ||
} SlotInfo; | ||
/* | ||
* Structure to hold information during layout calculations. There | ||
* is one of these for each slot, an array for each of the rows or columns. | ||
*/ | ||
typedef struct GridLayout { | ||
struct Gridder *binNextPtr; /* The next slave window in this bin. | ||
* Each bin contains a list of all | ||
* slaves whose spans are >1 and whose | ||
* right edges fall in this slot. */ | ||
int minSize; /* Minimum size needed for this slot, | ||
* in pixels. This is the space required | ||
* to hold any slaves contained entirely | ||
* in this slot, adjusted for any slot | ||
* constrants, such as size or padding. */ | ||
int pad; /* Padding needed for this slot */ | ||
int weight; /* Slot weight, controls resizing. */ | ||
int minOffset; /* The minimum offset, in pixels, from | ||
* the beginning of the layout to the | ||
* right/bottom edge of the slot calculated | ||
* from top/left to bottom/right. */ | ||
int maxOffset; /* The maximum offset, in pixels, from | ||
* the beginning of the layout to the | ||
* right-or-bottom edge of the slot calculated | ||
* from bottom-or-right to top-or-left. */ | ||
} GridLayout; | ||
/* | ||
* Keep one of these for each geometry master. | ||
*/ | ||
typedef struct { | ||
SlotInfo *columnPtr; /* Pointer to array of column constraints. */ | ||
SlotInfo *rowPtr; /* Pointer to array of row constraints. */ | ||
int columnEnd; /* The last column occupied by any slave. */ | ||
int columnMax; /* The number of columns with constraints. */ | ||
int columnSpace; /* The number of slots currently allocated for | ||
* column constraints. */ | ||
int rowEnd; /* The last row occupied by any slave. */ | ||
int rowMax; /* The number of rows with constraints. */ | ||
int rowSpace; /* The number of slots currently allocated | ||
* for row constraints. */ | ||
int startX; /* Pixel offset of this layout within its | ||
* parent. */ | ||
int startY; /* Pixel offset of this layout within its | ||
* parent. */ | ||
} GridMaster; | ||
/* | ||
* For each window that the grid cares about (either because | ||
* the window is managed by the grid or because the window | ||
* has slaves that are managed by the grid), there is a | ||
* structure of the following type: | ||
*/ | ||
typedef struct Gridder { | ||
Tk_Window tkwin; /* Tk token for window. NULL means that | ||
* the window has been deleted, but the | ||
* gridder hasn't had a chance to clean up | ||
* yet because the structure is still in | ||
* use. */ | ||
struct Gridder *masterPtr; /* Master window within which this window | ||
* is managed (NULL means this window | ||
* isn't managed by the gridder). */ | ||
struct Gridder *nextPtr; /* Next window managed within same | ||
* parent. List order doesn't matter. */ | ||
struct Gridder *slavePtr; /* First in list of slaves managed | ||
* inside this window (NULL means | ||
* no grid slaves). */ | ||
GridMaster *masterDataPtr; /* Additional data for geometry master. */ | ||
int column, row; /* Location in the grid (starting | ||
* from zero). */ | ||
int numCols, numRows; /* Number of columns or rows this slave spans. | ||
* Should be at least 1. */ | ||
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 sticky; /* which sides of its cavity this window | ||
* sticks to. See below for definitions */ | ||
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. */ | ||
/* | ||
* These fields are used temporarily for layout calculations only. | ||
*/ | ||
struct Gridder *binNextPtr; /* Link to next span>1 slave in this bin. */ | ||
int size; /* Nominal size (width or height) in pixels | ||
* of the slave. This includes the padding. */ | ||
} Gridder; | ||
/* 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 | ||
/* | ||
* Flag values for Grid structures: | ||
* | ||
* REQUESTED_RELAYOUT: 1 means a Tcl_DoWhenIdle request | ||
* has already been made to re-arrange | ||
* all the slaves of this window. | ||
* | ||
* 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 REQUESTED_RELAYOUT 1 | ||
#define DONT_PROPAGATE 2 | ||
/* | ||
* Prototypes for procedures used only in this file: | ||
*/ | ||
static void AdjustForSticky _ANSI_ARGS_((Gridder *slavePtr, int *xPtr, | ||
int *yPtr, int *widthPtr, int *heightPtr)); | ||
static int AdjustOffsets _ANSI_ARGS_((int width, | ||
int elements, SlotInfo *slotPtr)); | ||
static void ArrangeGrid _ANSI_ARGS_((ClientData clientData)); | ||
static int CheckSlotData _ANSI_ARGS_((Gridder *masterPtr, int slot, | ||
int slotType, int checkOnly)); | ||
static int ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp, | ||
Tk_Window tkwin, int argc, char *argv[])); | ||
static void DestroyGrid _ANSI_ARGS_((char *memPtr)); | ||
static Gridder *GetGrid _ANSI_ARGS_((Tk_Window tkwin)); | ||
static void GridStructureProc _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 InitMasterData _ANSI_ARGS_((Gridder *masterPtr)); | ||
static int ResolveConstraints _ANSI_ARGS_((Gridder *gridPtr, | ||
int rowOrColumn, int maxOffset)); | ||
static void SetGridSize _ANSI_ARGS_((Gridder *gridPtr)); | ||
static void StickyToString _ANSI_ARGS_((int flags, char *result)); | ||
static int StringToSticky _ANSI_ARGS_((char *string)); | ||
static void Unlink _ANSI_ARGS_((Gridder *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; | ||
Gridder *masterPtr; /* master grid record */ | ||
GridMaster *gridPtr; /* pointer to grid data */ | ||
size_t length; /* streing length of argument */ | ||
char c; /* 1st character of argument */ | ||
if ((argc >= 2) && ((argv[1][0] == '.') || (argv[1][0] == REL_SKIP) || | ||
(argv[1][0] == REL_VERT))) { | ||
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; | ||
int row, column; /* origin for bounding box */ | ||
int row2, column2; /* end of bounding box */ | ||
int endX, endY; /* last column/row in the layout */ | ||
int x=0, y=0; /* starting pixels for this bounding box */ | ||
int width, height; /* size of the bounding box */ | ||
char buf[TCL_INTEGER_SPACE * 4]; | ||
if (argc!=3 && argc != 5 && argc != 7) { | ||
Tcl_AppendResult(interp, "wrong number of arguments: ", | ||
"must be \"",argv[0], | ||
" bbox master ?column row ?column row??\"", | ||
(char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
master = Tk_NameToWindow(interp, argv[2], tkwin); | ||
if (master == NULL) { | ||
return TCL_ERROR; | ||
} | ||
masterPtr = GetGrid(master); | ||
if (argc >= 5) { | ||
if (Tcl_GetInt(interp, argv[3], &column) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
if (Tcl_GetInt(interp, argv[4], &row) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
column2 = column; | ||
row2 = row; | ||
} | ||
if (argc == 7) { | ||
if (Tcl_GetInt(interp, argv[5], &column2) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
if (Tcl_GetInt(interp, argv[6], &row2) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
} | ||
gridPtr = masterPtr->masterDataPtr; | ||
if (gridPtr == NULL) { | ||
Tcl_SetResult(interp, "0 0 0 0", TCL_STATIC); | ||
return(TCL_OK); | ||
} | ||
SetGridSize(masterPtr); | ||
endX = MAX(gridPtr->columnEnd, gridPtr->columnMax); | ||
endY = MAX(gridPtr->rowEnd, gridPtr->rowMax); | ||
if ((endX == 0) || (endY == 0)) { | ||
Tcl_SetResult(interp, "0 0 0 0", TCL_STATIC); | ||
return(TCL_OK); | ||
} | ||
if (argc == 3) { | ||
row = column = 0; | ||
row2 = endY; | ||
column2 = endX; | ||
} | ||
if (column > column2) { | ||
int temp = column; | ||
column = column2, column2 = temp; | ||
} | ||
if (row > row2) { | ||
int temp = row; | ||
row = row2, row2 = temp; | ||
} | ||
if (column > 0 && column < endX) { | ||
x = gridPtr->columnPtr[column-1].offset; | ||
} else if (column > 0) { | ||
x = gridPtr->columnPtr[endX-1].offset; | ||
} | ||
if (row > 0 && row < endY) { | ||
y = gridPtr->rowPtr[row-1].offset; | ||
} else if (row > 0) { | ||
y = gridPtr->rowPtr[endY-1].offset; | ||
} | ||
if (column2 < 0) { | ||
width = 0; | ||
} else if (column2 >= endX) { | ||
width = gridPtr->columnPtr[endX-1].offset - x; | ||
} else { | ||
width = gridPtr->columnPtr[column2].offset - x; | ||
} | ||
if (row2 < 0) { | ||
height = 0; | ||
} else if (row2 >= endY) { | ||
height = gridPtr->rowPtr[endY-1].offset - y; | ||
} else { | ||
height = gridPtr->rowPtr[row2].offset - y; | ||
} | ||
sprintf(buf, "%d %d %d %d", x + gridPtr->startX, y + gridPtr->startY, | ||
width, height); | ||
Tcl_SetResult(interp, buf, TCL_VOLATILE); | ||
} 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)) || | ||
((c == 'r') && (strncmp(argv[1], "remove", length) == 0))) { | ||
Tk_Window slave; | ||
Gridder *slavePtr; | ||
int i; | ||
for (i = 2; i < argc; i++) { | ||
slave = Tk_NameToWindow(interp, argv[i], tkwin); | ||
if (slave == NULL) { | ||
return TCL_ERROR; | ||
} | ||
slavePtr = GetGrid(slave); | ||
if (slavePtr->masterPtr != NULL) { | ||
/* | ||
* For "forget", reset all the settings to their defaults | ||
*/ | ||
if (c == 'f') { | ||
slavePtr->column = slavePtr->row = -1; | ||
slavePtr->numCols = 1; | ||
slavePtr->numRows = 1; | ||
slavePtr->padX = slavePtr->padY = 0; | ||
slavePtr->iPadX = slavePtr->iPadY = 0; | ||
slavePtr->doubleBw = 2*Tk_Changes(tkwin)->border_width; | ||
if (slavePtr->flags & REQUESTED_RELAYOUT) { | ||
Tcl_CancelIdleCall(ArrangeGrid, (ClientData) slavePtr); | ||
} | ||
slavePtr->flags = 0; | ||
slavePtr->sticky = 0; | ||
} | ||
Tk_ManageGeometry(slave, (Tk_GeomMgr *) NULL, | ||
(ClientData) NULL); | ||
if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) { | ||
Tk_UnmaintainGeometry(slavePtr->tkwin, | ||
slavePtr->masterPtr->tkwin); | ||
} | ||
Unlink(slavePtr); | ||
Tk_UnmapWindow(slavePtr->tkwin); | ||
} | ||
} | ||
} else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) { | ||
register Gridder *slavePtr; | ||
Tk_Window slave; | ||
char buffer[64 + TCL_INTEGER_SPACE * 4]; | ||
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 = GetGrid(slave); | ||
if (slavePtr->masterPtr == NULL) { | ||
Tcl_ResetResult(interp); | ||
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->column, slavePtr->row, | ||
slavePtr->numCols, slavePtr->numRows); | ||
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->sticky,buffer); | ||
Tcl_AppendResult(interp, " -sticky ", buffer, (char *) NULL); | ||
} else if((c == 'l') && (strncmp(argv[1], "location", length) == 0)) { | ||
Tk_Window master; | ||
register SlotInfo *slotPtr; | ||
int x, y; /* Offset in pixels, from edge of parent. */ | ||
int i, j; /* Corresponding column and row indeces. */ | ||
int endX, endY; /* end of grid */ | ||
char buf[TCL_INTEGER_SPACE * 2]; | ||
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; | ||
} | ||
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; | ||
} | ||
masterPtr = GetGrid(master); | ||
if (masterPtr->masterDataPtr == NULL) { | ||
Tcl_SetResult(interp, "-1 -1", TCL_STATIC); | ||
return TCL_OK; | ||
} | ||
gridPtr = masterPtr->masterDataPtr; | ||
/* | ||
* Update any pending requests. This is not always the | ||
* steady state value, as more configure events could be in | ||
* the pipeline, but its as close as its easy to get. | ||
*/ | ||
while (masterPtr->flags & REQUESTED_RELAYOUT) { | ||
Tcl_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr); | ||
ArrangeGrid ((ClientData) masterPtr); | ||
} | ||
SetGridSize(masterPtr); | ||
endX = MAX(gridPtr->columnEnd, gridPtr->columnMax); | ||
endY = MAX(gridPtr->rowEnd, gridPtr->rowMax); | ||
slotPtr = masterPtr->masterDataPtr->columnPtr; | ||
if (x < masterPtr->masterDataPtr->startX) { | ||
i = -1; | ||
} else { | ||
x -= masterPtr->masterDataPtr->startX; | ||
for (i=0;slotPtr[i].offset < x && i < endX; i++) { | ||
/* null body */ | ||
} | ||
} | ||
slotPtr = masterPtr->masterDataPtr->rowPtr; | ||
if (y < masterPtr->masterDataPtr->startY) { | ||
j = -1; | ||
} else { | ||
y -= masterPtr->masterDataPtr->startY; | ||
for (j=0;slotPtr[j].offset < y && j < endY; j++) { | ||
/* null body */ | ||
} | ||
} | ||
sprintf(buf, "%d %d", i, j); | ||
Tcl_SetResult(interp, buf, TCL_VOLATILE); | ||
} else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) { | ||
Tk_Window master; | ||
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 = GetGrid(master); | ||
if (argc == 3) { | ||
Tcl_SetResult(interp, | ||
((masterPtr->flags & DONT_PROPAGATE) ? "0" : "1"), | ||
TCL_STATIC); | ||
return TCL_OK; | ||
} | ||
if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
/* Only request a relayout if the propagation bit changes */ | ||
if ((!propagate) ^ (masterPtr->flags&DONT_PROPAGATE)) { | ||
if (propagate) { | ||
masterPtr->flags &= ~DONT_PROPAGATE; | ||
} else { | ||
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; | ||
} | ||
if (!(masterPtr->flags & REQUESTED_RELAYOUT)) { | ||
masterPtr->flags |= REQUESTED_RELAYOUT; | ||
Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr); | ||
} | ||
} | ||
} else if ((c == 's') && (strncmp(argv[1], "size", length) == 0) | ||
&& (length > 1)) { | ||
Tk_Window master; | ||
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 = GetGrid(master); | ||
if (masterPtr->masterDataPtr != NULL) { | ||
char buf[TCL_INTEGER_SPACE * 2]; | ||
SetGridSize(masterPtr); | ||
gridPtr = masterPtr->masterDataPtr; | ||
sprintf(buf, "%d %d", | ||
MAX(gridPtr->columnEnd, gridPtr->columnMax), | ||
MAX(gridPtr->rowEnd, gridPtr->rowMax)); | ||
Tcl_SetResult(interp, buf, TCL_VOLATILE); | ||
} else { | ||
Tcl_SetResult(interp, "0 0", TCL_STATIC); | ||
} | ||
} else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0) | ||
&& (length > 1)) { | ||
Tk_Window master; | ||
Gridder *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) { | ||
length = strlen(argv[i]); | ||
if ((*argv[i] != '-') || (length < 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 = GetGrid(master); | ||
for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; | ||
slavePtr = slavePtr->nextPtr) { | ||
if (column>=0 && (slavePtr->column > column | ||
|| slavePtr->column+slavePtr->numCols-1 < column)) { | ||
continue; | ||
} | ||
if (row>=0 && (slavePtr->row > row || | ||
slavePtr->row+slavePtr->numRows-1 < row)) { | ||
continue; | ||
} | ||
Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin)); | ||
} | ||
/* | ||
* Sample argument combinations: | ||
* grid columnconfigure <master> <index> -option | ||
* grid columnconfigure <master> <index> -option value -option value | ||
* grid rowconfigure <master> <index> | ||
* grid rowconfigure <master> <index> -option | ||
* grid rowconfigure <master> <index> -option value -option value. | ||
*/ | ||
} else if(((c == 'c') && (strncmp(argv[1], "columnconfigure", length) == 0) | ||
&& (length >= 3)) || | ||
((c == 'r') && (strncmp(argv[1], "rowconfigure", length) == 0) | ||
&& (length >=2))) { | ||
Tk_Window master; | ||
SlotInfo *slotPtr = NULL; | ||
int slot; /* the column or row number */ | ||
size_t length; /* the # of chars in the "-option" string */ | ||
int slotType; /* COLUMN or ROW */ | ||
int size; /* the configuration value */ | ||
int checkOnly; /* check the size only */ | ||
int argcPtr; /* Number of items in index list */ | ||
char **argvPtr; /* array of indeces */ | ||
char **indexP; /* String value of current index list item. */ | ||
int ok; /* temporary TCL result code */ | ||
int i; | ||
if (((argc%2 != 0) && (argc>6)) || (argc < 4)) { | ||
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; | ||
} | ||
if (Tcl_SplitList(interp, argv[3], &argcPtr, &argvPtr) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
checkOnly = ((argc == 4) || (argc == 5)); | ||
masterPtr = GetGrid(master); | ||
slotType = (c == 'c') ? COLUMN : ROW; | ||
if (checkOnly && argcPtr > 1) { | ||
Tcl_AppendResult(interp, argv[3], | ||
" must be a single element.", (char *) NULL); | ||
Tcl_Free((char *)argvPtr); | ||
return TCL_ERROR; | ||
} | ||
for (indexP=argvPtr; *indexP != NULL; indexP++) { | ||
if (Tcl_GetInt(interp, *indexP, &slot) != TCL_OK) { | ||
Tcl_Free((char *)argvPtr); | ||
return TCL_ERROR; | ||
} | ||
ok = CheckSlotData(masterPtr, slot, slotType, checkOnly); | ||
if ((ok!=TCL_OK) && ((argc<4) || (argc>5))) { | ||
Tcl_AppendResult(interp, argv[0], | ||
" ", argv[1], ": \"", *argvPtr,"\" is out of range", | ||
(char *) NULL); | ||
Tcl_Free((char *)argvPtr); | ||
return TCL_ERROR; | ||
} else if (ok == TCL_OK) { | ||
slotPtr = (slotType == COLUMN) ? | ||
masterPtr->masterDataPtr->columnPtr : | ||
masterPtr->masterDataPtr->rowPtr; | ||
} | ||
/* | ||
* Return all of the options for this row or column. If the | ||
* request is out of range, return all 0's. | ||
*/ | ||
if (argc == 4) { | ||
Tcl_Free((char *)argvPtr); | ||
} | ||
if ((argc == 4) && (ok == TCL_OK)) { | ||
char buf[64 + TCL_INTEGER_SPACE * 3]; | ||
sprintf(buf, "-minsize %d -pad %d -weight %d", | ||
slotPtr[slot].minSize,slotPtr[slot].pad, | ||
slotPtr[slot].weight); | ||
Tcl_SetResult(interp, buf, TCL_VOLATILE); | ||
return (TCL_OK); | ||
} else if (argc == 4) { | ||
Tcl_SetResult(interp, "-minsize 0 -pad 0 -weight 0", | ||
TCL_STATIC); | ||
return (TCL_OK); | ||
} | ||
/* | ||
* Loop through each option value pair, setting the values as required. | ||
* If only one option is given, with no value, the current value is | ||
* returned. | ||
*/ | ||
for (i=4; i<argc; i+=2) { | ||
length = strlen(argv[i]); | ||
if ((*argv[i] != '-') || length < 2) { | ||
Tcl_AppendResult(interp, "invalid arg \"", | ||
argv[i], "\" :expecting -minsize, -pad, or -weight.", | ||
(char *) NULL); | ||
Tcl_Free((char *)argvPtr); | ||
return TCL_ERROR; | ||
} | ||
if (strncmp(argv[i], "-minsize", length) == 0) { | ||
if (argc == 5) { | ||
char buf[TCL_INTEGER_SPACE]; | ||
int value; | ||
value = (ok == TCL_OK) ? slotPtr[slot].minSize : 0; | ||
sprintf(buf, "%d", value); | ||
Tcl_SetResult(interp, buf, TCL_VOLATILE); | ||
} else if (Tk_GetPixels(interp, master, argv[i+1], &size) | ||
!= TCL_OK) { | ||
Tcl_Free((char *)argvPtr); | ||
return TCL_ERROR; | ||
} else { | ||
slotPtr[slot].minSize = size; | ||
} | ||
} | ||
else if (strncmp(argv[i], "-weight", length) == 0) { | ||
int wt; | ||
if (argc == 5) { | ||
char buf[TCL_INTEGER_SPACE]; | ||
int value; | ||
value = (ok == TCL_OK) ? slotPtr[slot].weight : 0; | ||
sprintf(buf, "%d", value); | ||
Tcl_SetResult(interp, buf, TCL_VOLATILE); | ||
} else if (Tcl_GetInt(interp, argv[i+1], &wt) != TCL_OK) { | ||
Tcl_Free((char *)argvPtr); | ||
return TCL_ERROR; | ||
} else if (wt < 0) { | ||
Tcl_AppendResult(interp, "invalid arg \"", argv[i], | ||
"\": should be non-negative", (char *) NULL); | ||
Tcl_Free((char *)argvPtr); | ||
return TCL_ERROR; | ||
} else { | ||
slotPtr[slot].weight = wt; | ||
} | ||
} | ||
else if (strncmp(argv[i], "-pad", length) == 0) { | ||
if (argc == 5) { | ||
char buf[TCL_INTEGER_SPACE]; | ||
int value; | ||
value = (ok == TCL_OK) ? slotPtr[slot].pad : 0; | ||
sprintf(buf, "%d", value); | ||
Tcl_SetResult(interp, buf, TCL_VOLATILE); | ||
} else if (Tk_GetPixels(interp, master, argv[i+1], &size) | ||
!= TCL_OK) { | ||
Tcl_Free((char *)argvPtr); | ||
return TCL_ERROR; | ||
} else if (size < 0) { | ||
Tcl_AppendResult(interp, "invalid arg \"", argv[i], | ||
"\": should be non-negative", (char *) NULL); | ||
Tcl_Free((char *)argvPtr); | ||
return TCL_ERROR; | ||
} else { | ||
slotPtr[slot].pad = size; | ||
} | ||
} else { | ||
Tcl_AppendResult(interp, "invalid arg \"", | ||
argv[i], "\": expecting -minsize, -pad, or -weight.", | ||
(char *) NULL); | ||
Tcl_Free((char *)argvPtr); | ||
return TCL_ERROR; | ||
} | ||
} | ||
} | ||
Tcl_Free((char *)argvPtr); | ||
/* | ||
* If we changed a property, re-arrange the table, | ||
* and check for constraint shrinkage. | ||
*/ | ||
if (argc != 5) { | ||
if (slotType == ROW) { | ||
int last = masterPtr->masterDataPtr->rowMax - 1; | ||
while ((last >= 0) && (slotPtr[last].weight == 0) | ||
&& (slotPtr[last].pad == 0) | ||
&& (slotPtr[last].minSize == 0)) { | ||
last--; | ||
} | ||
masterPtr->masterDataPtr->rowMax = last+1; | ||
} else { | ||
int last = masterPtr->masterDataPtr->columnMax - 1; | ||
while ((last >= 0) && (slotPtr[last].weight == 0) | ||
&& (slotPtr[last].pad == 0) | ||
&& (slotPtr[last].minSize == 0)) { | ||
last--; | ||
} | ||
masterPtr->masterDataPtr->columnMax = last + 1; | ||
} | ||
if (masterPtr->abortPtr != NULL) { | ||
*masterPtr->abortPtr = 1; | ||
} | ||
if (!(masterPtr->flags & REQUESTED_RELAYOUT)) { | ||
masterPtr->flags |= REQUESTED_RELAYOUT; | ||
Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr); | ||
} | ||
} | ||
} else { | ||
Tcl_AppendResult(interp, "bad option \"", argv[1], | ||
"\": must be bbox, columnconfigure, configure, forget, info, ", | ||
"location, propagate, remove, 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 grid. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* Arranges for tkwin, and all its managed siblings, to | ||
* be re-arranged at the next idle point. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static void | ||
GridReqProc(clientData, tkwin) | ||
ClientData clientData; /* Grid's information about | ||
* window that got new preferred | ||
* geometry. */ | ||
Tk_Window tkwin; /* Other Tk-related information | ||
* about the window. */ | ||
{ | ||
register Gridder *gridPtr = (Gridder *) clientData; | ||
gridPtr = gridPtr->masterPtr; | ||
if (!(gridPtr->flags & REQUESTED_RELAYOUT)) { | ||
gridPtr->flags |= REQUESTED_RELAYOUT; | ||
Tcl_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. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static void | ||
GridLostSlaveProc(clientData, tkwin) | ||
ClientData clientData; /* Grid structure for slave window that | ||
* was stolen away. */ | ||
Tk_Window tkwin; /* Tk's handle for the slave window. */ | ||
{ | ||
register Gridder *slavePtr = (Gridder *) clientData; | ||
if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) { | ||
Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin); | ||
} | ||
Unlink(slavePtr); | ||
Tk_UnmapWindow(slavePtr->tkwin); | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* AdjustOffsets -- | ||
* | ||
* This procedure adjusts the size of the layout to fit in the | ||
* space provided. If it needs more space, the extra is added | ||
* according to the weights. If it needs less, the space is removed | ||
* according to the weights, but at no time does the size drop below | ||
* the minsize specified for that slot. | ||
* | ||
* Results: | ||
* The initial offset of the layout, | ||
* if all the weights are zero, else 0. | ||
* | ||
* Side effects: | ||
* The slot offsets are modified to shrink the layout. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static int | ||
AdjustOffsets(size, slots, slotPtr) | ||
int size; /* The total layout size (in pixels). */ | ||
int slots; /* Number of slots. */ | ||
register SlotInfo *slotPtr; /* Pointer to slot array. */ | ||
{ | ||
register int slot; /* Current slot. */ | ||
int diff; /* Extra pixels needed to add to the layout. */ | ||
int totalWeight = 0; /* Sum of the weights for all the slots. */ | ||
int weight = 0; /* Sum of the weights so far. */ | ||
int minSize = 0; /* Minimum possible layout size. */ | ||
int newDiff; /* The most pixels that can be added on | ||
* the current pass. */ | ||
diff = size - slotPtr[slots-1].offset; | ||
/* | ||
* The layout is already the correct size; all done. | ||
*/ | ||
if (diff == 0) { | ||
return(0); | ||
} | ||
/* | ||
* If all the weights are zero, center the layout in its parent if | ||
* there is extra space, else clip on the bottom/right. | ||
*/ | ||
for (slot=0; slot < slots; slot++) { | ||
totalWeight += slotPtr[slot].weight; | ||
} | ||
if (totalWeight == 0 ) { | ||
return(diff > 0 ? diff/2 : 0); | ||
} | ||
/* | ||
* Add extra space according to the slot weights. This is done | ||
* cumulatively to prevent round-off error accumulation. | ||
*/ | ||
if (diff > 0) { | ||
for (weight=slot=0; slot < slots; slot++) { | ||
weight += slotPtr[slot].weight; | ||
slotPtr[slot].offset += diff * weight / totalWeight; | ||
} | ||
return(0); | ||
} | ||
/* | ||
* The layout must shrink below its requested size. Compute the | ||
* minimum possible size by looking at the slot minSizes. | ||
*/ | ||
for (slot=0; slot < slots; slot++) { | ||
if (slotPtr[slot].weight > 0) { | ||
minSize += slotPtr[slot].minSize; | ||
} else if (slot > 0) { | ||
minSize += slotPtr[slot].offset - slotPtr[slot-1].offset; | ||
} else { | ||
minSize += slotPtr[slot].offset; | ||
} | ||
} | ||
/* | ||
* If the requested size is less than the minimum required size, | ||
* set the slot sizes to their minimum values, then clip on the | ||
* bottom/right. | ||
*/ | ||
if (size <= minSize) { | ||
int offset = 0; | ||
for (slot=0; slot < slots; slot++) { | ||
if (slotPtr[slot].weight > 0) { | ||
offset += slotPtr[slot].minSize; | ||
} else if (slot > 0) { | ||
offset += slotPtr[slot].offset - slotPtr[slot-1].offset; | ||
} else { | ||
offset += slotPtr[slot].offset; | ||
} | ||
slotPtr[slot].offset = offset; | ||
} | ||
return(0); | ||
} | ||
/* | ||
* Remove space from slots according to their weights. The weights | ||
* get renormalized anytime a slot shrinks to its minimum size. | ||
*/ | ||
while (diff < 0) { | ||
/* | ||
* Find the total weight for the shrinkable slots. | ||
*/ | ||
for (totalWeight=slot=0; slot < slots; slot++) { | ||
int current = (slot == 0) ? slotPtr[slot].offset : | ||
slotPtr[slot].offset - slotPtr[slot-1].offset; | ||
if (current > slotPtr[slot].minSize) { | ||
totalWeight += slotPtr[slot].weight; | ||
slotPtr[slot].temp = slotPtr[slot].weight; | ||
} else { | ||
slotPtr[slot].temp = 0; | ||
} | ||
} | ||
if (totalWeight == 0) { | ||
break; | ||
} | ||
/* | ||
* Find the maximum amount of space we can distribute this pass. | ||
*/ | ||
newDiff = diff; | ||
for (slot = 0; slot < slots; slot++) { | ||
int current; /* current size of this slot */ | ||
int maxDiff; /* max diff that would cause | ||
* this slot to equal its minsize */ | ||
if (slotPtr[slot].temp == 0) { | ||
continue; | ||
} | ||
current = (slot == 0) ? slotPtr[slot].offset : | ||
slotPtr[slot].offset - slotPtr[slot-1].offset; | ||
maxDiff = totalWeight * (slotPtr[slot].minSize - current) | ||
/ slotPtr[slot].temp; | ||
if (maxDiff > newDiff) { | ||
newDiff = maxDiff; | ||
} | ||
} | ||
/* | ||
* Now distribute the space. | ||
*/ | ||
for (weight=slot=0; slot < slots; slot++) { | ||
weight += slotPtr[slot].temp; | ||
slotPtr[slot].offset += newDiff * weight / totalWeight; | ||
} | ||
diff -= newDiff; | ||
} | ||
return(0); | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* AdjustForSticky -- | ||
* | ||
* This procedure adjusts the size of a slave in its cavity based | ||
* on its "sticky" flags. | ||
* | ||
* Results: | ||
* The input x, y, width, and height are changed to represent the | ||
* desired coordinates of the slave. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static void | ||
AdjustForSticky(slavePtr, xPtr, yPtr, widthPtr, heightPtr) | ||
Gridder *slavePtr; /* Slave window to arrange in its cavity. */ | ||
int *xPtr; /* Pixel location of the left edge of the cavity. */ | ||
int *yPtr; /* Pixel location of the top edge of the cavity. */ | ||
int *widthPtr; /* Width of the cavity (in pixels). */ | ||
int *heightPtr; /* Height of the cavity (in pixels). */ | ||
{ | ||
int diffx=0; /* Cavity width - slave width. */ | ||
int diffy=0; /* Cavity hight - slave height. */ | ||
int sticky = slavePtr->sticky; | ||
*xPtr += slavePtr->padX/2; | ||
*widthPtr -= slavePtr->padX; | ||
*yPtr += slavePtr->padY/2; | ||
*heightPtr -= slavePtr->padY; | ||
if (*widthPtr > (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX)) { | ||
diffx = *widthPtr - (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX); | ||
*widthPtr = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX; | ||
} | ||
if (*heightPtr > (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY)) { | ||
diffy = *heightPtr - (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY); | ||
*heightPtr = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY; | ||
} | ||
if (sticky&STICK_EAST && sticky&STICK_WEST) { | ||
*widthPtr += diffx; | ||
} | ||
if (sticky&STICK_NORTH && sticky&STICK_SOUTH) { | ||
*heightPtr += diffy; | ||
} | ||
if (!(sticky&STICK_WEST)) { | ||
*xPtr += (sticky&STICK_EAST) ? diffx : diffx/2; | ||
} | ||
if (!(sticky&STICK_NORTH)) { | ||
*yPtr += (sticky&STICK_SOUTH) ? diffy : diffy/2; | ||
} | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* ArrangeGrid -- | ||
* | ||
* This procedure is invoked (using the Tcl_DoWhenIdle | ||
* mechanism) to re-layout a set of windows managed by | ||
* the grid. It is invoked at idle time so that a | ||
* series of grid 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 Gridder *masterPtr = (Gridder *) clientData; | ||
register Gridder *slavePtr; | ||
GridMaster *slotPtr = masterPtr->masterDataPtr; | ||
int abort; | ||
int width, height; /* requested size of layout, in pixels */ | ||
int realWidth, realHeight; /* actual size layout should take-up */ | ||
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. Otherwise there is | ||
* no way to "relinquish" control over the parent so another geometry | ||
* manager can take over. | ||
*/ | ||
if (masterPtr->slavePtr == NULL) { | ||
return; | ||
} | ||
if (masterPtr->masterDataPtr == 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; | ||
Tcl_Preserve((ClientData) masterPtr); | ||
/* | ||
* Call the constraint engine to fill in the row and column offsets. | ||
*/ | ||
SetGridSize(masterPtr); | ||
width = ResolveConstraints(masterPtr, COLUMN, 0); | ||
height = ResolveConstraints(masterPtr, ROW, 0); | ||
width += 2*Tk_InternalBorderWidth(masterPtr->tkwin); | ||
height += 2*Tk_InternalBorderWidth(masterPtr->tkwin); | ||
if (((width != Tk_ReqWidth(masterPtr->tkwin)) | ||
|| (height != Tk_ReqHeight(masterPtr->tkwin))) | ||
&& !(masterPtr->flags & DONT_PROPAGATE)) { | ||
Tk_GeometryRequest(masterPtr->tkwin, width, height); | ||
if (width>1 && height>1) { | ||
masterPtr->flags |= REQUESTED_RELAYOUT; | ||
Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr); | ||
} | ||
masterPtr->abortPtr = NULL; | ||
Tcl_Release((ClientData) masterPtr); | ||
return; | ||
} | ||
/* | ||
* If the currently requested layout size doesn't match the parent's | ||
* window size, then adjust the slot offsets according to the | ||
* weights. If all of the weights are zero, center the layout in | ||
* its parent. I haven't decided what to do if the parent is smaller | ||
* than the requested size. | ||
*/ | ||
realWidth = Tk_Width(masterPtr->tkwin) - | ||
2*Tk_InternalBorderWidth(masterPtr->tkwin); | ||
realHeight = Tk_Height(masterPtr->tkwin) - | ||
2*Tk_InternalBorderWidth(masterPtr->tkwin); | ||
slotPtr->startX = AdjustOffsets(realWidth, | ||
MAX(slotPtr->columnEnd,slotPtr->columnMax), slotPtr->columnPtr); | ||
slotPtr->startY = AdjustOffsets(realHeight, | ||
MAX(slotPtr->rowEnd,slotPtr->rowMax), slotPtr->rowPtr); | ||
slotPtr->startX += Tk_InternalBorderWidth(masterPtr->tkwin); | ||
slotPtr->startY += Tk_InternalBorderWidth(masterPtr->tkwin); | ||
/* | ||
* Now adjust the actual size of the slave to its cavity by | ||
* computing the cavity size, and adjusting the widget according | ||
* to its stickyness. | ||
*/ | ||
for (slavePtr = masterPtr->slavePtr; slavePtr != NULL && !abort; | ||
slavePtr = slavePtr->nextPtr) { | ||
int x, y; /* top left coordinate */ | ||
int width, height; /* slot or slave size */ | ||
int col = slavePtr->column; | ||
int row = slavePtr->row; | ||
x = (col>0) ? slotPtr->columnPtr[col-1].offset : 0; | ||
y = (row>0) ? slotPtr->rowPtr[row-1].offset : 0; | ||
width = slotPtr->columnPtr[slavePtr->numCols+col-1].offset - x; | ||
height = slotPtr->rowPtr[slavePtr->numRows+row-1].offset - y; | ||
x += slotPtr->startX; | ||
y += slotPtr->startY; | ||
AdjustForSticky(slavePtr, &x, &y, &width, &height); | ||
/* | ||
* Now put the window in the proper spot. (This was taken directly | ||
* from tkPack.c.) If the slave is a child of the master, then | ||
* do this here. Otherwise let Tk_MaintainGeometry do the work. | ||
*/ | ||
if (masterPtr->tkwin == Tk_Parent(slavePtr->tkwin)) { | ||
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) { | ||
break; | ||
} | ||
/* | ||
* Don't map the slave if the master isn't mapped: wait | ||
* until the master gets mapped later. | ||
*/ | ||
if (Tk_IsMapped(masterPtr->tkwin)) { | ||
Tk_MapWindow(slavePtr->tkwin); | ||
} | ||
} | ||
} else { | ||
if ((width <= 0) || (height <= 0)) { | ||
Tk_UnmaintainGeometry(slavePtr->tkwin, masterPtr->tkwin); | ||
Tk_UnmapWindow(slavePtr->tkwin); | ||
} else { | ||
Tk_MaintainGeometry(slavePtr->tkwin, masterPtr->tkwin, | ||
x, y, width, height); | ||
} | ||
} | ||
} | ||
masterPtr->abortPtr = NULL; | ||
Tcl_Release((ClientData) masterPtr); | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* ResolveConstraints -- | ||
* | ||
* Resolve all of the column and row boundaries. Most of | ||
* the calculations are identical for rows and columns, so this procedure | ||
* is called twice, once for rows, and again for columns. | ||
* | ||
* Results: | ||
* The offset (in pixels) from the left/top edge of this layout is | ||
* returned. | ||
* | ||
* Side effects: | ||
* The slot offsets are copied into the SlotInfo structure for the | ||
* geometry master. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static int | ||
ResolveConstraints(masterPtr, slotType, maxOffset) | ||
Gridder *masterPtr; /* The geometry master for this grid. */ | ||
int slotType; /* Either ROW or COLUMN. */ | ||
int maxOffset; /* The actual maximum size of this layout | ||
* in pixels, or 0 (not currently used). */ | ||
{ | ||
register SlotInfo *slotPtr; /* Pointer to row/col constraints. */ | ||
register Gridder *slavePtr; /* List of slave windows in this grid. */ | ||
int constraintCount; /* Count of rows or columns that have | ||
* constraints. */ | ||
int slotCount; /* Last occupied row or column. */ | ||
int gridCount; /* The larger of slotCount and constraintCount. | ||
*/ | ||
GridLayout *layoutPtr; /* Temporary layout structure. */ | ||
int requiredSize; /* The natural size of the grid (pixels). | ||
* This is the minimum size needed to | ||
* accomodate all of the slaves at their | ||
* requested sizes. */ | ||
int offset; /* The pixel offset of the right edge of the | ||
* current slot from the beginning of the | ||
* layout. */ | ||
int slot; /* The current slot. */ | ||
int start; /* The first slot of a contiguous set whose | ||
* constraints are not yet fully resolved. */ | ||
int end; /* The Last slot of a contiguous set whose | ||
* constraints are not yet fully resolved. */ | ||
/* | ||
* For typical sized tables, we'll use stack space for the layout data | ||
* to avoid the overhead of a malloc and free for every layout. | ||
*/ | ||
GridLayout layoutData[TYPICAL_SIZE + 1]; | ||
if (slotType == COLUMN) { | ||
constraintCount = masterPtr->masterDataPtr->columnMax; | ||
slotCount = masterPtr->masterDataPtr->columnEnd; | ||
slotPtr = masterPtr->masterDataPtr->columnPtr; | ||
} else { | ||
constraintCount = masterPtr->masterDataPtr->rowMax; | ||
slotCount = masterPtr->masterDataPtr->rowEnd; | ||
slotPtr = masterPtr->masterDataPtr->rowPtr; | ||
} | ||
/* | ||
* Make sure there is enough memory for the layout. | ||
*/ | ||
gridCount = MAX(constraintCount,slotCount); | ||
if (gridCount >= TYPICAL_SIZE) { | ||
layoutPtr = (GridLayout *) ckalloc(sizeof(GridLayout) * (1+gridCount)); | ||
} else { | ||
layoutPtr = layoutData; | ||
} | ||
/* | ||
* Allocate an extra layout slot to represent the left/top edge of | ||
* the 0th slot to make it easier to calculate slot widths from | ||
* offsets without special case code. | ||
* Initialize the "dummy" slot to the left/top of the table. | ||
* This slot avoids special casing the first slot. | ||
*/ | ||
layoutPtr->minOffset = 0; | ||
layoutPtr->maxOffset = 0; | ||
layoutPtr++; | ||
/* | ||
* Step 1. | ||
* Copy the slot constraints into the layout structure, | ||
* and initialize the rest of the fields. | ||
*/ | ||
for (slot=0; slot < constraintCount; slot++) { | ||
layoutPtr[slot].minSize = slotPtr[slot].minSize; | ||
layoutPtr[slot].weight = slotPtr[slot].weight; | ||
layoutPtr[slot].pad = slotPtr[slot].pad; | ||
layoutPtr[slot].binNextPtr = NULL; | ||
} | ||
for(;slot<gridCount;slot++) { | ||
layoutPtr[slot].minSize = 0; | ||
layoutPtr[slot].weight = 0; | ||
layoutPtr[slot].pad = 0; | ||
layoutPtr[slot].binNextPtr = NULL; | ||
} | ||
/* | ||
* Step 2. | ||
* Slaves with a span of 1 are used to determine the minimum size of | ||
* each slot. Slaves whose span is two or more slots don't | ||
* contribute to the minimum size of each slot directly, but can cause | ||
* slots to grow if their size exceeds the the sizes of the slots they | ||
* span. | ||
* | ||
* Bin all slaves whose spans are > 1 by their right edges. This | ||
* allows the computation on minimum and maximum possible layout | ||
* sizes at each slot boundary, without the need to re-sort the slaves. | ||
*/ | ||
switch (slotType) { | ||
case COLUMN: | ||
for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; | ||
slavePtr = slavePtr->nextPtr) { | ||
int rightEdge = slavePtr->column + slavePtr->numCols - 1; | ||
slavePtr->size = Tk_ReqWidth(slavePtr->tkwin) + | ||
slavePtr->padX + slavePtr->iPadX + slavePtr->doubleBw; | ||
if (slavePtr->numCols > 1) { | ||
slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr; | ||
layoutPtr[rightEdge].binNextPtr = slavePtr; | ||
} else { | ||
int size = slavePtr->size + layoutPtr[rightEdge].pad; | ||
if (size > layoutPtr[rightEdge].minSize) { | ||
layoutPtr[rightEdge].minSize = size; | ||
} | ||
} | ||
} | ||
break; | ||
case ROW: | ||
for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; | ||
slavePtr = slavePtr->nextPtr) { | ||
int rightEdge = slavePtr->row + slavePtr->numRows - 1; | ||
slavePtr->size = Tk_ReqHeight(slavePtr->tkwin) + | ||
slavePtr->padY + slavePtr->iPadY + slavePtr->doubleBw; | ||
if (slavePtr->numRows > 1) { | ||
slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr; | ||
layoutPtr[rightEdge].binNextPtr = slavePtr; | ||
} else { | ||
int size = slavePtr->size + layoutPtr[rightEdge].pad; | ||
if (size > layoutPtr[rightEdge].minSize) { | ||
layoutPtr[rightEdge].minSize = size; | ||
} | ||
} | ||
} | ||
break; | ||
} | ||
/* | ||
* Step 3. | ||
* Determine the minimum slot offsets going from left to right | ||
* that would fit all of the slaves. This determines the minimum | ||
*/ | ||
for (offset=slot=0; slot < gridCount; slot++) { | ||
layoutPtr[slot].minOffset = layoutPtr[slot].minSize + offset; | ||
for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL; | ||
slavePtr = slavePtr->binNextPtr) { | ||
int span = (slotType == COLUMN) ? slavePtr->numCols : slavePtr->numRows; | ||
int required = slavePtr->size + layoutPtr[slot - span].minOffset; | ||
if (required > layoutPtr[slot].minOffset) { | ||
layoutPtr[slot].minOffset = required; | ||
} | ||
} | ||
offset = layoutPtr[slot].minOffset; | ||
} | ||
/* | ||
* At this point, we know the minimum required size of the entire layout. | ||
* It might be prudent to stop here if our "master" will resize itself | ||
* to this size. | ||
*/ | ||
requiredSize = offset; | ||
if (maxOffset > offset) { | ||
offset=maxOffset; | ||
} | ||
/* | ||
* Step 4. | ||
* Determine the minimum slot offsets going from right to left, | ||
* bounding the pixel range of each slot boundary. | ||
* Pre-fill all of the right offsets with the actual size of the table; | ||
* they will be reduced as required. | ||
*/ | ||
for (slot=0; slot < gridCount; slot++) { | ||
layoutPtr[slot].maxOffset = offset; | ||
} | ||
for (slot=gridCount-1; slot > 0;) { | ||
for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL; | ||
slavePtr = slavePtr->binNextPtr) { | ||
int span = (slotType == COLUMN) ? slavePtr->numCols : slavePtr->numRows; | ||
int require = offset - slavePtr->size; | ||
int startSlot = slot - span; | ||
if (startSlot >=0 && require < layoutPtr[startSlot].maxOffset) { | ||
layoutPtr[startSlot].maxOffset = require; | ||
} | ||
} | ||
offset -= layoutPtr[slot].minSize; | ||
slot--; | ||
if (layoutPtr[slot].maxOffset < offset) { | ||
offset = layoutPtr[slot].maxOffset; | ||
} else { | ||
layoutPtr[slot].maxOffset = offset; | ||
} | ||
} | ||
/* | ||
* Step 5. | ||
* At this point, each slot boundary has a range of values that | ||
* will satisfy the overall layout size. | ||
* Make repeated passes over the layout structure looking for | ||
* spans of slot boundaries where the minOffsets are less than | ||
* the maxOffsets, and adjust the offsets according to the slot | ||
* weights. At each pass, at least one slot boundary will have | ||
* its range of possible values fixed at a single value. | ||
*/ | ||
for (start=0; start < gridCount;) { | ||
int totalWeight = 0; /* Sum of the weights for all of the | ||
* slots in this span. */ | ||
int need = 0; /* The minimum space needed to layout | ||
* this span. */ | ||
int have; /* The actual amount of space that will | ||
* be taken up by this span. */ | ||
int weight; /* Cumulative weights of the columns in | ||
* this span. */ | ||
int noWeights = 0; /* True if the span has no weights. */ | ||
/* | ||
* Find a span by identifying ranges of slots whose edges are | ||
* already constrained at fixed offsets, but whose internal | ||
* slot boundaries have a range of possible positions. | ||
*/ | ||
if (layoutPtr[start].minOffset == layoutPtr[start].maxOffset) { | ||
start++; | ||
continue; | ||
} | ||
for (end=start+1; end<gridCount; end++) { | ||
if (layoutPtr[end].minOffset == layoutPtr[end].maxOffset) { | ||
break; | ||
} | ||
} | ||
/* | ||
* We found a span. Compute the total weight, minumum space required, | ||
* for this span, and the actual amount of space the span should | ||
* use. | ||
*/ | ||
for (slot=start; slot<=end; slot++) { | ||
totalWeight += layoutPtr[slot].weight; | ||
need += layoutPtr[slot].minSize; | ||
} | ||
have = layoutPtr[end].maxOffset - layoutPtr[start-1].minOffset; | ||
/* | ||
* If all the weights in the span are zero, then distribute the | ||
* extra space evenly. | ||
*/ | ||
if (totalWeight == 0) { | ||
noWeights++; | ||
totalWeight = end - start + 1; | ||
} | ||
/* | ||
* It might not be possible to give the span all of the space | ||
* available on this pass without violating the size constraints | ||
* of one or more of the internal slot boundaries. | ||
* Determine the maximum amount of space that when added to the | ||
* entire span, would cause a slot boundary to have its possible | ||
* range reduced to one value, and reduce the amount of extra | ||
* space allocated on this pass accordingly. | ||
* | ||
* The calculation is done cumulatively to avoid accumulating | ||
* roundoff errors. | ||
*/ | ||
for (weight=0,slot=start; slot<end; slot++) { | ||
int diff = layoutPtr[slot].maxOffset - layoutPtr[slot].minOffset; | ||
weight += noWeights ? 1 : layoutPtr[slot].weight; | ||
if ((noWeights || layoutPtr[slot].weight>0) && | ||
(diff*totalWeight/weight) < (have-need)) { | ||
have = diff * totalWeight / weight + need; | ||
} | ||
} | ||
/* | ||
* Now distribute the extra space among the slots by | ||
* adjusting the minSizes and minOffsets. | ||
*/ | ||
for (weight=0,slot=start; slot<end; slot++) { | ||
weight += noWeights ? 1 : layoutPtr[slot].weight; | ||
layoutPtr[slot].minOffset += | ||
(int)((double) (have-need) * weight/totalWeight + 0.5); | ||
layoutPtr[slot].minSize = layoutPtr[slot].minOffset | ||
- layoutPtr[slot-1].minOffset; | ||
} | ||
layoutPtr[slot].minSize = layoutPtr[slot].minOffset | ||
- layoutPtr[slot-1].minOffset; | ||
/* | ||
* Having pushed the top/left boundaries of the slots to | ||
* take up extra space, the bottom/right space is recalculated | ||
* to propagate the new space allocation. | ||
*/ | ||
for (slot=end; slot > start; slot--) { | ||
layoutPtr[slot-1].maxOffset = | ||
layoutPtr[slot].maxOffset-layoutPtr[slot].minSize; | ||
} | ||
} | ||
/* | ||
* Step 6. | ||
* All of the space has been apportioned; copy the | ||
* layout information back into the master. | ||
*/ | ||
for (slot=0; slot < gridCount; slot++) { | ||
slotPtr[slot].offset = layoutPtr[slot].minOffset; | ||
} | ||
--layoutPtr; | ||
if (layoutPtr != layoutData) { | ||
Tcl_Free((char *)layoutPtr); | ||
} | ||
return requiredSize; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* GetGrid -- | ||
* | ||
* This internal procedure is used to locate a Grid | ||
* structure for a given window, creating one if one | ||
* doesn't exist already. | ||
* | ||
* Results: | ||
* The return value is a pointer to the Grid structure | ||
* corresponding to tkwin. | ||
* | ||
* Side effects: | ||
* A new grid structure may be created. If so, then | ||
* a callback is set up to clean things up when the | ||
* window is deleted. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static Gridder * | ||
GetGrid(tkwin) | ||
Tk_Window tkwin; /* Token for window for which | ||
* grid structure is desired. */ | ||
{ | ||
register Gridder *gridPtr; | ||
Tcl_HashEntry *hPtr; | ||
int new; | ||
TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr; | ||
if (!dispPtr->gridInit) { | ||
Tcl_InitHashTable(&dispPtr->gridHashTable, TCL_ONE_WORD_KEYS); | ||
dispPtr->gridInit = 1; | ||
} | ||
/* | ||
* See if there's already grid for this window. If not, | ||
* then create a new one. | ||
*/ | ||
hPtr = Tcl_CreateHashEntry(&dispPtr->gridHashTable, (char *) tkwin, &new); | ||
if (!new) { | ||
return (Gridder *) Tcl_GetHashValue(hPtr); | ||
} | ||
gridPtr = (Gridder *) ckalloc(sizeof(Gridder)); | ||
gridPtr->tkwin = tkwin; | ||
gridPtr->masterPtr = NULL; | ||
gridPtr->masterDataPtr = NULL; | ||
gridPtr->nextPtr = NULL; | ||
gridPtr->slavePtr = NULL; | ||
gridPtr->binNextPtr = NULL; | ||
gridPtr->column = gridPtr->row = -1; | ||
gridPtr->numCols = 1; | ||
gridPtr->numRows = 1; | ||
gridPtr->padX = gridPtr->padY = 0; | ||
gridPtr->iPadX = gridPtr->iPadY = 0; | ||
gridPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width; | ||
gridPtr->abortPtr = NULL; | ||
gridPtr->flags = 0; | ||
gridPtr->sticky = 0; | ||
gridPtr->size = 0; | ||
gridPtr->masterDataPtr = NULL; | ||
Tcl_SetHashValue(hPtr, gridPtr); | ||
Tk_CreateEventHandler(tkwin, StructureNotifyMask, | ||
GridStructureProc, (ClientData) gridPtr); | ||
return gridPtr; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* SetGridSize -- | ||
* | ||
* This internal procedure sets the size of the grid occupied | ||
* by slaves. | ||
* | ||
* Results: | ||
* none | ||
* | ||
* Side effects: | ||
* The width and height arguments are filled in the master data structure. | ||
* Additional space is allocated for the constraints to accomodate | ||
* the offsets. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static void | ||
SetGridSize(masterPtr) | ||
Gridder *masterPtr; /* The geometry master for this grid. */ | ||
{ | ||
register Gridder *slavePtr; /* Current slave window. */ | ||
int maxX = 0, maxY = 0; | ||
for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; | ||
slavePtr = slavePtr->nextPtr) { | ||
maxX = MAX(maxX,slavePtr->numCols + slavePtr->column); | ||
maxY = MAX(maxY,slavePtr->numRows + slavePtr->row); | ||
} | ||
masterPtr->masterDataPtr->columnEnd = maxX; | ||
masterPtr->masterDataPtr->rowEnd = maxY; | ||
CheckSlotData(masterPtr, maxX, COLUMN, CHECK_SPACE); | ||
CheckSlotData(masterPtr, maxY, ROW, CHECK_SPACE); | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* CheckSlotData -- | ||
* | ||
* This internal procedure is used to manage the storage for | ||
* row and column (slot) constraints. | ||
* | ||
* Results: | ||
* TRUE if the index is OK, False otherwise. | ||
* | ||
* Side effects: | ||
* A new master grid structure may be created. If so, then | ||
* it is initialized. In addition, additional storage for | ||
* a row or column constraints may be allocated, and the constraint | ||
* maximums are adjusted. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static int | ||
CheckSlotData(masterPtr, slot, slotType, checkOnly) | ||
Gridder *masterPtr; /* the geometry master for this grid */ | ||
int slot; /* which slot to look at */ | ||
int slotType; /* ROW or COLUMN */ | ||
int checkOnly; /* don't allocate new space if true */ | ||
{ | ||
int numSlot; /* number of slots already allocated (Space) */ | ||
int end; /* last used constraint */ | ||
/* | ||
* If slot is out of bounds, return immediately. | ||
*/ | ||
if (slot < 0 || slot >= MAX_ELEMENT) { | ||
return TCL_ERROR; | ||
} | ||
if ((checkOnly == CHECK_ONLY) && (masterPtr->masterDataPtr == NULL)) { | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* If we need to allocate more space, allocate a little extra to avoid | ||
* repeated re-alloc's for large tables. We need enough space to | ||
* hold all of the offsets as well. | ||
*/ | ||
InitMasterData(masterPtr); | ||
end = (slotType == ROW) ? masterPtr->masterDataPtr->rowMax : | ||
masterPtr->masterDataPtr->columnMax; | ||
if (checkOnly == CHECK_ONLY) { | ||
return (end < slot) ? TCL_ERROR : TCL_OK; | ||
} else { | ||
numSlot = (slotType == ROW) ? masterPtr->masterDataPtr->rowSpace | ||
: masterPtr->masterDataPtr->columnSpace; | ||
if (slot >= numSlot) { | ||
int newNumSlot = slot + PREALLOC ; | ||
size_t oldSize = numSlot * sizeof(SlotInfo) ; | ||
size_t newSize = newNumSlot * sizeof(SlotInfo) ; | ||
SlotInfo *new = (SlotInfo *) ckalloc(newSize); | ||
SlotInfo *old = (slotType == ROW) ? | ||
masterPtr->masterDataPtr->rowPtr : | ||
masterPtr->masterDataPtr->columnPtr; | ||
memcpy((VOID *) new, (VOID *) old, oldSize ); | ||
memset((VOID *) (new+numSlot), 0, newSize - oldSize ); | ||
Tcl_Free((char *) old); | ||
if (slotType == ROW) { | ||
masterPtr->masterDataPtr->rowPtr = new ; | ||
masterPtr->masterDataPtr->rowSpace = newNumSlot ; | ||
} else { | ||
masterPtr->masterDataPtr->columnPtr = new; | ||
masterPtr->masterDataPtr->columnSpace = newNumSlot ; | ||
} | ||
} | ||
if (slot >= end && checkOnly != CHECK_SPACE) { | ||
if (slotType == ROW) { | ||
masterPtr->masterDataPtr->rowMax = slot+1; | ||
} else { | ||
masterPtr->masterDataPtr->columnMax = slot+1; | ||
} | ||
} | ||
return TCL_OK; | ||
} | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* InitMasterData -- | ||
* | ||
* This internal procedure is used to allocate and initialize | ||
* the data for a geometry master, if the data | ||
* doesn't exist already. | ||
* | ||
* Results: | ||
* none | ||
* | ||
* Side effects: | ||
* A new master grid structure may be created. If so, then | ||
* it is initialized. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static void | ||
InitMasterData(masterPtr) | ||
Gridder *masterPtr; | ||
{ | ||
size_t size; | ||
if (masterPtr->masterDataPtr == NULL) { | ||
GridMaster *gridPtr = masterPtr->masterDataPtr = | ||
(GridMaster *) ckalloc(sizeof(GridMaster)); | ||
size = sizeof(SlotInfo) * TYPICAL_SIZE; | ||
gridPtr->columnEnd = 0; | ||
gridPtr->columnMax = 0; | ||
gridPtr->columnPtr = (SlotInfo *) ckalloc(size); | ||
gridPtr->columnSpace = TYPICAL_SIZE; | ||
gridPtr->rowEnd = 0; | ||
gridPtr->rowMax = 0; | ||
gridPtr->rowPtr = (SlotInfo *) ckalloc(size); | ||
gridPtr->rowSpace = TYPICAL_SIZE; | ||
gridPtr->startX = 0; | ||
gridPtr->startY = 0; | ||
memset((VOID *) gridPtr->columnPtr, 0, size); | ||
memset((VOID *) gridPtr->rowPtr, 0, size); | ||
} | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Unlink -- | ||
* | ||
* Remove a grid from its parent's list of slaves. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The parent will be scheduled for re-arranging, and the size of the | ||
* grid will be adjusted accordingly | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static void | ||
Unlink(slavePtr) | ||
register Gridder *slavePtr; /* Window to unlink. */ | ||
{ | ||
register Gridder *masterPtr, *slavePtr2; | ||
GridMaster *gridPtr; /* pointer to grid data */ | ||
masterPtr = slavePtr->masterPtr; | ||
if (masterPtr == NULL) { | ||
return; | ||
} | ||
gridPtr = masterPtr->masterDataPtr; | ||
if (masterPtr->slavePtr == slavePtr) { | ||
masterPtr->slavePtr = slavePtr->nextPtr; | ||
} | ||
else { | ||
for (slavePtr2 = masterPtr->slavePtr; ; slavePtr2 = slavePtr2->nextPtr) { | ||
if (slavePtr2 == NULL) { | ||
panic("Unlink couldn't find previous window"); | ||
} | ||
if (slavePtr2->nextPtr == slavePtr) { | ||
slavePtr2->nextPtr = slavePtr->nextPtr; | ||
break; | ||
} | ||
} | ||
} | ||
if (!(masterPtr->flags & REQUESTED_RELAYOUT)) { | ||
masterPtr->flags |= REQUESTED_RELAYOUT; | ||
Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr); | ||
} | ||
if (masterPtr->abortPtr != NULL) { | ||
*masterPtr->abortPtr = 1; | ||
} | ||
if ((slavePtr->numCols+slavePtr->column == gridPtr->columnMax) | ||
|| (slavePtr->numRows+slavePtr->row == gridPtr->rowMax)) { | ||
} | ||
slavePtr->masterPtr = NULL; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* DestroyGrid -- | ||
* | ||
* This procedure is invoked by Tcl_EventuallyFree or Tcl_Release | ||
* to clean up the internal structure of a grid at a safe time | ||
* (when no-one is using it anymore). Cleaning up the grid involves | ||
* freeing the main structure for all windows. and the master structure | ||
* for geometry managers. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* Everything associated with the grid is freed up. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static void | ||
DestroyGrid(memPtr) | ||
char *memPtr; /* Info about window that is now dead. */ | ||
{ | ||
register Gridder *gridPtr = (Gridder *) memPtr; | ||
if (gridPtr->masterDataPtr != NULL) { | ||
if (gridPtr->masterDataPtr->rowPtr != NULL) { | ||
Tcl_Free((char *) gridPtr->masterDataPtr -> rowPtr); | ||
} | ||
if (gridPtr->masterDataPtr->columnPtr != NULL) { | ||
Tcl_Free((char *) gridPtr->masterDataPtr -> columnPtr); | ||
} | ||
Tcl_Free((char *) gridPtr->masterDataPtr); | ||
} | ||
Tcl_Free((char *) gridPtr); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* GridStructureProc -- | ||
* | ||
* 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 grid-related | ||
* information. If it was just resized, re-configure its slaves, if | ||
* any. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static void | ||
GridStructureProc(clientData, eventPtr) | ||
ClientData clientData; /* Our information about window | ||
* referred to by eventPtr. */ | ||
XEvent *eventPtr; /* Describes what just happened. */ | ||
{ | ||
register Gridder *gridPtr = (Gridder *) clientData; | ||
TkDisplay *dispPtr = ((TkWindow *) gridPtr->tkwin)->dispPtr; | ||
if (eventPtr->type == ConfigureNotify) { | ||
if (!(gridPtr->flags & REQUESTED_RELAYOUT)) { | ||
gridPtr->flags |= REQUESTED_RELAYOUT; | ||
Tcl_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; | ||
Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr->masterPtr); | ||
} | ||
} | ||
} else if (eventPtr->type == DestroyNotify) { | ||
register Gridder *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(&dispPtr->gridHashTable, | ||
(char *) gridPtr->tkwin)); | ||
if (gridPtr->flags & REQUESTED_RELAYOUT) { | ||
Tcl_CancelIdleCall(ArrangeGrid, (ClientData) gridPtr); | ||
} | ||
gridPtr->tkwin = NULL; | ||
Tcl_EventuallyFree((ClientData) gridPtr, DestroyGrid); | ||
} else if (eventPtr->type == MapNotify) { | ||
if (!(gridPtr->flags & REQUESTED_RELAYOUT)) { | ||
gridPtr->flags |= REQUESTED_RELAYOUT; | ||
Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr); | ||
} | ||
} else if (eventPtr->type == UnmapNotify) { | ||
register Gridder *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 | ||
* grid to manage the slaves and sets the specified options. | ||
* arguments consist of windows or window shortcuts followed by | ||
* "-option value" pairs. | ||
* | ||
* Results: | ||
* TCL_OK is returned if all went well. Otherwise, TCL_ERROR is | ||
* returned and the interp's result is set to contain an error message. | ||
* | ||
* Side effects: | ||
* Slave windows get taken over by the grid. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
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; /* Number 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. */ | ||
{ | ||
Gridder *masterPtr; | ||
Gridder *slavePtr; | ||
Tk_Window other, slave, parent, ancestor; | ||
int i, j, c, tmp; | ||
size_t length; | ||
int numWindows; | ||
int width; | ||
int defaultColumn = 0; /* default column number */ | ||
int defaultColumnSpan = 1; /* default number of columns */ | ||
char *lastWindow; /* use this window to base current | ||
* Row/col on */ | ||
/* | ||
* Count the number of windows, or window short-cuts. | ||
*/ | ||
for(numWindows=i=0;i<argc;i++) { | ||
char firstChar = *argv[i]; | ||
if (firstChar == '.') { | ||
numWindows++; | ||
continue; | ||
} | ||
length = strlen(argv[i]); | ||
if (length > 1 && firstChar == '-') { | ||
break; | ||
} | ||
if (length > 1) { | ||
Tcl_AppendResult(interp, "unexpected parameter, \"", | ||
argv[i], "\", in configure list. ", | ||
"Should be window name or option", (char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
if ((firstChar == REL_HORIZ) && ((numWindows == 0) || | ||
(*argv[i-1] == REL_SKIP) || (*argv[i-1] == REL_VERT))) { | ||
Tcl_AppendResult(interp, | ||
"Must specify window before shortcut '-'.", | ||
(char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
if ((firstChar == REL_VERT) || (firstChar == REL_SKIP) | ||
|| (firstChar == REL_HORIZ)) { | ||
continue; | ||
} | ||
Tcl_AppendResult(interp, "invalid window shortcut, \"", | ||
argv[i], "\" should be '-', 'x', or '^'", (char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
numWindows = i; | ||
if ((argc-numWindows)&1) { | ||
Tcl_AppendResult(interp, "extra option or", | ||
" option with no value", (char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Iterate over all of the slave windows and short-cuts, parsing | ||
* 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; | ||
for (j = 0; j < numWindows; j++) { | ||
char firstChar = *argv[j]; | ||
/* | ||
* '^' and 'x' cause us to skip a column. '-' is processed | ||
* as part of its preceeding slave. | ||
*/ | ||
if ((firstChar == REL_VERT) || (firstChar == REL_SKIP)) { | ||
defaultColumn++; | ||
continue; | ||
} | ||
if (firstChar == REL_HORIZ) { | ||
continue; | ||
} | ||
for (defaultColumnSpan=1; | ||
j + defaultColumnSpan < numWindows && | ||
(*argv[j+defaultColumnSpan] == REL_HORIZ); | ||
defaultColumnSpan++) { | ||
/* 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 = GetGrid(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 [D.S.] 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 the | ||
* defaults can be reestablished by hand, without having to worry | ||
* about keeping track of the old state. | ||
*/ | ||
for (i = numWindows; i < argc; i+=2) { | ||
length = strlen(argv[i]); | ||
c = argv[i][1]; | ||
if (length < 2) { | ||
Tcl_AppendResult(interp, "unknown or ambiguous option \"", | ||
argv[i], "\": must be ", | ||
"-column, -columnspan, -in, -ipadx, -ipady, ", | ||
"-padx, -pady, -row, -rowspan, or -sticky", | ||
(char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
if ((c == 'c') && (strncmp(argv[i], "-column", length) == 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->column = tmp; | ||
} else if ((c == 'c') | ||
&& (strncmp(argv[i], "-columnspan", length) == 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->numCols = tmp; | ||
} else if ((c == 'i') && (strncmp(argv[i], "-in", length) == 0)) { | ||
other = Tk_NameToWindow(interp, argv[i+1], tkwin); | ||
if (other == NULL) { | ||
return TCL_ERROR; | ||
} | ||
if (other == slave) { | ||
Tcl_SetResult(interp, "Window can't be managed in itself", | ||
TCL_STATIC); | ||
return TCL_ERROR; | ||
} | ||
masterPtr = GetGrid(other); | ||
InitMasterData(masterPtr); | ||
} else if ((c == 'i') | ||
&& (strncmp(argv[i], "-ipadx", length) == 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') | ||
&& (strncmp(argv[i], "-ipady", length) == 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') | ||
&& (strncmp(argv[i], "-padx", length) == 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') | ||
&& (strncmp(argv[i], "-pady", length) == 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 == 'r') && (strncmp(argv[i], "-row", length) == 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->row = tmp; | ||
} else if ((c == 'r') | ||
&& (strncmp(argv[i], "-rowspan", length) == 0)) { | ||
if ((Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK) || tmp<=0) { | ||
Tcl_ResetResult(interp); | ||
Tcl_AppendResult(interp, "bad rowspan value \"", argv[i+1], | ||
"\": must be a positive integer", (char *)NULL); | ||
return TCL_ERROR; | ||
} | ||
slavePtr->numRows = tmp; | ||
} else if ((c == 's') | ||
&& strncmp(argv[i], "-sticky", length) == 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->sticky = sticky; | ||
} else { | ||
Tcl_AppendResult(interp, "unknown or ambiguous option \"", | ||
argv[i], "\": must be ", | ||
"-column, -columnspan, -in, -ipadx, -ipady, ", | ||
"-padx, -pady, -row, -rowspan, or -sticky", | ||
(char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
} | ||
/* | ||
* Make sure we have a geometry master. We look at: | ||
* 1) the -in flag | ||
* 2) the geometry master of the first slave (if specified) | ||
* 3) the parent of the first slave. | ||
*/ | ||
if (masterPtr == NULL) { | ||
masterPtr = slavePtr->masterPtr; | ||
} | ||
parent = Tk_Parent(slave); | ||
if (masterPtr == NULL) { | ||
masterPtr = GetGrid(parent); | ||
InitMasterData(masterPtr); | ||
} | ||
if (slavePtr->masterPtr != NULL && slavePtr->masterPtr != masterPtr) { | ||
Unlink(slavePtr); | ||
slavePtr->masterPtr = NULL; | ||
} | ||
if (slavePtr->masterPtr == NULL) { | ||
Gridder *tempPtr = masterPtr->slavePtr; | ||
slavePtr->masterPtr = masterPtr; | ||
masterPtr->slavePtr = slavePtr; | ||
slavePtr->nextPtr = tempPtr; | ||
} | ||
/* | ||
* Make sure that the slave's parent is either the master or | ||
* an ancestor of the master, and that the master and slave | ||
* aren't the same. | ||
*/ | ||
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); | ||
Unlink(slavePtr); | ||
return TCL_ERROR; | ||
} | ||
} | ||
/* | ||
* Try to make sure our master isn't managed by us. | ||
*/ | ||
if (masterPtr->masterPtr == slavePtr) { | ||
Tcl_AppendResult(interp, "can't put ", argv[j], | ||
" inside ", Tk_PathName(masterPtr->tkwin), | ||
", would cause management loop.", | ||
(char *) NULL); | ||
Unlink(slavePtr); | ||
return TCL_ERROR; | ||
} | ||
Tk_ManageGeometry(slave, &gridMgrType, (ClientData) slavePtr); | ||
/* | ||
* Assign default position information. | ||
*/ | ||
if (slavePtr->column == -1) { | ||
slavePtr->column = defaultColumn; | ||
} | ||
slavePtr->numCols += defaultColumnSpan - 1; | ||
if (slavePtr->row == -1) { | ||
if (masterPtr->masterDataPtr == NULL) { | ||
slavePtr->row = 0; | ||
} else { | ||
slavePtr->row = masterPtr->masterDataPtr->rowEnd; | ||
} | ||
} | ||
defaultColumn += slavePtr->numCols; | ||
defaultColumnSpan = 1; | ||
/* | ||
* Arrange for the parent to be re-arranged at the first | ||
* idle moment. | ||
*/ | ||
if (masterPtr->abortPtr != NULL) { | ||
*masterPtr->abortPtr = 1; | ||
} | ||
if (!(masterPtr->flags & REQUESTED_RELAYOUT)) { | ||
masterPtr->flags |= REQUESTED_RELAYOUT; | ||
Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr); | ||
} | ||
} | ||
/* Now look for all the "^"'s. */ | ||
lastWindow = NULL; | ||
for (j = 0; j < numWindows; j++) { | ||
struct Gridder *otherPtr; | ||
int match; /* found a match for the ^ */ | ||
int lastRow, lastColumn; /* implied end of table */ | ||
if (*argv[j] == '.') { | ||
lastWindow = argv[j]; | ||
} | ||
if (*argv[j] != REL_VERT) { | ||
continue; | ||
} | ||
if (masterPtr == NULL) { | ||
Tcl_AppendResult(interp, "can't use '^', cant find master", | ||
(char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
/* Count the number of consecutive ^'s starting from this position */ | ||
for (width=1; width+j < numWindows && *argv[j+width] == REL_VERT; | ||
width++) { | ||
/* Null Body */ | ||
} | ||
/* | ||
* Find the implied grid location of the ^ | ||
*/ | ||
if (lastWindow == NULL) { | ||
if (masterPtr->masterDataPtr != NULL) { | ||
SetGridSize(masterPtr); | ||
lastRow = masterPtr->masterDataPtr->rowEnd - 2; | ||
} else { | ||
lastRow = 0; | ||
} | ||
lastColumn = 0; | ||
} else { | ||
other = Tk_NameToWindow(interp, lastWindow, tkwin); | ||
otherPtr = GetGrid(other); | ||
lastRow = otherPtr->row + otherPtr->numRows - 2; | ||
lastColumn = otherPtr->column + otherPtr->numCols; | ||
} | ||
for (match=0, slavePtr = masterPtr->slavePtr; slavePtr != NULL; | ||
slavePtr = slavePtr->nextPtr) { | ||
if (slavePtr->column == lastColumn | ||
&& slavePtr->row + slavePtr->numRows - 1 == lastRow) { | ||
if (slavePtr->numCols <= width) { | ||
slavePtr->numRows++; | ||
match++; | ||
j += slavePtr->numCols - 1; | ||
lastWindow = Tk_PathName(slavePtr->tkwin); | ||
break; | ||
} | ||
} | ||
} | ||
if (!match) { | ||
Tcl_AppendResult(interp, "can't find slave to extend with \"^\".", | ||
(char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
/* j += width - 1; */ | ||
} | ||
if (masterPtr == NULL) { | ||
Tcl_AppendResult(interp, "can't determine master window", | ||
(char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
SetGridSize(masterPtr); | ||
return TCL_OK; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* StickyToString | ||
* | ||
* Converts the internal boolean combination of "sticky" bits onto | ||
* a TCL list element containing zero or mor of n, s, e, or w. | ||
* | ||
* Results: | ||
* A string is placed into the "result" pointer. | ||
* | ||
* Side effects: | ||
* none. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
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,"{}"); | ||
} | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* StringToSticky -- | ||
* | ||
* Converts an ascii string representing a widgets stickyness | ||
* into the boolean result. | ||
* | ||
* Results: | ||
* The boolean combination of the "sticky" bits is retuned. If an | ||
* error occurs, such as an invalid character, -1 is returned instead. | ||
* | ||
* Side effects: | ||
* none | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
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; | ||
} | ||
/* $History: tkGrid.c $ | ||
* | ||
* ***************** Version 1 ***************** | ||
* User: Dtashley Date: 1/02/01 Time: 2:55a | ||
* Created in $/IjuScripter, IjuConsole/Source/Tk Base | ||
* Initial check-in. | ||
*/ | ||
/* End of TKGRID.C */ | ||
1 | /* $Header$ */ | |
2 | ||
3 | /* | |
4 | * tkGrid.c -- | |
5 | * | |
6 | * Grid based geometry manager. | |
7 | * | |
8 | * Copyright (c) 1996-1997 by Sun Microsystems, Inc. | |
9 | * | |
10 | * See the file "license.terms" for information on usage and redistribution | |
11 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. | |
12 | * | |
13 | * RCS: @(#) $Id: tkgrid.c,v 1.1.1.1 2001/06/13 05:02:05 dtashley Exp $ | |
14 | */ | |
15 | ||
16 | #include "tkInt.h" | |
17 | ||
18 | /* | |
19 | * Convenience Macros | |
20 | */ | |
21 | ||
22 | #ifdef MAX | |
23 | # undef MAX | |
24 | #endif | |
25 | #define MAX(x,y) ((x) > (y) ? (x) : (y)) | |
26 | #ifdef MIN | |
27 | # undef MIN | |
28 | #endif | |
29 | #define MIN(x,y) ((x) > (y) ? (y) : (x)) | |
30 | ||
31 | #define COLUMN (1) /* working on column offsets */ | |
32 | #define ROW (2) /* working on row offsets */ | |
33 | ||
34 | #define CHECK_ONLY (1) /* check max slot constraint */ | |
35 | #define CHECK_SPACE (2) /* alloc more space, don't change max */ | |
36 | ||
37 | /* | |
38 | * Pre-allocate enough row and column slots for "typical" sized tables | |
39 | * this value should be chosen so by the time the extra malloc's are | |
40 | * required, the layout calculations overwehlm them. [A "slot" contains | |
41 | * information for either a row or column, depending upon the context.] | |
42 | */ | |
43 | ||
44 | #define TYPICAL_SIZE 25 /* (arbitrary guess) */ | |
45 | #define PREALLOC 10 /* extra slots to allocate */ | |
46 | ||
47 | /* | |
48 | * Data structures are allocated dynamically to support arbitrary sized tables. | |
49 | * However, the space is proportional to the highest numbered slot with | |
50 | * some non-default property. This limit is used to head off mistakes and | |
51 | * denial of service attacks by limiting the amount of storage required. | |
52 | */ | |
53 | ||
54 | #define MAX_ELEMENT 10000 | |
55 | ||
56 | /* | |
57 | * Special characters to support relative layouts. | |
58 | */ | |
59 | ||
60 | #define REL_SKIP 'x' /* Skip this column. */ | |
61 | #define REL_HORIZ '-' /* Extend previous widget horizontally. */ | |
62 | #define REL_VERT '^' /* Extend widget from row above. */ | |
63 | ||
64 | /* | |
65 | * Structure to hold information for grid masters. A slot is either | |
66 | * a row or column. | |
67 | */ | |
68 | ||
69 | typedef struct SlotInfo { | |
70 | int minSize; /* The minimum size of this slot (in pixels). | |
71 | * It is set via the rowconfigure or | |
72 | * columnconfigure commands. */ | |
73 | int weight; /* The resize weight of this slot. (0) means | |
74 | * this slot doesn't resize. Extra space in | |
75 | * the layout is given distributed among slots | |
76 | * inproportion to their weights. */ | |
77 | int pad; /* Extra padding, in pixels, required for | |
78 | * this slot. This amount is "added" to the | |
79 | * largest slave in the slot. */ | |
80 | int offset; /* This is a cached value used for | |
81 | * introspection. It is the pixel | |
82 | * offset of the right or bottom edge | |
83 | * of this slot from the beginning of the | |
84 | * layout. */ | |
85 | int temp; /* This is a temporary value used for | |
86 | * calculating adjusted weights when | |
87 | * shrinking the layout below its | |
88 | * nominal size. */ | |
89 | } SlotInfo; | |
90 | ||
91 | /* | |
92 | * Structure to hold information during layout calculations. There | |
93 | * is one of these for each slot, an array for each of the rows or columns. | |
94 | */ | |
95 | ||
96 | typedef struct GridLayout { | |
97 | struct Gridder *binNextPtr; /* The next slave window in this bin. | |
98 | * Each bin contains a list of all | |
99 | * slaves whose spans are >1 and whose | |
100 | * right edges fall in this slot. */ | |
101 | int minSize; /* Minimum size needed for this slot, | |
102 | * in pixels. This is the space required | |
103 | * to hold any slaves contained entirely | |
104 | * in this slot, adjusted for any slot | |
105 | * constrants, such as size or padding. */ | |
106 | int pad; /* Padding needed for this slot */ | |
107 | int weight; /* Slot weight, controls resizing. */ | |
108 | int minOffset; /* The minimum offset, in pixels, from | |
109 | * the beginning of the layout to the | |
110 | * right/bottom edge of the slot calculated | |
111 | * from top/left to bottom/right. */ | |
112 | int maxOffset; /* The maximum offset, in pixels, from | |
113 | * the beginning of the layout to the | |
114 | * right-or-bottom edge of the slot calculated | |
115 | * from bottom-or-right to top-or-left. */ | |
116 | } GridLayout; | |
117 | ||
118 | /* | |
119 | * Keep one of these for each geometry master. | |
120 | */ | |
121 | ||
122 | typedef struct { | |
123 | SlotInfo *columnPtr; /* Pointer to array of column constraints. */ | |
124 | SlotInfo *rowPtr; /* Pointer to array of row constraints. */ | |
125 | int columnEnd; /* The last column occupied by any slave. */ | |
126 | int columnMax; /* The number of columns with constraints. */ | |
127 | int columnSpace; /* The number of slots currently allocated for | |
128 | * column constraints. */ | |
129 | int rowEnd; /* The last row occupied by any slave. */ | |
130 | int rowMax; /* The number of rows with constraints. */ | |
131 | int rowSpace; /* The number of slots currently allocated | |
132 | * for row constraints. */ | |
133 | int startX; /* Pixel offset of this layout within its | |
134 | * parent. */ | |
135 | int startY; /* Pixel offset of this layout within its | |
136 | * parent. */ | |
137 | } GridMaster; | |
138 | ||
139 | /* | |
140 | * For each window that the grid cares about (either because | |
141 | * the window is managed by the grid or because the window | |
142 | * has slaves that are managed by the grid), there is a | |
143 | * structure of the following type: | |
144 | */ | |
145 | ||
146 | typedef struct Gridder { | |
147 | Tk_Window tkwin; /* Tk token for window. NULL means that | |
148 | * the window has been deleted, but the | |
149 | * gridder hasn't had a chance to clean up | |
150 | * yet because the structure is still in | |
151 | * use. */ | |
152 | struct Gridder *masterPtr; /* Master window within which this window | |
153 | * is managed (NULL means this window | |
154 | * isn't managed by the gridder). */ | |
155 | struct Gridder *nextPtr; /* Next window managed within same | |
156 | * parent. List order doesn't matter. */ | |
157 | struct Gridder *slavePtr; /* First in list of slaves managed | |
158 | * inside this window (NULL means | |
159 | * no grid slaves). */ | |
160 | GridMaster *masterDataPtr; /* Additional data for geometry master. */ | |
161 | int column, row; /* Location in the grid (starting | |
162 | * from zero). */ | |
163 | int numCols, numRows; /* Number of columns or rows this slave spans. | |
164 | * Should be at least 1. */ | |
165 | int padX, padY; /* Total additional pixels to leave around the | |
166 | * window (half of this space is left on each | |
167 | * side). This is space *outside* the window: | |
168 | * we'll allocate extra space in frame but | |
169 | * won't enlarge window). */ | |
170 | int iPadX, iPadY; /* Total extra pixels to allocate inside the | |
171 | * window (half this amount will appear on | |
172 | * each side). */ | |
173 | int sticky; /* which sides of its cavity this window | |
174 | * sticks to. See below for definitions */ | |
175 | int doubleBw; /* Twice the window's last known border | |
176 | * width. If this changes, the window | |
177 | * must be re-arranged within its parent. */ | |
178 | int *abortPtr; /* If non-NULL, it means that there is a nested | |
179 | * call to ArrangeGrid already working on | |
180 | * this window. *abortPtr may be set to 1 to | |
181 | * abort that nested call. This happens, for | |
182 | * example, if tkwin or any of its slaves | |
183 | * is deleted. */ | |
184 | int flags; /* Miscellaneous flags; see below | |
185 | * for definitions. */ | |
186 | ||
187 | /* | |
188 | * These fields are used temporarily for layout calculations only. | |
189 | */ | |
190 | ||
191 | struct Gridder *binNextPtr; /* Link to next span>1 slave in this bin. */ | |
192 | int size; /* Nominal size (width or height) in pixels | |
193 | * of the slave. This includes the padding. */ | |
194 | } Gridder; | |
195 | ||
196 | /* Flag values for "sticky"ness The 16 combinations subsume the packer's | |
197 | * notion of anchor and fill. | |
198 | * | |
199 | * STICK_NORTH This window sticks to the top of its cavity. | |
200 | * STICK_EAST This window sticks to the right edge of its cavity. | |
201 | * STICK_SOUTH This window sticks to the bottom of its cavity. | |
202 | * STICK_WEST This window sticks to the left edge of its cavity. | |
203 | */ | |
204 | ||
205 | #define STICK_NORTH 1 | |
206 | #define STICK_EAST 2 | |
207 | #define STICK_SOUTH 4 | |
208 | #define STICK_WEST 8 | |
209 | ||
210 | /* | |
211 | * Flag values for Grid structures: | |
212 | * | |
213 | * REQUESTED_RELAYOUT: 1 means a Tcl_DoWhenIdle request | |
214 | * has already been made to re-arrange | |
215 | * all the slaves of this window. | |
216 | * | |
217 | * DONT_PROPAGATE: 1 means don't set this window's requested | |
218 | * size. 0 means if this window is a master | |
219 | * then Tk will set its requested size to fit | |
220 | * the needs of its slaves. | |
221 | */ | |
222 | ||
223 | #define REQUESTED_RELAYOUT 1 | |
224 | #define DONT_PROPAGATE 2 | |
225 | ||
226 | /* | |
227 | * Prototypes for procedures used only in this file: | |
228 | */ | |
229 | ||
230 | static void AdjustForSticky _ANSI_ARGS_((Gridder *slavePtr, int *xPtr, | |
231 | int *yPtr, int *widthPtr, int *heightPtr)); | |
232 | static int AdjustOffsets _ANSI_ARGS_((int width, | |
233 | int elements, SlotInfo *slotPtr)); | |
234 | static void ArrangeGrid _ANSI_ARGS_((ClientData clientData)); | |
235 | static int CheckSlotData _ANSI_ARGS_((Gridder *masterPtr, int slot, | |
236 | int slotType, int checkOnly)); | |
237 | static int ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp, | |
238 | Tk_Window tkwin, int argc, char *argv[])); | |
239 | static void DestroyGrid _ANSI_ARGS_((char *memPtr)); | |
240 | static Gridder *GetGrid _ANSI_ARGS_((Tk_Window tkwin)); | |
241 | static void GridStructureProc _ANSI_ARGS_(( | |
242 | ClientData clientData, XEvent *eventPtr)); | |
243 | static void GridLostSlaveProc _ANSI_ARGS_((ClientData clientData, | |
244 | Tk_Window tkwin)); | |
245 | static void GridReqProc _ANSI_ARGS_((ClientData clientData, | |
246 | Tk_Window tkwin)); | |
247 | static void InitMasterData _ANSI_ARGS_((Gridder *masterPtr)); | |
248 | static int ResolveConstraints _ANSI_ARGS_((Gridder *gridPtr, | |
249 | int rowOrColumn, int maxOffset)); | |
250 | static void SetGridSize _ANSI_ARGS_((Gridder *gridPtr)); | |
251 | static void StickyToString _ANSI_ARGS_((int flags, char *result)); | |
252 | static int StringToSticky _ANSI_ARGS_((char *string)); | |
253 | static void Unlink _ANSI_ARGS_((Gridder *gridPtr)); | |
254 | ||
255 | static Tk_GeomMgr gridMgrType = { | |
256 | "grid", /* name */ | |
257 | GridReqProc, /* requestProc */ | |
258 | GridLostSlaveProc, /* lostSlaveProc */ | |
259 | }; | |
260 | ||
261 | /* | |
262 | *-------------------------------------------------------------- | |
263 | * | |
264 | * Tk_GridCmd -- | |
265 | * | |
266 | * This procedure is invoked to process the "grid" Tcl command. | |
267 | * See the user documentation for details on what it does. | |
268 | * | |
269 | * Results: | |
270 | * A standard Tcl result. | |
271 | * | |
272 | * Side effects: | |
273 | * See the user documentation. | |
274 | * | |
275 | *-------------------------------------------------------------- | |
276 | */ | |
277 | ||
278 | int | |
279 | Tk_GridCmd(clientData, interp, argc, argv) | |
280 | ClientData clientData; /* Main window associated with | |
281 | * interpreter. */ | |
282 | Tcl_Interp *interp; /* Current interpreter. */ | |
283 | int argc; /* Number of arguments. */ | |
284 | char **argv; /* Argument strings. */ | |
285 | { | |
286 | Tk_Window tkwin = (Tk_Window) clientData; | |
287 | Gridder *masterPtr; /* master grid record */ | |
288 | GridMaster *gridPtr; /* pointer to grid data */ | |
289 | size_t length; /* streing length of argument */ | |
290 | char c; /* 1st character of argument */ | |
291 | ||
292 | if ((argc >= 2) && ((argv[1][0] == '.') || (argv[1][0] == REL_SKIP) || | |
293 | (argv[1][0] == REL_VERT))) { | |
294 | return ConfigureSlaves(interp, tkwin, argc-1, argv+1); | |
295 | } | |
296 | if (argc < 3) { | |
297 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
298 | argv[0], " option arg ?arg ...?\"", (char *) NULL); | |
299 | return TCL_ERROR; | |
300 | } | |
301 | c = argv[1][0]; | |
302 | length = strlen(argv[1]); | |
303 | ||
304 | if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) { | |
305 | Tk_Window master; | |
306 | int row, column; /* origin for bounding box */ | |
307 | int row2, column2; /* end of bounding box */ | |
308 | int endX, endY; /* last column/row in the layout */ | |
309 | int x=0, y=0; /* starting pixels for this bounding box */ | |
310 | int width, height; /* size of the bounding box */ | |
311 | char buf[TCL_INTEGER_SPACE * 4]; | |
312 | ||
313 | if (argc!=3 && argc != 5 && argc != 7) { | |
314 | Tcl_AppendResult(interp, "wrong number of arguments: ", | |
315 | "must be \"",argv[0], | |
316 | " bbox master ?column row ?column row??\"", | |
317 | (char *) NULL); | |
318 | return TCL_ERROR; | |
319 | } | |
320 | ||
321 | master = Tk_NameToWindow(interp, argv[2], tkwin); | |
322 | if (master == NULL) { | |
323 | return TCL_ERROR; | |
324 | } | |
325 | masterPtr = GetGrid(master); | |
326 | ||
327 | if (argc >= 5) { | |
328 | if (Tcl_GetInt(interp, argv[3], &column) != TCL_OK) { | |
329 | return TCL_ERROR; | |
330 | } | |
331 | if (Tcl_GetInt(interp, argv[4], &row) != TCL_OK) { | |
332 | return TCL_ERROR; | |
333 | } | |
334 | column2 = column; | |
335 | row2 = row; | |
336 | } | |
337 | ||
338 | if (argc == 7) { | |
339 | if (Tcl_GetInt(interp, argv[5], &column2) != TCL_OK) { | |
340 | return TCL_ERROR; | |
341 | } | |
342 | if (Tcl_GetInt(interp, argv[6], &row2) != TCL_OK) { | |
343 | return TCL_ERROR; | |
344 | } | |
345 | } | |
346 | ||
347 | gridPtr = masterPtr->masterDataPtr; | |
348 | if (gridPtr == NULL) { | |
349 | Tcl_SetResult(interp, "0 0 0 0", TCL_STATIC); | |
350 | return(TCL_OK); | |
351 | } | |
352 | ||
353 | SetGridSize(masterPtr); | |
354 | endX = MAX(gridPtr->columnEnd, gridPtr->columnMax); | |
355 | endY = MAX(gridPtr->rowEnd, gridPtr->rowMax); | |
356 | ||
357 | if ((endX == 0) || (endY == 0)) { | |
358 | Tcl_SetResult(interp, "0 0 0 0", TCL_STATIC); | |
359 | return(TCL_OK); | |
360 | } | |
361 | if (argc == 3) { | |
362 | row = column = 0; | |
363 | row2 = endY; | |
364 | column2 = endX; | |
365 | } | |
366 | ||
367 | if (column > column2) { | |
368 | int temp = column; | |
369 | column = column2, column2 = temp; | |
370 | } | |
371 | if (row > row2) { | |
372 | int temp = row; | |
373 | row = row2, row2 = temp; | |
374 | } | |
375 | ||
376 | if (column > 0 && column < endX) { | |
377 | x = gridPtr->columnPtr[column-1].offset; | |
378 | } else if (column > 0) { | |
379 | x = gridPtr->columnPtr[endX-1].offset; | |
380 | } | |
381 | ||
382 | if (row > 0 && row < endY) { | |
383 | y = gridPtr->rowPtr[row-1].offset; | |
384 | } else if (row > 0) { | |
385 | y = gridPtr->rowPtr[endY-1].offset; | |
386 | } | |
387 | ||
388 | if (column2 < 0) { | |
389 | width = 0; | |
390 | } else if (column2 >= endX) { | |
391 | width = gridPtr->columnPtr[endX-1].offset - x; | |
392 | } else { | |
393 | width = gridPtr->columnPtr[column2].offset - x; | |
394 | } | |
395 | ||
396 | if (row2 < 0) { | |
397 | height = 0; | |
398 | } else if (row2 >= endY) { | |
399 | height = gridPtr->rowPtr[endY-1].offset - y; | |
400 | } else { | |
401 | height = gridPtr->rowPtr[row2].offset - y; | |
402 | } | |
403 | ||
404 | sprintf(buf, "%d %d %d %d", x + gridPtr->startX, y + gridPtr->startY, | |
405 | width, height); | |
406 | Tcl_SetResult(interp, buf, TCL_VOLATILE); | |
407 | } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) { | |
408 | if (argv[2][0] != '.') { | |
409 | Tcl_AppendResult(interp, "bad argument \"", argv[2], | |
410 | "\": must be name of window", (char *) NULL); | |
411 | return TCL_ERROR; | |
412 | } | |
413 | return ConfigureSlaves(interp, tkwin, argc-2, argv+2); | |
414 | } else if (((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) || | |
415 | ((c == 'r') && (strncmp(argv[1], "remove", length) == 0))) { | |
416 | Tk_Window slave; | |
417 | Gridder *slavePtr; | |
418 | int i; | |
419 | ||
420 | for (i = 2; i < argc; i++) { | |
421 | slave = Tk_NameToWindow(interp, argv[i], tkwin); | |
422 | if (slave == NULL) { | |
423 | return TCL_ERROR; | |
424 | } | |
425 | slavePtr = GetGrid(slave); | |
426 | if (slavePtr->masterPtr != NULL) { | |
427 | ||
428 | /* | |
429 | * For "forget", reset all the settings to their defaults | |
430 | */ | |
431 | ||
432 | if (c == 'f') { | |
433 | slavePtr->column = slavePtr->row = -1; | |
434 | slavePtr->numCols = 1; | |
435 | slavePtr->numRows = 1; | |
436 | slavePtr->padX = slavePtr->padY = 0; | |
437 | slavePtr->iPadX = slavePtr->iPadY = 0; | |
438 | slavePtr->doubleBw = 2*Tk_Changes(tkwin)->border_width; | |
439 | if (slavePtr->flags & REQUESTED_RELAYOUT) { | |
440 | Tcl_CancelIdleCall(ArrangeGrid, (ClientData) slavePtr); | |
441 | } | |
442 | slavePtr->flags = 0; | |
443 | slavePtr->sticky = 0; | |
444 | } | |
445 | Tk_ManageGeometry(slave, (Tk_GeomMgr *) NULL, | |
446 | (ClientData) NULL); | |
447 | if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) { | |
448 | Tk_UnmaintainGeometry(slavePtr->tkwin, | |
449 | slavePtr->masterPtr->tkwin); | |
450 | } | |
451 | Unlink(slavePtr); | |
452 | Tk_UnmapWindow(slavePtr->tkwin); | |
453 | } | |
454 | } | |
455 | } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) { | |
456 | register Gridder *slavePtr; | |
457 | Tk_Window slave; | |
458 | char buffer[64 + TCL_INTEGER_SPACE * 4]; | |
459 | ||
460 | if (argc != 3) { | |
461 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
462 | argv[0], " info window\"", (char *) NULL); | |
463 | return TCL_ERROR; | |
464 | } | |
465 | slave = Tk_NameToWindow(interp, argv[2], tkwin); | |
466 | if (slave == NULL) { | |
467 | return TCL_ERROR; | |
468 | } | |
469 | slavePtr = GetGrid(slave); | |
470 | if (slavePtr->masterPtr == NULL) { | |
471 | Tcl_ResetResult(interp); | |
472 | return TCL_OK; | |
473 | } | |
474 | ||
475 | Tcl_AppendElement(interp, "-in"); | |
476 | Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin)); | |
477 | sprintf(buffer, " -column %d -row %d -columnspan %d -rowspan %d", | |
478 | slavePtr->column, slavePtr->row, | |
479 | slavePtr->numCols, slavePtr->numRows); | |
480 | Tcl_AppendResult(interp, buffer, (char *) NULL); | |
481 | sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d", | |
482 | slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2, | |
483 | slavePtr->padY/2); | |
484 | Tcl_AppendResult(interp, buffer, (char *) NULL); | |
485 | StickyToString(slavePtr->sticky,buffer); | |
486 | Tcl_AppendResult(interp, " -sticky ", buffer, (char *) NULL); | |
487 | } else if((c == 'l') && (strncmp(argv[1], "location", length) == 0)) { | |
488 | Tk_Window master; | |
489 | register SlotInfo *slotPtr; | |
490 | int x, y; /* Offset in pixels, from edge of parent. */ | |
491 | int i, j; /* Corresponding column and row indeces. */ | |
492 | int endX, endY; /* end of grid */ | |
493 | char buf[TCL_INTEGER_SPACE * 2]; | |
494 | ||
495 | if (argc != 5) { | |
496 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
497 | argv[0], " location master x y\"", (char *)NULL); | |
498 | return TCL_ERROR; | |
499 | } | |
500 | ||
501 | master = Tk_NameToWindow(interp, argv[2], tkwin); | |
502 | if (master == NULL) { | |
503 | return TCL_ERROR; | |
504 | } | |
505 | ||
506 | if (Tk_GetPixels(interp, master, argv[3], &x) != TCL_OK) { | |
507 | return TCL_ERROR; | |
508 | } | |
509 | if (Tk_GetPixels(interp, master, argv[4], &y) != TCL_OK) { | |
510 | return TCL_ERROR; | |
511 | } | |
512 | ||
513 | masterPtr = GetGrid(master); | |
514 | if (masterPtr->masterDataPtr == NULL) { | |
515 | Tcl_SetResult(interp, "-1 -1", TCL_STATIC); | |
516 | return TCL_OK; | |
517 | } | |
518 | gridPtr = masterPtr->masterDataPtr; | |
519 | ||
520 | /* | |
521 | * Update any pending requests. This is not always the | |
522 | * steady state value, as more configure events could be in | |
523 | * the pipeline, but its as close as its easy to get. | |
524 | */ | |
525 | ||
526 | while (masterPtr->flags & REQUESTED_RELAYOUT) { | |
527 | Tcl_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr); | |
528 | ArrangeGrid ((ClientData) masterPtr); | |
529 | } | |
530 | SetGridSize(masterPtr); | |
531 | endX = MAX(gridPtr->columnEnd, gridPtr->columnMax); | |
532 | endY = MAX(gridPtr->rowEnd, gridPtr->rowMax); | |
533 | ||
534 | slotPtr = masterPtr->masterDataPtr->columnPtr; | |
535 | if (x < masterPtr->masterDataPtr->startX) { | |
536 | i = -1; | |
537 | } else { | |
538 | x -= masterPtr->masterDataPtr->startX; | |
539 | for (i=0;slotPtr[i].offset < x && i < endX; i++) { | |
540 | /* null body */ | |
541 | } | |
542 | } | |
543 | ||
544 | slotPtr = masterPtr->masterDataPtr->rowPtr; | |
545 | if (y < masterPtr->masterDataPtr->startY) { | |
546 | j = -1; | |
547 | } else { | |
548 | y -= masterPtr->masterDataPtr->startY; | |
549 | for (j=0;slotPtr[j].offset < y && j < endY; j++) { | |
550 | /* null body */ | |
551 | } | |
552 | } | |
553 | ||
554 | sprintf(buf, "%d %d", i, j); | |
555 | Tcl_SetResult(interp, buf, TCL_VOLATILE); | |
556 | } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) { | |
557 | Tk_Window master; | |
558 | int propagate; | |
559 | ||
560 | if (argc > 4) { | |
561 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
562 | argv[0], " propagate window ?boolean?\"", | |
563 | (char *) NULL); | |
564 | return TCL_ERROR; | |
565 | } | |
566 | master = Tk_NameToWindow(interp, argv[2], tkwin); | |
567 | if (master == NULL) { | |
568 | return TCL_ERROR; | |
569 | } | |
570 | masterPtr = GetGrid(master); | |
571 | if (argc == 3) { | |
572 | Tcl_SetResult(interp, | |
573 | ((masterPtr->flags & DONT_PROPAGATE) ? "0" : "1"), | |
574 | TCL_STATIC); | |
575 | return TCL_OK; | |
576 | } | |
577 | if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) { | |
578 | return TCL_ERROR; | |
579 | } | |
580 | ||
581 | /* Only request a relayout if the propagation bit changes */ | |
582 | ||
583 | if ((!propagate) ^ (masterPtr->flags&DONT_PROPAGATE)) { | |
584 | if (propagate) { | |
585 | masterPtr->flags &= ~DONT_PROPAGATE; | |
586 | } else { | |
587 | masterPtr->flags |= DONT_PROPAGATE; | |
588 | } | |
589 | ||
590 | /* | |
591 | * Re-arrange the master to allow new geometry information to | |
592 | * propagate upwards to the master's master. | |
593 | */ | |
594 | ||
595 | if (masterPtr->abortPtr != NULL) { | |
596 | *masterPtr->abortPtr = 1; | |
597 | } | |
598 | if (!(masterPtr->flags & REQUESTED_RELAYOUT)) { | |
599 | masterPtr->flags |= REQUESTED_RELAYOUT; | |
600 | Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr); | |
601 | } | |
602 | } | |
603 | } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0) | |
604 | && (length > 1)) { | |
605 | Tk_Window master; | |
606 | ||
607 | if (argc != 3) { | |
608 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
609 | argv[0], " size window\"", (char *) NULL); | |
610 | return TCL_ERROR; | |
611 | } | |
612 | master = Tk_NameToWindow(interp, argv[2], tkwin); | |
613 | if (master == NULL) { | |
614 | return TCL_ERROR; | |
615 | } | |
616 | masterPtr = GetGrid(master); | |
617 | ||
618 | if (masterPtr->masterDataPtr != NULL) { | |
619 | char buf[TCL_INTEGER_SPACE * 2]; | |
620 | ||
621 | SetGridSize(masterPtr); | |
622 | gridPtr = masterPtr->masterDataPtr; | |
623 | sprintf(buf, "%d %d", | |
624 | MAX(gridPtr->columnEnd, gridPtr->columnMax), | |
625 | MAX(gridPtr->rowEnd, gridPtr->rowMax)); | |
626 | Tcl_SetResult(interp, buf, TCL_VOLATILE); | |
627 | } else { | |
628 | Tcl_SetResult(interp, "0 0", TCL_STATIC); | |
629 | } | |
630 | } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0) | |
631 | && (length > 1)) { | |
632 | Tk_Window master; | |
633 | Gridder *slavePtr; | |
634 | int i, value; | |
635 | int row = -1, column = -1; | |
636 | ||
637 | if ((argc < 3) || ((argc%2) == 0)) { | |
638 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
639 | argv[0], " slaves window ?-option value...?\"", | |
640 | (char *) NULL); | |
641 | return TCL_ERROR; | |
642 | } | |
643 | ||
644 | for (i=3; i<argc; i+=2) { | |
645 | length = strlen(argv[i]); | |
646 | if ((*argv[i] != '-') || (length < 2)) { | |
647 | Tcl_AppendResult(interp, "invalid args: should be \"", | |
648 | argv[0], " slaves window ?-option value...?\"", | |
649 | (char *) NULL); | |
650 | return TCL_ERROR; | |
651 | } | |
652 | if (Tcl_GetInt(interp, argv[i+1], &value) != TCL_OK) { | |
653 | return TCL_ERROR; | |
654 | } | |
655 | if (value < 0) { | |
656 | Tcl_AppendResult(interp, argv[i], | |
657 | " is an invalid value: should NOT be < 0", | |
658 | (char *) NULL); | |
659 | return TCL_ERROR; | |
660 | } | |
661 | if (strncmp(argv[i], "-column", length) == 0) { | |
662 | column = value; | |
663 | } else if (strncmp(argv[i], "-row", length) == 0) { | |
664 | row = value; | |
665 | } else { | |
666 | Tcl_AppendResult(interp, argv[i], | |
667 | " is an invalid option: should be \"", | |
668 | "-row, -column\"", | |
669 | (char *) NULL); | |
670 | return TCL_ERROR; | |
671 | } | |
672 | } | |
673 | master = Tk_NameToWindow(interp, argv[2], tkwin); | |
674 | if (master == NULL) { | |
675 | return TCL_ERROR; | |
676 | } | |
677 | masterPtr = GetGrid(master); | |
678 | ||
679 | for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; | |
680 | slavePtr = slavePtr->nextPtr) { | |
681 | if (column>=0 && (slavePtr->column > column | |
682 | || slavePtr->column+slavePtr->numCols-1 < column)) { | |
683 | continue; | |
684 | } | |
685 | if (row>=0 && (slavePtr->row > row || | |
686 | slavePtr->row+slavePtr->numRows-1 < row)) { | |
687 | continue; | |
688 | } | |
689 | Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin)); | |
690 | } | |
691 | ||
692 | /* | |
693 | * Sample argument combinations: | |
694 | * grid columnconfigure <master> <index> -option | |
695 | * grid columnconfigure <master> <index> -option value -option value | |
696 | * grid rowconfigure <master> <index> | |
697 | * grid rowconfigure <master> <index> -option | |
698 | * grid rowconfigure <master> <index> -option value -option value. | |
699 | */ | |
700 | ||
701 | } else if(((c == 'c') && (strncmp(argv[1], "columnconfigure", length) == 0) | |
702 | && (length >= 3)) || | |
703 | ((c == 'r') && (strncmp(argv[1], "rowconfigure", length) == 0) | |
704 | && (length >=2))) { | |
705 | Tk_Window master; | |
706 | SlotInfo *slotPtr = NULL; | |
707 | int slot; /* the column or row number */ | |
708 | size_t length; /* the # of chars in the "-option" string */ | |
709 | int slotType; /* COLUMN or ROW */ | |
710 | int size; /* the configuration value */ | |
711 | int checkOnly; /* check the size only */ | |
712 | int argcPtr; /* Number of items in index list */ | |
713 | char **argvPtr; /* array of indeces */ | |
714 | char **indexP; /* String value of current index list item. */ | |
715 | int ok; /* temporary TCL result code */ | |
716 | int i; | |
717 | ||
718 | if (((argc%2 != 0) && (argc>6)) || (argc < 4)) { | |
719 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], | |
720 | " ", argv[1], " master index ?-option value...?\"", | |
721 | (char *)NULL); | |
722 | return TCL_ERROR; | |
723 | } | |
724 | ||
725 | master = Tk_NameToWindow(interp, argv[2], tkwin); | |
726 | if (master == NULL) { | |
727 | return TCL_ERROR; | |
728 | } | |
729 | ||
730 | if (Tcl_SplitList(interp, argv[3], &argcPtr, &argvPtr) != TCL_OK) { | |
731 | return TCL_ERROR; | |
732 | } | |
733 | ||
734 | checkOnly = ((argc == 4) || (argc == 5)); | |
735 | masterPtr = GetGrid(master); | |
736 | slotType = (c == 'c') ? COLUMN : ROW; | |
737 | if (checkOnly && argcPtr > 1) { | |
738 | Tcl_AppendResult(interp, argv[3], | |
739 | " must be a single element.", (char *) NULL); | |
740 | Tcl_Free((char *)argvPtr); | |
741 | return TCL_ERROR; | |
742 | } | |
743 | for (indexP=argvPtr; *indexP != NULL; indexP++) { | |
744 | if (Tcl_GetInt(interp, *indexP, &slot) != TCL_OK) { | |
745 | Tcl_Free((char *)argvPtr); | |
746 | return TCL_ERROR; | |
747 | } | |
748 | ok = CheckSlotData(masterPtr, slot, slotType, checkOnly); | |
749 | if ((ok!=TCL_OK) && ((argc<4) || (argc>5))) { | |
750 | Tcl_AppendResult(interp, argv[0], | |
751 | " ", argv[1], ": \"", *argvPtr,"\" is out of range", | |
752 | (char *) NULL); | |
753 | Tcl_Free((char *)argvPtr); | |
754 | return TCL_ERROR; | |
755 | } else if (ok == TCL_OK) { | |
756 | slotPtr = (slotType == COLUMN) ? | |
757 | masterPtr->masterDataPtr->columnPtr : | |
758 | masterPtr->masterDataPtr->rowPtr; | |
759 | } | |
760 | ||
761 | /* | |
762 | * Return all of the options for this row or column. If the | |
763 | * request is out of range, return all 0's. | |
764 | */ | |
765 | ||
766 | if (argc == 4) { | |
767 | Tcl_Free((char *)argvPtr); | |
768 | } | |
769 | if ((argc == 4) && (ok == TCL_OK)) { | |
770 | char buf[64 + TCL_INTEGER_SPACE * 3]; | |
771 | ||
772 | sprintf(buf, "-minsize %d -pad %d -weight %d", | |
773 | slotPtr[slot].minSize,slotPtr[slot].pad, | |
774 | slotPtr[slot].weight); | |
775 | Tcl_SetResult(interp, buf, TCL_VOLATILE); | |
776 | return (TCL_OK); | |
777 | } else if (argc == 4) { | |
778 | Tcl_SetResult(interp, "-minsize 0 -pad 0 -weight 0", | |
779 | TCL_STATIC); | |
780 | return (TCL_OK); | |
781 | } | |
782 | ||
783 | /* | |
784 | * Loop through each option value pair, setting the values as required. | |
785 | * If only one option is given, with no value, the current value is | |
786 | * returned. | |
787 | */ | |
788 | ||
789 | for (i=4; i<argc; i+=2) { | |
790 | length = strlen(argv[i]); | |
791 | if ((*argv[i] != '-') || length < 2) { | |
792 | Tcl_AppendResult(interp, "invalid arg \"", | |
793 | argv[i], "\" :expecting -minsize, -pad, or -weight.", | |
794 | (char *) NULL); | |
795 | Tcl_Free((char *)argvPtr); | |
796 | return TCL_ERROR; | |
797 | } | |
798 | if (strncmp(argv[i], "-minsize", length) == 0) { | |
799 | if (argc == 5) { | |
800 | char buf[TCL_INTEGER_SPACE]; | |
801 | int value; | |
802 | ||
803 | value = (ok == TCL_OK) ? slotPtr[slot].minSize : 0; | |
804 | sprintf(buf, "%d", value); | |
805 | Tcl_SetResult(interp, buf, TCL_VOLATILE); | |
806 | } else if (Tk_GetPixels(interp, master, argv[i+1], &size) | |
807 | != TCL_OK) { | |
808 | Tcl_Free((char *)argvPtr); | |
809 | return TCL_ERROR; | |
810 | } else { | |
811 | slotPtr[slot].minSize = size; | |
812 | } | |
813 | } | |
814 | else if (strncmp(argv[i], "-weight", length) == 0) { | |
815 | int wt; | |
816 | if (argc == 5) { | |
817 | char buf[TCL_INTEGER_SPACE]; | |
818 | int value; | |
819 | ||
820 | value = (ok == TCL_OK) ? slotPtr[slot].weight : 0; | |
821 | sprintf(buf, "%d", value); | |
822 | Tcl_SetResult(interp, buf, TCL_VOLATILE); | |
823 | } else if (Tcl_GetInt(interp, argv[i+1], &wt) != TCL_OK) { | |
824 | Tcl_Free((char *)argvPtr); | |
825 | return TCL_ERROR; | |
826 | } else if (wt < 0) { | |
827 | Tcl_AppendResult(interp, "invalid arg \"", argv[i], | |
828 | "\": should be non-negative", (char *) NULL); | |
829 | Tcl_Free((char *)argvPtr); | |
830 | return TCL_ERROR; | |
831 | } else { | |
832 | slotPtr[slot].weight = wt; | |
833 | } | |
834 | } | |
835 | else if (strncmp(argv[i], "-pad", length) == 0) { | |
836 | if (argc == 5) { | |
837 | char buf[TCL_INTEGER_SPACE]; | |
838 | int value; | |
839 | ||
840 | value = (ok == TCL_OK) ? slotPtr[slot].pad : 0; | |
841 | sprintf(buf, "%d", value); | |
842 | Tcl_SetResult(interp, buf, TCL_VOLATILE); | |
843 | } else if (Tk_GetPixels(interp, master, argv[i+1], &size) | |
844 | != TCL_OK) { | |
845 | Tcl_Free((char *)argvPtr); | |
846 | return TCL_ERROR; | |
847 | } else if (size < 0) { | |
848 | Tcl_AppendResult(interp, "invalid arg \"", argv[i], | |
849 | "\": should be non-negative", (char *) NULL); | |
850 | Tcl_Free((char *)argvPtr); | |
851 | return TCL_ERROR; | |
852 | } else { | |
853 | slotPtr[slot].pad = size; | |
854 | } | |
855 | } else { | |
856 | Tcl_AppendResult(interp, "invalid arg \"", | |
857 | argv[i], "\": expecting -minsize, -pad, or -weight.", | |
858 | (char *) NULL); | |
859 | Tcl_Free((char *)argvPtr); | |
860 | return TCL_ERROR; | |
861 | } | |
862 | } | |
863 | } | |
864 | Tcl_Free((char *)argvPtr); | |
865 | ||
866 | /* | |
867 | * If we changed a property, re-arrange the table, | |
868 | * and check for constraint shrinkage. | |
869 | */ | |
870 | ||
871 | if (argc != 5) { | |
872 | if (slotType == ROW) { | |
873 | int last = masterPtr->masterDataPtr->rowMax - 1; | |
874 | while ((last >= 0) && (slotPtr[last].weight == 0) | |
875 | && (slotPtr[last].pad == 0) | |
876 | && (slotPtr[last].minSize == 0)) { | |
877 | last--; | |
878 | } | |
879 | masterPtr->masterDataPtr->rowMax = last+1; | |
880 | } else { | |
881 | int last = masterPtr->masterDataPtr->columnMax - 1; | |
882 | while ((last >= 0) && (slotPtr[last].weight == 0) | |
883 | && (slotPtr[last].pad == 0) | |
884 | && (slotPtr[last].minSize == 0)) { | |
885 | last--; | |
886 | } | |
887 | masterPtr->masterDataPtr->columnMax = last + 1; | |
888 | } | |
889 | ||
890 | if (masterPtr->abortPtr != NULL) { | |
891 | *masterPtr->abortPtr = 1; | |
892 | } | |
893 | if (!(masterPtr->flags & REQUESTED_RELAYOUT)) { | |
894 | masterPtr->flags |= REQUESTED_RELAYOUT; | |
895 | Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr); | |
896 | } | |
897 | } | |
898 | } else { | |
899 | Tcl_AppendResult(interp, "bad option \"", argv[1], | |
900 | "\": must be bbox, columnconfigure, configure, forget, info, ", | |
901 | "location, propagate, remove, rowconfigure, size, or slaves.", | |
902 | (char *) NULL); | |
903 | return TCL_ERROR; | |
904 | } | |
905 | return TCL_OK; | |
906 | } | |
907 | ||
908 | /* | |
909 | *-------------------------------------------------------------- | |
910 | * | |
911 | * GridReqProc -- | |
912 | * | |
913 | * This procedure is invoked by Tk_GeometryRequest for | |
914 | * windows managed by the grid. | |
915 | * | |
916 | * Results: | |
917 | * None. | |
918 | * | |
919 | * Side effects: | |
920 | * Arranges for tkwin, and all its managed siblings, to | |
921 | * be re-arranged at the next idle point. | |
922 | * | |
923 | *-------------------------------------------------------------- | |
924 | */ | |
925 | ||
926 | static void | |
927 | GridReqProc(clientData, tkwin) | |
928 | ClientData clientData; /* Grid's information about | |
929 | * window that got new preferred | |
930 | * geometry. */ | |
931 | Tk_Window tkwin; /* Other Tk-related information | |
932 | * about the window. */ | |
933 | { | |
934 | register Gridder *gridPtr = (Gridder *) clientData; | |
935 | ||
936 | gridPtr = gridPtr->masterPtr; | |
937 | if (!(gridPtr->flags & REQUESTED_RELAYOUT)) { | |
938 | gridPtr->flags |= REQUESTED_RELAYOUT; | |
939 | Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr); | |
940 | } | |
941 | } | |
942 | ||
943 | /* | |
944 | *-------------------------------------------------------------- | |
945 | * | |
946 | * GridLostSlaveProc -- | |
947 | * | |
948 | * This procedure is invoked by Tk whenever some other geometry | |
949 | * claims control over a slave that used to be managed by us. | |
950 | * | |
951 | * Results: | |
952 | * None. | |
953 | * | |
954 | * Side effects: | |
955 | * Forgets all grid-related information about the slave. | |
956 | * | |
957 | *-------------------------------------------------------------- | |
958 | */ | |
959 | ||
960 | static void | |
961 | GridLostSlaveProc(clientData, tkwin) | |
962 | ClientData clientData; /* Grid structure for slave window that | |
963 | * was stolen away. */ | |
964 | Tk_Window tkwin; /* Tk's handle for the slave window. */ | |
965 | { | |
966 | register Gridder *slavePtr = (Gridder *) clientData; | |
967 | ||
968 | if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) { | |
969 | Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin); | |
970 | } | |
971 | Unlink(slavePtr); | |
972 | Tk_UnmapWindow(slavePtr->tkwin); | |
973 | } | |
974 | ||
975 | /* | |
976 | *-------------------------------------------------------------- | |
977 | * | |
978 | * AdjustOffsets -- | |
979 | * | |
980 | * This procedure adjusts the size of the layout to fit in the | |
981 | * space provided. If it needs more space, the extra is added | |
982 | * according to the weights. If it needs less, the space is removed | |
983 | * according to the weights, but at no time does the size drop below | |
984 | * the minsize specified for that slot. | |
985 | * | |
986 | * Results: | |
987 | * The initial offset of the layout, | |
988 | * if all the weights are zero, else 0. | |
989 | * | |
990 | * Side effects: | |
991 | * The slot offsets are modified to shrink the layout. | |
992 | * | |
993 | *-------------------------------------------------------------- | |
994 | */ | |
995 | ||
996 | static int | |
997 | AdjustOffsets(size, slots, slotPtr) | |
998 | int size; /* The total layout size (in pixels). */ | |
999 | int slots; /* Number of slots. */ | |
1000 | register SlotInfo *slotPtr; /* Pointer to slot array. */ | |
1001 | { | |
1002 | register int slot; /* Current slot. */ | |
1003 | int diff; /* Extra pixels needed to add to the layout. */ | |
1004 | int totalWeight = 0; /* Sum of the weights for all the slots. */ | |
1005 | int weight = 0; /* Sum of the weights so far. */ | |
1006 | int minSize = 0; /* Minimum possible layout size. */ | |
1007 | int newDiff; /* The most pixels that can be added on | |
1008 | * the current pass. */ | |
1009 | ||
1010 | diff = size - slotPtr[slots-1].offset; | |
1011 | ||
1012 | /* | |
1013 | * The layout is already the correct size; all done. | |
1014 | */ | |
1015 | ||
1016 | if (diff == 0) { | |
1017 | return(0); | |
1018 | } | |
1019 | ||
1020 | /* | |
1021 | * If all the weights are zero, center the layout in its parent if | |
1022 | * there is extra space, else clip on the bottom/right. | |
1023 | */ | |
1024 | ||
1025 | for (slot=0; slot < slots; slot++) { | |
1026 | totalWeight += slotPtr[slot].weight; | |
1027 | } | |
1028 | ||
1029 | if (totalWeight == 0 ) { | |
1030 | return(diff > 0 ? diff/2 : 0); | |
1031 | } | |
1032 | ||
1033 | /* | |
1034 | * Add extra space according to the slot weights. This is done | |
1035 | * cumulatively to prevent round-off error accumulation. | |
1036 | */ | |
1037 | ||
1038 | if (diff > 0) { | |
1039 | for (weight=slot=0; slot < slots; slot++) { | |
1040 | weight += slotPtr[slot].weight; | |
1041 | slotPtr[slot].offset += diff * weight / totalWeight; | |
1042 | } | |
1043 | return(0); | |
1044 | } | |
1045 | ||
1046 | /* | |
1047 | * The layout must shrink below its requested size. Compute the | |
1048 | * minimum possible size by looking at the slot minSizes. | |
1049 | */ | |
1050 | ||
1051 | for (slot=0; slot < slots; slot++) { | |
1052 | if (slotPtr[slot].weight > 0) { | |
1053 | minSize += slotPtr[slot].minSize; | |
1054 | } else if (slot > 0) { | |
1055 | minSize += slotPtr[slot].offset - slotPtr[slot-1].offset; | |
1056 | } else { | |
1057 | minSize += slotPtr[slot].offset; | |
1058 | } | |
1059 | } | |
1060 | ||
1061 | /* | |
1062 | * If the requested size is less than the minimum required size, | |
1063 | * set the slot sizes to their minimum values, then clip on the | |
1064 | * bottom/right. | |
1065 | */ | |
1066 | ||
1067 | if (size <= minSize) { | |
1068 | int offset = 0; | |
1069 | for (slot=0; slot < slots; slot++) { | |
1070 | if (slotPtr[slot].weight > 0) { | |
1071 | offset += slotPtr[slot].minSize; | |
1072 | } else if (slot > 0) { | |
1073 | offset += slotPtr[slot].offset - slotPtr[slot-1].offset; | |
1074 | } else { | |
1075 | offset += slotPtr[slot].offset; | |
1076 | } | |
1077 | slotPtr[slot].offset = offset; | |
1078 | } | |
1079 | return(0); | |
1080 | } | |
1081 | ||
1082 | /* | |
1083 | * Remove space from slots according to their weights. The weights | |
1084 | * get renormalized anytime a slot shrinks to its minimum size. | |
1085 | */ | |
1086 | ||
1087 | while (diff < 0) { | |
1088 | ||
1089 | /* | |
1090 | * Find the total weight for the shrinkable slots. | |
1091 | */ | |
1092 | ||
1093 | for (totalWeight=slot=0; slot < slots; slot++) { | |
1094 | int current = (slot == 0) ? slotPtr[slot].offset : | |
1095 | slotPtr[slot].offset - slotPtr[slot-1].offset; | |
1096 | if (current > slotPtr[slot].minSize) { | |
1097 | totalWeight += slotPtr[slot].weight; | |
1098 | slotPtr[slot].temp = slotPtr[slot].weight; | |
1099 | } else { | |
1100 | slotPtr[slot].temp = 0; | |
1101 | } | |
1102 | } | |
1103 | if (totalWeight == 0) { | |
1104 | break; | |
1105 | } | |
1106 | ||
1107 | /* | |
1108 | * Find the maximum amount of space we can distribute this pass. | |
1109 | */ | |
1110 | ||
1111 | newDiff = diff; | |
1112 | for (slot = 0; slot < slots; slot++) { | |
1113 | int current; /* current size of this slot */ | |
1114 | int maxDiff; /* max diff that would cause | |
1115 | * this slot to equal its minsize */ | |
1116 | if (slotPtr[slot].temp == 0) { | |
1117 | continue; | |
1118 | } | |
1119 | current = (slot == 0) ? slotPtr[slot].offset : | |
1120 | slotPtr[slot].offset - slotPtr[slot-1].offset; | |
1121 | maxDiff = totalWeight * (slotPtr[slot].minSize - current) | |
1122 | / slotPtr[slot].temp; | |
1123 | if (maxDiff > newDiff) { | |
1124 | newDiff = maxDiff; | |
1125 | } | |
1126 | } | |
1127 | ||
1128 | /* | |
1129 | * Now distribute the space. | |
1130 | */ | |
1131 | ||
1132 | for (weight=slot=0; slot < slots; slot++) { | |
1133 | weight += slotPtr[slot].temp; | |
1134 | slotPtr[slot].offset += newDiff * weight / totalWeight; | |
1135 | } | |
1136 | diff -= newDiff; | |
1137 | } | |
1138 | return(0); | |
1139 | } | |
1140 | ||
1141 | /* | |
1142 | *-------------------------------------------------------------- | |
1143 | * | |
1144 | * AdjustForSticky -- | |
1145 | * | |
1146 | * This procedure adjusts the size of a slave in its cavity based | |
1147 | * on its "sticky" flags. | |
1148 | * | |
1149 | * Results: | |
1150 | * The input x, y, width, and height are changed to represent the | |
1151 | * desired coordinates of the slave. | |
1152 | * | |
1153 | * Side effects: | |
1154 | * None. | |
1155 | * | |
1156 | *-------------------------------------------------------------- | |
1157 | */ | |
1158 | ||
1159 | static void | |
1160 | AdjustForSticky(slavePtr, xPtr, yPtr, widthPtr, heightPtr) | |
1161 | Gridder *slavePtr; /* Slave window to arrange in its cavity. */ | |
1162 | int *xPtr; /* Pixel location of the left edge of the cavity. */ | |
1163 | int *yPtr; /* Pixel location of the top edge of the cavity. */ | |
1164 | int *widthPtr; /* Width of the cavity (in pixels). */ | |
1165 | int *heightPtr; /* Height of the cavity (in pixels). */ | |
1166 | { | |
1167 | int diffx=0; /* Cavity width - slave width. */ | |
1168 | int diffy=0; /* Cavity hight - slave height. */ | |
1169 | int sticky = slavePtr->sticky; | |
1170 | ||
1171 | *xPtr += slavePtr->padX/2; | |
1172 | *widthPtr -= slavePtr->padX; | |
1173 | *yPtr += slavePtr->padY/2; | |
1174 | *heightPtr -= slavePtr->padY; | |
1175 | ||
1176 | if (*widthPtr > (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX)) { | |
1177 | diffx = *widthPtr - (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX); | |
1178 | *widthPtr = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX; | |
1179 | } | |
1180 | ||
1181 | if (*heightPtr > (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY)) { | |
1182 | diffy = *heightPtr - (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY); | |
1183 | *heightPtr = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY; | |
1184 | } | |
1185 | ||
1186 | if (sticky&STICK_EAST && sticky&STICK_WEST) { | |
1187 | *widthPtr += diffx; | |
1188 | } | |
1189 | if (sticky&STICK_NORTH && sticky&STICK_SOUTH) { | |
1190 | *heightPtr += diffy; | |
1191 | } | |
1192 | if (!(sticky&STICK_WEST)) { | |
1193 | *xPtr += (sticky&STICK_EAST) ? diffx : diffx/2; | |
1194 | } | |
1195 | if (!(sticky&STICK_NORTH)) { | |
1196 | *yPtr += (sticky&STICK_SOUTH) ? diffy : diffy/2; | |
1197 | } | |
1198 | } | |
1199 | ||
1200 | /* | |
1201 | *-------------------------------------------------------------- | |
1202 | * | |
1203 | * ArrangeGrid -- | |
1204 | * | |
1205 | * This procedure is invoked (using the Tcl_DoWhenIdle | |
1206 | * mechanism) to re-layout a set of windows managed by | |
1207 | * the grid. It is invoked at idle time so that a | |
1208 | * series of grid requests can be merged into a single | |
1209 | * layout operation. | |
1210 | * | |
1211 | * Results: | |
1212 | * None. | |
1213 | * | |
1214 | * Side effects: | |
1215 | * The slaves of masterPtr may get resized or moved. | |
1216 | * | |
1217 | *-------------------------------------------------------------- | |
1218 | */ | |
1219 | ||
1220 | static void | |
1221 | ArrangeGrid(clientData) | |
1222 | ClientData clientData; /* Structure describing parent whose slaves | |
1223 | * are to be re-layed out. */ | |
1224 | { | |
1225 | register Gridder *masterPtr = (Gridder *) clientData; | |
1226 | register Gridder *slavePtr; | |
1227 | GridMaster *slotPtr = masterPtr->masterDataPtr; | |
1228 | int abort; | |
1229 | int width, height; /* requested size of layout, in pixels */ | |
1230 | int realWidth, realHeight; /* actual size layout should take-up */ | |
1231 | ||
1232 | masterPtr->flags &= ~REQUESTED_RELAYOUT; | |
1233 | ||
1234 | /* | |
1235 | * If the parent has no slaves anymore, then don't do anything | |
1236 | * at all: just leave the parent's size as-is. Otherwise there is | |
1237 | * no way to "relinquish" control over the parent so another geometry | |
1238 | * manager can take over. | |
1239 | */ | |
1240 | ||
1241 | if (masterPtr->slavePtr == NULL) { | |
1242 | return; | |
1243 | } | |
1244 | ||
1245 | if (masterPtr->masterDataPtr == NULL) { | |
1246 | return; | |
1247 | } | |
1248 | ||
1249 | /* | |
1250 | * Abort any nested call to ArrangeGrid for this window, since | |
1251 | * we'll do everything necessary here, and set up so this call | |
1252 | * can be aborted if necessary. | |
1253 | */ | |
1254 | ||
1255 | if (masterPtr->abortPtr != NULL) { | |
1256 | *masterPtr->abortPtr = 1; | |
1257 | } | |
1258 | masterPtr->abortPtr = &abort; | |
1259 | abort = 0; | |
1260 | Tcl_Preserve((ClientData) masterPtr); | |
1261 | ||
1262 | /* | |
1263 | * Call the constraint engine to fill in the row and column offsets. | |
1264 | */ | |
1265 | ||
1266 | SetGridSize(masterPtr); | |
1267 | width = ResolveConstraints(masterPtr, COLUMN, 0); | |
1268 | height = ResolveConstraints(masterPtr, ROW, 0); | |
1269 | width += 2*Tk_InternalBorderWidth(masterPtr->tkwin); | |
1270 | height += 2*Tk_InternalBorderWidth(masterPtr->tkwin); | |
1271 | ||
1272 | if (((width != Tk_ReqWidth(masterPtr->tkwin)) | |
1273 | || (height != Tk_ReqHeight(masterPtr->tkwin))) | |
1274 | && !(masterPtr->flags & DONT_PROPAGATE)) { | |
1275 | Tk_GeometryRequest(masterPtr->tkwin, width, height); | |
1276 | if (width>1 && height>1) { | |
1277 | masterPtr->flags |= REQUESTED_RELAYOUT; | |
1278 | Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr); | |
1279 | } | |
1280 | masterPtr->abortPtr = NULL; | |
1281 | Tcl_Release((ClientData) masterPtr); | |
1282 | return; | |
1283 | } | |
1284 | ||
1285 | /* | |
1286 | * If the currently requested layout size doesn't match the parent's | |
1287 | * window size, then adjust the slot offsets according to the | |
1288 | * weights. If all of the weights are zero, center the layout in | |
1289 | * its parent. I haven't decided what to do if the parent is smaller | |
1290 | * than the requested size. | |
1291 | */ | |
1292 | ||
1293 | realWidth = Tk_Width(masterPtr->tkwin) - | |
1294 | 2*Tk_InternalBorderWidth(masterPtr->tkwin); | |
1295 | realHeight = Tk_Height(masterPtr->tkwin) - | |
1296 | 2*Tk_InternalBorderWidth(masterPtr->tkwin); | |
1297 | slotPtr->startX = AdjustOffsets(realWidth, | |
1298 | MAX(slotPtr->columnEnd,slotPtr->columnMax), slotPtr->columnPtr); | |
1299 | slotPtr->startY = AdjustOffsets(realHeight, | |
1300 | MAX(slotPtr->rowEnd,slotPtr->rowMax), slotPtr->rowPtr); | |
1301 | slotPtr->startX += Tk_InternalBorderWidth(masterPtr->tkwin); | |
1302 | slotPtr->startY += Tk_InternalBorderWidth(masterPtr->tkwin); | |
1303 | ||
1304 | /* | |
1305 | * Now adjust the actual size of the slave to its cavity by | |
1306 | * computing the cavity size, and adjusting the widget according | |
1307 | * to its stickyness. | |
1308 | */ | |
1309 | ||
1310 | for (slavePtr = masterPtr->slavePtr; slavePtr != NULL && !abort; | |
1311 | slavePtr = slavePtr->nextPtr) { | |
1312 | int x, y; /* top left coordinate */ | |
1313 | int width, height; /* slot or slave size */ | |
1314 | int col = slavePtr->column; | |
1315 | int row = slavePtr->row; | |
1316 | ||
1317 | x = (col>0) ? slotPtr->columnPtr[col-1].offset : 0; | |
1318 | y = (row>0) ? slotPtr->rowPtr[row-1].offset : 0; | |
1319 | ||
1320 | width = slotPtr->columnPtr[slavePtr->numCols+col-1].offset - x; | |
1321 | height = slotPtr->rowPtr[slavePtr->numRows+row-1].offset - y; | |
1322 | ||
1323 | x += slotPtr->startX; | |
1324 | y += slotPtr->startY; | |
1325 | ||
1326 | AdjustForSticky(slavePtr, &x, &y, &width, &height); | |
1327 | ||
1328 | /* | |
1329 | * Now put the window in the proper spot. (This was taken directly | |
1330 | * from tkPack.c.) If the slave is a child of the master, then | |
1331 | * do this here. Otherwise let Tk_MaintainGeometry do the work. | |
1332 | */ | |
1333 | ||
1334 | if (masterPtr->tkwin == Tk_Parent(slavePtr->tkwin)) { | |
1335 | if ((width <= 0) || (height <= 0)) { | |
1336 | Tk_UnmapWindow(slavePtr->tkwin); | |
1337 | } else { | |
1338 | if ((x != Tk_X(slavePtr->tkwin)) | |
1339 | || (y != Tk_Y(slavePtr->tkwin)) | |
1340 | || (width != Tk_Width(slavePtr->tkwin)) | |
1341 | || (height != Tk_Height(slavePtr->tkwin))) { | |
1342 | Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height); | |
1343 | } | |
1344 | if (abort) { | |
1345 | break; | |
1346 | } | |
1347 | ||
1348 | /* | |
1349 | * Don't map the slave if the master isn't mapped: wait | |
1350 | * until the master gets mapped later. | |
1351 | */ | |
1352 | ||
1353 | if (Tk_IsMapped(masterPtr->tkwin)) { | |
1354 | Tk_MapWindow(slavePtr->tkwin); | |
1355 | } | |
1356 | } | |
1357 | } else { | |
1358 | if ((width <= 0) || (height <= 0)) { | |
1359 | Tk_UnmaintainGeometry(slavePtr->tkwin, masterPtr->tkwin); | |
1360 | Tk_UnmapWindow(slavePtr->tkwin); | |
1361 | } else { | |
1362 | Tk_MaintainGeometry(slavePtr->tkwin, masterPtr->tkwin, | |
1363 | x, y, width, height); | |
1364 | } | |
1365 | } | |
1366 | } | |
1367 | ||
1368 | masterPtr->abortPtr = NULL; | |
1369 | Tcl_Release((ClientData) masterPtr); | |
1370 | } | |
1371 | ||
1372 | /* | |
1373 | *-------------------------------------------------------------- | |
1374 | * | |
1375 | * ResolveConstraints -- | |
1376 | * | |
1377 | * Resolve all of the column and row boundaries. Most of | |
1378 | * the calculations are identical for rows and columns, so this procedure | |
1379 | * is called twice, once for rows, and again for columns. | |
1380 | * | |
1381 | * Results: | |
1382 | * The offset (in pixels) from the left/top edge of this layout is | |
1383 | * returned. | |
1384 | * | |
1385 | * Side effects: | |
1386 | * The slot offsets are copied into the SlotInfo structure for the | |
1387 | * geometry master. | |
1388 | * | |
1389 | *-------------------------------------------------------------- | |
1390 | */ | |
1391 | ||
1392 | static int | |
1393 | ResolveConstraints(masterPtr, slotType, maxOffset) | |
1394 | Gridder *masterPtr; /* The geometry master for this grid. */ | |
1395 | int slotType; /* Either ROW or COLUMN. */ | |
1396 | int maxOffset; /* The actual maximum size of this layout | |
1397 | * in pixels, or 0 (not currently used). */ | |
1398 | { | |
1399 | register SlotInfo *slotPtr; /* Pointer to row/col constraints. */ | |
1400 | register Gridder *slavePtr; /* List of slave windows in this grid. */ | |
1401 | int constraintCount; /* Count of rows or columns that have | |
1402 | * constraints. */ | |
1403 | int slotCount; /* Last occupied row or column. */ | |
1404 | int gridCount; /* The larger of slotCount and constraintCount. | |
1405 | */ | |
1406 | GridLayout *layoutPtr; /* Temporary layout structure. */ | |
1407 | int requiredSize; /* The natural size of the grid (pixels). | |
1408 | * This is the minimum size needed to | |
1409 | * accomodate all of the slaves at their | |
1410 | * requested sizes. */ | |
1411 | int offset; /* The pixel offset of the right edge of the | |
1412 | * current slot from the beginning of the | |
1413 | * layout. */ | |
1414 | int slot; /* The current slot. */ | |
1415 | int start; /* The first slot of a contiguous set whose | |
1416 | * constraints are not yet fully resolved. */ | |
1417 | int end; /* The Last slot of a contiguous set whose | |
1418 | * constraints are not yet fully resolved. */ | |
1419 | ||
1420 | /* | |
1421 | * For typical sized tables, we'll use stack space for the layout data | |
1422 | * to avoid the overhead of a malloc and free for every layout. | |
1423 | */ | |
1424 | ||
1425 | GridLayout layoutData[TYPICAL_SIZE + 1]; | |
1426 | ||
1427 | if (slotType == COLUMN) { | |
1428 | constraintCount = masterPtr->masterDataPtr->columnMax; | |
1429 | slotCount = masterPtr->masterDataPtr->columnEnd; | |
1430 | slotPtr = masterPtr->masterDataPtr->columnPtr; | |
1431 | } else { | |
1432 | constraintCount = masterPtr->masterDataPtr->rowMax; | |
1433 | slotCount = masterPtr->masterDataPtr->rowEnd; | |
1434 | slotPtr = masterPtr->masterDataPtr->rowPtr; | |
1435 | } | |
1436 | ||
1437 | /* | |
1438 | * Make sure there is enough memory for the layout. | |
1439 | */ | |
1440 | ||
1441 | gridCount = MAX(constraintCount,slotCount); | |
1442 | if (gridCount >= TYPICAL_SIZE) { | |
1443 | layoutPtr = (GridLayout *) ckalloc(sizeof(GridLayout) * (1+gridCount)); | |
1444 | } else { | |
1445 | layoutPtr = layoutData; | |
1446 | } | |
1447 | ||
1448 | /* | |
1449 | * Allocate an extra layout slot to represent the left/top edge of | |
1450 | * the 0th slot to make it easier to calculate slot widths from | |
1451 | * offsets without special case code. | |
1452 | * Initialize the "dummy" slot to the left/top of the table. | |
1453 | * This slot avoids special casing the first slot. | |
1454 | */ | |
1455 | ||
1456 | layoutPtr->minOffset = 0; | |
1457 | layoutPtr->maxOffset = 0; | |
1458 | layoutPtr++; | |
1459 | ||
1460 | /* | |
1461 | * Step 1. | |
1462 | * Copy the slot constraints into the layout structure, | |
1463 | * and initialize the rest of the fields. | |
1464 | */ | |
1465 | ||
1466 | for (slot=0; slot < constraintCount; slot++) { | |
1467 | layoutPtr[slot].minSize = slotPtr[slot].minSize; | |
1468 | layoutPtr[slot].weight = slotPtr[slot].weight; | |
1469 | layoutPtr[slot].pad = slotPtr[slot].pad; | |
1470 | layoutPtr[slot].binNextPtr = NULL; | |
1471 | } | |
1472 | for(;slot<gridCount;slot++) { | |
1473 | layoutPtr[slot].minSize = 0; | |
1474 | layoutPtr[slot].weight = 0; | |
1475 | layoutPtr[slot].pad = 0; | |
1476 | layoutPtr[slot].binNextPtr = NULL; | |
1477 | } | |
1478 | ||
1479 | /* | |
1480 | * Step 2. | |
1481 | * Slaves with a span of 1 are used to determine the minimum size of | |
1482 | * each slot. Slaves whose span is two or more slots don't | |
1483 | * contribute to the minimum size of each slot directly, but can cause | |
1484 | * slots to grow if their size exceeds the the sizes of the slots they | |
1485 | * span. | |
1486 | * | |
1487 | * Bin all slaves whose spans are > 1 by their right edges. This | |
1488 | * allows the computation on minimum and maximum possible layout | |
1489 | * sizes at each slot boundary, without the need to re-sort the slaves. | |
1490 | */ | |
1491 | ||
1492 | switch (slotType) { | |
1493 | case COLUMN: | |
1494 | for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; | |
1495 | slavePtr = slavePtr->nextPtr) { | |
1496 | int rightEdge = slavePtr->column + slavePtr->numCols - 1; | |
1497 | slavePtr->size = Tk_ReqWidth(slavePtr->tkwin) + | |
1498 | slavePtr->padX + slavePtr->iPadX + slavePtr->doubleBw; | |
1499 | if (slavePtr->numCols > 1) { | |
1500 | slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr; | |
1501 | layoutPtr[rightEdge].binNextPtr = slavePtr; | |
1502 | } else { | |
1503 | int size = slavePtr->size + layoutPtr[rightEdge].pad; | |
1504 | if (size > layoutPtr[rightEdge].minSize) { | |
1505 | layoutPtr[rightEdge].minSize = size; | |
1506 | } | |
1507 | } | |
1508 | } | |
1509 | break; | |
1510 | case ROW: | |
1511 | for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; | |
1512 | slavePtr = slavePtr->nextPtr) { | |
1513 | int rightEdge = slavePtr->row + slavePtr->numRows - 1; | |
1514 | slavePtr->size = Tk_ReqHeight(slavePtr->tkwin) + | |
1515 | slavePtr->padY + slavePtr->iPadY + slavePtr->doubleBw; | |
1516 | if (slavePtr->numRows > 1) { | |
1517 | slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr; | |
1518 | layoutPtr[rightEdge].binNextPtr = slavePtr; | |
1519 | } else { | |
1520 | int size = slavePtr->size + layoutPtr[rightEdge].pad; | |
1521 | if (size > layoutPtr[rightEdge].minSize) { | |
1522 | layoutPtr[rightEdge].minSize = size; | |
1523 | } | |
1524 | } | |
1525 | } | |
1526 | break; | |
1527 | } | |
1528 | ||
1529 | /* | |
1530 | * Step 3. | |
1531 | * Determine the minimum slot offsets going from left to right | |
1532 | * that would fit all of the slaves. This determines the minimum | |
1533 | */ | |
1534 | ||
1535 | for (offset=slot=0; slot < gridCount; slot++) { | |
1536 | layoutPtr[slot].minOffset = layoutPtr[slot].minSize + offset; | |
1537 | for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL; | |
1538 | slavePtr = slavePtr->binNextPtr) { | |
1539 | int span = (slotType == COLUMN) ? slavePtr->numCols : slavePtr->numRows; | |
1540 | int required = slavePtr->size + layoutPtr[slot - span].minOffset; | |
1541 | if (required > layoutPtr[slot].minOffset) { | |
1542 | layoutPtr[slot].minOffset = required; | |
1543 | } | |
1544 | } | |
1545 | offset = layoutPtr[slot].minOffset; | |
1546 | } | |
1547 | ||
1548 | /* | |
1549 | * At this point, we know the minimum required size of the entire layout. | |
1550 | * It might be prudent to stop here if our "master" will resize itself | |
1551 | * to this size. | |
1552 | */ | |
1553 | ||
1554 | requiredSize = offset; | |
1555 | if (maxOffset > offset) { | |
1556 | offset=maxOffset; | |
1557 | } | |
1558 | ||
1559 | /* | |
1560 | * Step 4. | |
1561 | * Determine the minimum slot offsets going from right to left, | |
1562 | * bounding the pixel range of each slot boundary. | |
1563 | * Pre-fill all of the right offsets with the actual size of the table; | |
1564 | * they will be reduced as required. | |
1565 | */ | |
1566 | ||
1567 | for (slot=0; slot < gridCount; slot++) { | |
1568 | layoutPtr[slot].maxOffset = offset; | |
1569 | } | |
1570 | for (slot=gridCount-1; slot > 0;) { | |
1571 | for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL; | |
1572 | slavePtr = slavePtr->binNextPtr) { | |
1573 | int span = (slotType == COLUMN) ? slavePtr->numCols : slavePtr->numRows; | |
1574 | int require = offset - slavePtr->size; | |
1575 | int startSlot = slot - span; | |
1576 | if (startSlot >=0 && require < layoutPtr[startSlot].maxOffset) { | |
1577 | layoutPtr[startSlot].maxOffset = require; | |
1578 | } | |
1579 | } | |
1580 | offset -= layoutPtr[slot].minSize; | |
1581 | slot--; | |
1582 | if (layoutPtr[slot].maxOffset < offset) { | |
1583 | offset = layoutPtr[slot].maxOffset; | |
1584 | } else { | |
1585 | layoutPtr[slot].maxOffset = offset; | |
1586 | } | |
1587 | } | |
1588 | ||
1589 | /* | |
1590 | * Step 5. | |
1591 | * At this point, each slot boundary has a range of values that | |
1592 | * will satisfy the overall layout size. | |
1593 | * Make repeated passes over the layout structure looking for | |
1594 | * spans of slot boundaries where the minOffsets are less than | |
1595 | * the maxOffsets, and adjust the offsets according to the slot | |
1596 | * weights. At each pass, at least one slot boundary will have | |
1597 | * its range of possible values fixed at a single value. | |
1598 | */ | |
1599 | ||
1600 | for (start=0; start < gridCount;) { | |
1601 | int totalWeight = 0; /* Sum of the weights for all of the | |
1602 | * slots in this span. */ | |
1603 | int need = 0; /* The minimum space needed to layout | |
1604 | * this span. */ | |
1605 | int have; /* The actual amount of space that will | |
1606 | * be taken up by this span. */ | |
1607 | int weight; /* Cumulative weights of the columns in | |
1608 | * this span. */ | |
1609 | int noWeights = 0; /* True if the span has no weights. */ | |
1610 | ||
1611 | /* | |
1612 | * Find a span by identifying ranges of slots whose edges are | |
1613 | * already constrained at fixed offsets, but whose internal | |
1614 | * slot boundaries have a range of possible positions. | |
1615 | */ | |
1616 | ||
1617 | if (layoutPtr[start].minOffset == layoutPtr[start].maxOffset) { | |
1618 | start++; | |
1619 | continue; | |
1620 | } | |
1621 | ||
1622 | for (end=start+1; end<gridCount; end++) { | |
1623 | if (layoutPtr[end].minOffset == layoutPtr[end].maxOffset) { | |
1624 | break; | |
1625 | } | |
1626 | } | |
1627 | ||
1628 | /* | |
1629 | * We found a span. Compute the total weight, minumum space required, | |
1630 | * for this span, and the actual amount of space the span should | |
1631 | * use. | |
1632 | */ | |
1633 | ||
1634 | for (slot=start; slot<=end; slot++) { | |
1635 | totalWeight += layoutPtr[slot].weight; | |
1636 | need += layoutPtr[slot].minSize; | |
1637 | } | |
1638 | have = layoutPtr[end].maxOffset - layoutPtr[start-1].minOffset; | |
1639 | ||
1640 | /* | |
1641 | * If all the weights in the span are zero, then distribute the | |
1642 | * extra space evenly. | |
1643 | */ | |
1644 | ||
1645 | if (totalWeight == 0) { | |
1646 | noWeights++; | |
1647 | totalWeight = end - start + 1; | |
1648 | } | |
1649 | ||
1650 | /* | |
1651 | * It might not be possible to give the span all of the space | |
1652 | * available on this pass without violating the size constraints | |
1653 | * of one or more of the internal slot boundaries. | |
1654 | * Determine the maximum amount of space that when added to the | |
1655 | * entire span, would cause a slot boundary to have its possible | |
1656 | * range reduced to one value, and reduce the amount of extra | |
1657 | * space allocated on this pass accordingly. | |
1658 | * | |
1659 | * The calculation is done cumulatively to avoid accumulating | |
1660 | * roundoff errors. | |
1661 | */ | |
1662 | ||
1663 | for (weight=0,slot=start; slot<end; slot++) { | |
1664 | int diff = layoutPtr[slot].maxOffset - layoutPtr[slot].minOffset; | |
1665 | weight += noWeights ? 1 : layoutPtr[slot].weight; | |
1666 | if ((noWeights || layoutPtr[slot].weight>0) && | |
1667 | (diff*totalWeight/weight) < (have-need)) { | |
1668 | have = diff * totalWeight / weight + need; | |
1669 | } | |
1670 | } | |
1671 | ||
1672 | /* | |
1673 | * Now distribute the extra space among the slots by | |
1674 | * adjusting the minSizes and minOffsets. | |
1675 | */ | |
1676 | ||
1677 | for (weight=0,slot=start; slot<end; slot++) { | |
1678 | weight += noWeights ? 1 : layoutPtr[slot].weight; | |
1679 | layoutPtr[slot].minOffset += | |
1680 | (int)((double) (have-need) * weight/totalWeight + 0.5); | |
1681 | layoutPtr[slot].minSize = layoutPtr[slot].minOffset | |
1682 | - layoutPtr[slot-1].minOffset; | |
1683 | } | |
1684 | layoutPtr[slot].minSize = layoutPtr[slot].minOffset | |
1685 | - layoutPtr[slot-1].minOffset; | |
1686 | ||
1687 | /* | |
1688 | * Having pushed the top/left boundaries of the slots to | |
1689 | * take up extra space, the bottom/right space is recalculated | |
1690 | * to propagate the new space allocation. | |
1691 | */ | |
1692 | ||
1693 | for (slot=end; slot > start; slot--) { | |
1694 | layoutPtr[slot-1].maxOffset = | |
1695 | layoutPtr[slot].maxOffset-layoutPtr[slot].minSize; | |
1696 | } | |
1697 | } | |
1698 | ||
1699 | ||
1700 | /* | |
1701 | * Step 6. | |
1702 | * All of the space has been apportioned; copy the | |
1703 | * layout information back into the master. | |
1704 | */ | |
1705 | ||
1706 | for (slot=0; slot < gridCount; slot++) { | |
1707 | slotPtr[slot].offset = layoutPtr[slot].minOffset; | |
1708 | } | |
1709 | ||
1710 | --layoutPtr; | |
1711 | if (layoutPtr != layoutData) { | |
1712 | Tcl_Free((char *)layoutPtr); | |
1713 | } | |
1714 | return requiredSize; | |
1715 | } | |
1716 | ||
1717 | /* | |
1718 | *-------------------------------------------------------------- | |
1719 | * | |
1720 | * GetGrid -- | |
1721 | * | |
1722 | * This internal procedure is used to locate a Grid | |
1723 | * structure for a given window, creating one if one | |
1724 | * doesn't exist already. | |
1725 | * | |
1726 | * Results: | |
1727 | * The return value is a pointer to the Grid structure | |
1728 | * corresponding to tkwin. | |
1729 | * | |
1730 | * Side effects: | |
1731 | * A new grid structure may be created. If so, then | |
1732 | * a callback is set up to clean things up when the | |
1733 | * window is deleted. | |
1734 | * | |
1735 | *-------------------------------------------------------------- | |
1736 | */ | |
1737 | ||
1738 | static Gridder * | |
1739 | GetGrid(tkwin) | |
1740 | Tk_Window tkwin; /* Token for window for which | |
1741 | * grid structure is desired. */ | |
1742 | { | |
1743 | register Gridder *gridPtr; | |
1744 | Tcl_HashEntry *hPtr; | |
1745 | int new; | |
1746 | TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr; | |
1747 | ||