Index: generic/tkGrid.c ================================================================== --- generic/tkGrid.c +++ generic/tkGrid.c @@ -142,10 +142,21 @@ * master. */ int startY; /* Pixel offset of this layout within its * master. */ Tk_Anchor anchor; /* Value of anchor option: specifies where a * grid without weight should be placed. */ + char *xScrollCmd; /* Command prefix for communicating with + * horizontal scrollbar. NULL means no + * horizontal scrollbar. Malloc'ed*/ + char *yScrollCmd; /* Command prefix for communicating with + * vertical scrollbar. NULL means no + * vertical scrollbar. Malloc'ed*/ + struct Gridder *nextColPeerPtr; + struct Gridder *nextRowPeerPtr; + int xOrigin, yOrigin; + int xUsed, yUsed; + Tcl_Interp *interp; } 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 @@ -262,10 +273,14 @@ int slotType, int checkOnly); static int ConfigureSlaves(Tcl_Interp *interp, Tk_Window tkwin, int objc, Tcl_Obj *const objv[]); static void DestroyGrid(void *memPtr); static Gridder * GetGrid(Tk_Window tkwin); +static int GridXYScrollcommandCommand(Tk_Window tkwin, + Tcl_Interp *interp, int y, int objc, + Tcl_Obj *CONST objv[]); +static void GridUpdateScrollbars(Gridder *masterPtr); static int GridAnchorCommand(Tk_Window tkwin, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); static int GridBboxCommand(Tk_Window tkwin, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); static int GridForgetRemoveCommand(Tk_Window tkwin, @@ -276,10 +291,16 @@ static int GridLocationCommand(Tk_Window tkwin, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); static int GridPropagateCommand(Tk_Window tkwin, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int GridColumnpeerCommand(Tk_Window tkwin, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int GridRowpeerCommand(Tk_Window tkwin, + Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); static int GridRowColumnConfigureCommand(Tk_Window tkwin, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); static int GridSizeCommand(Tk_Window tkwin, Tcl_Interp *interp, @@ -292,26 +313,88 @@ Tk_Window tkwin); static void GridReqProc(ClientData clientData, Tk_Window tkwin); static void InitMasterData(Gridder *masterPtr); static Tcl_Obj * NewPairObj(int, int); static Tcl_Obj * NewQuadObj(int, int, int, int); -static int ResolveConstraints(Gridder *gridPtr, int rowOrColumn, - int maxOffset); +static int ResolveConstraints(Gridder *gridPtr, int rowOrColumn); +static Tcl_Obj * ScrollFractions(int screen1, int screen2, int object1, + int object2); static void SetGridSize(Gridder *gridPtr); static int SetSlaveColumn(Tcl_Interp *interp, Gridder *slavePtr, int column, int numCols); static int SetSlaveRow(Tcl_Interp *interp, Gridder *slavePtr, int row, int numRows); static Tcl_Obj * StickyToObj(int flags); static int StringToSticky(const char *string); static void Unlink(Gridder *gridPtr); +static void UnlinkPeer(Gridder *masterPtr, int rowOrColumn); static const Tk_GeomMgr gridMgrType = { "grid", /* name */ GridReqProc, /* requestProc */ GridLostSlaveProc, /* lostSlaveProc */ }; + +static CONST char ** +TkGetStringsFromObjs(argc, objv) + int argc; + Tcl_Obj *CONST objv[]; +{ + register int i; + CONST char **argv; + if (argc <= 0) { + return NULL; + } + argv = (CONST char **) ckalloc((argc+1) * sizeof(char *)); + for (i = 0; i < argc; i++) { + argv[i]=Tcl_GetStringFromObj(objv[i], (int *) NULL); + } + argv[argc] = 0; + return argv; +} + +static void +GridSetOrigin(masterPtr, xOrigin, yOrigin) + Gridder *masterPtr; /* Information about master. */ + int xOrigin; /* New X origin. */ + int yOrigin; /* New Y origin. */ +{ + GridMaster *gridPtr = masterPtr->masterDataPtr; + int xUsed, yUsed, viewX, viewY; + + if ((xOrigin == gridPtr->xOrigin) && (yOrigin == gridPtr->yOrigin)) { + return; + } + + gridPtr->xOrigin = xOrigin; + gridPtr->yOrigin = yOrigin; + + /* Check range */ + xUsed = gridPtr->xUsed; + yUsed = gridPtr->yUsed; + viewX = Tk_Width(masterPtr->tkwin) - + Tk_InternalBorderLeft(masterPtr->tkwin) - + Tk_InternalBorderRight(masterPtr->tkwin); + viewY = Tk_Height(masterPtr->tkwin) - + Tk_InternalBorderTop(masterPtr->tkwin) - + Tk_InternalBorderBottom(masterPtr->tkwin); + if (xOrigin < 0) { + gridPtr->xOrigin = 0; + } else if (xOrigin > (xUsed - viewX)) { + gridPtr->xOrigin = (xUsed - viewX); + } + if (yOrigin < 0) { + gridPtr->yOrigin = 0; + } else if (yOrigin > (yUsed - viewY)) { + gridPtr->yOrigin = (yUsed - viewY); + } + + if (!(masterPtr->flags & REQUESTED_RELAYOUT)) { + masterPtr->flags |= REQUESTED_RELAYOUT; + Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr); + } +} /* *---------------------------------------------------------------------- * * Tk_GridCmd -- @@ -335,25 +418,29 @@ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { Tk_Window tkwin = clientData; static const char *const optionStrings[] = { + "xscrollcommand", "yscrollcommand", "xview", "yview", "anchor", "bbox", "columnconfigure", "configure", "forget", "info", "location", "propagate", "remove", + "columnpeer", "rowpeer", "rowconfigure", "size", "slaves", NULL }; enum options { + GRID_XSCROLLCOMMAND, GRID_YSCROLLCOMMAND, GRID_XVIEW, GRID_YVIEW, GRID_ANCHOR, GRID_BBOX, GRID_COLUMNCONFIGURE, GRID_CONFIGURE, GRID_FORGET, GRID_INFO, GRID_LOCATION, GRID_PROPAGATE, GRID_REMOVE, + GRID_COLUMNPEER, GRID_ROWPEER, GRID_ROWCONFIGURE, GRID_SIZE, GRID_SLAVES }; int index; if (objc >= 2) { const char *argv1 = Tcl_GetString(objv[1]); - if ((argv1[0] == '.') || (argv1[0] == REL_SKIP) || + if ((argv1[0] == '.') || (argv1[0] == REL_SKIP && argv1[1] == 0) || (argv1[0] == REL_VERT)) { return ConfigureSlaves(interp, tkwin, objc-1, objv+1); } } if (objc < 3) { @@ -365,10 +452,111 @@ sizeof(char *), "option", 0, &index) != TCL_OK) { return TCL_ERROR; } switch ((enum options) index) { + case GRID_XSCROLLCOMMAND: + return GridXYScrollcommandCommand(tkwin, interp, 0, objc, objv); + case GRID_YSCROLLCOMMAND: + return GridXYScrollcommandCommand(tkwin, interp, 1, objc, objv); + case GRID_XVIEW: { + int count, type; + int newX = 0; /* Initialization needed only to prevent + * gcc warnings. */ + double fraction; + int viewX; + Tk_Window master; + Gridder *masterPtr; + GridMaster *gridPtr; + if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) { + return TCL_ERROR; + } + masterPtr = GetGrid(master); + gridPtr = masterPtr->masterDataPtr; + viewX = Tk_Width(masterPtr->tkwin) - + Tk_InternalBorderLeft(masterPtr->tkwin) - + Tk_InternalBorderRight(masterPtr->tkwin); + if (objc < 3) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?scroll?"); + return TCL_ERROR; + } + if (objc == 3) { + Tcl_SetObjResult(interp, ScrollFractions( + gridPtr->xOrigin, gridPtr->xOrigin + viewX, + 0, gridPtr->xUsed)); + } else { + CONST char **args = TkGetStringsFromObjs(objc-1, &objv[1]); + type = Tk_GetScrollInfo(interp, objc-1, args, &fraction, &count); + if (args) ckfree((char *) args); + switch (type) { + case TK_SCROLL_ERROR: + return TCL_ERROR; + case TK_SCROLL_MOVETO: + newX = (int) ((fraction * gridPtr->xUsed) + 0.5); + break; + case TK_SCROLL_PAGES: + newX = (int) (gridPtr->xOrigin + count * .9 * viewX); + break; + case TK_SCROLL_UNITS: + newX = (int) (gridPtr->xOrigin + count * .1 * viewX); + break; + } + GridSetOrigin(masterPtr, newX, gridPtr->yOrigin); + } + return TCL_OK; + } + case GRID_YVIEW: { + int count, type; + int newY = 0; /* Initialization needed only to prevent + * gcc warnings. */ + double fraction; + int viewY; + Tk_Window master; + Gridder *masterPtr; + GridMaster *gridPtr; + if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) { + return TCL_ERROR; + } + masterPtr = GetGrid(master); + gridPtr = masterPtr->masterDataPtr; + viewY = Tk_Height(masterPtr->tkwin) - + Tk_InternalBorderTop(masterPtr->tkwin) - + Tk_InternalBorderBottom(masterPtr->tkwin); + if (objc < 3) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?scroll?"); + return TCL_ERROR; + } + if (objc == 3) { + Tcl_SetObjResult(interp, ScrollFractions( + gridPtr->yOrigin, gridPtr->yOrigin + viewY, + 0, gridPtr->yUsed)); + } else { + CONST char **args = TkGetStringsFromObjs(objc-1, &objv[1]); + type = Tk_GetScrollInfo(interp, objc-1, args, &fraction, &count); + if (args) ckfree((char *) args); + switch (type) { + case TK_SCROLL_ERROR: + return TCL_ERROR; + case TK_SCROLL_MOVETO: + newY = (int) ((fraction * gridPtr->yUsed) + 0.5); + break; + case TK_SCROLL_PAGES: + newY = (int) (gridPtr->yOrigin + count * .9 * viewY); + break; + case TK_SCROLL_UNITS: + newY = (int) (gridPtr->yOrigin + count * .1 * viewY); + break; + } + GridSetOrigin(masterPtr, gridPtr->xOrigin, newY); + } + GridUpdateScrollbars(masterPtr); + return TCL_OK; + } + case GRID_COLUMNPEER: + return GridColumnpeerCommand(tkwin, interp, objc, objv); + case GRID_ROWPEER: + return GridRowpeerCommand(tkwin, interp, objc, objv); case GRID_ANCHOR: return GridAnchorCommand(tkwin, interp, objc, objv); case GRID_BBOX: return GridBboxCommand(tkwin, interp, objc, objv); case GRID_CONFIGURE: @@ -404,10 +592,176 @@ /* This should not happen */ Tcl_SetObjResult(interp, Tcl_NewStringObj("internal error in grid", -1)); Tcl_SetErrorCode(interp, "TK", "API_ABUSE", NULL); return TCL_ERROR; } + +/* + *---------------------------------------------------------------------- + * + * ScrollFractions -- + * + * Given the range that's visible in the window and the "100% + * range" for what's in the canvas, return a list of two + * doubles representing the scroll fractions. This procedure + * is used for both x and y scrolling. + * + * Results: + * The memory pointed to by string is modified to hold + * two real numbers containing the scroll fractions (between + * 0 and 1) corresponding to the other arguments. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static Tcl_Obj * +ScrollFractions( + int screen1, /* Lowest coordinate visible in the window. */ + int screen2, /* Highest coordinate visible in the window. */ + int object1, /* Lowest coordinate in the object. */ + int object2) /* Highest coordinate in the object. */ +{ + double range, f1, f2; + char buffer[2*TCL_DOUBLE_SPACE+2]; + + range = object2 - object1; + if (range <= 0) { + f1 = 0; + f2 = 1.0; + } else { + f1 = (screen1 - object1)/range; + if (f1 < 0) { + f1 = 0.0; + } + f2 = (screen2 - object1)/range; + if (f2 > 1.0) { + f2 = 1.0; + } + if (f2 < f1) { + f2 = f1; + } + } + sprintf(buffer, "%g %g", f1, f2); + return Tcl_NewStringObj(buffer, -1); +} + +static void +GridUpdateScrollbars(masterPtr) + Gridder *masterPtr; /* Information about grid. */ +{ + int result; + int xOrigin, yOrigin, xUsed, yUsed, viewX, viewY; + char *xScrollCmd, *yScrollCmd; + GridMaster *gridPtr = masterPtr->masterDataPtr; + Tcl_Interp *interp = gridPtr->interp; + + /* + * Save all the relevant values from the canvasPtr, because it might be + * deleted as part of either of the two calls to Tcl_VarEval below. + */ + + Tcl_Preserve((ClientData) interp); + xScrollCmd = gridPtr->xScrollCmd; + if (xScrollCmd != (char *) NULL) { + Tcl_Preserve((ClientData) xScrollCmd); + } + yScrollCmd = gridPtr->yScrollCmd; + if (yScrollCmd != (char *) NULL) { + Tcl_Preserve((ClientData) yScrollCmd); + } + xOrigin = gridPtr->xOrigin; + yOrigin = gridPtr->yOrigin; + xUsed = gridPtr->xUsed; + yUsed = gridPtr->yUsed; + viewX = Tk_Width(masterPtr->tkwin) - + Tk_InternalBorderLeft(masterPtr->tkwin) - + Tk_InternalBorderRight(masterPtr->tkwin); + viewY = Tk_Height(masterPtr->tkwin) - + Tk_InternalBorderTop(masterPtr->tkwin) - + Tk_InternalBorderBottom(masterPtr->tkwin); + + if (xScrollCmd != NULL) { + Tcl_Obj *fractions = ScrollFractions( + gridPtr->xOrigin, gridPtr->xOrigin + viewX, + 0, gridPtr->xUsed); + result = Tcl_VarEval(interp, xScrollCmd, " ", + Tcl_GetString(fractions), (char *) NULL); + Tcl_DecrRefCount(fractions); + if (result != TCL_OK) { + Tcl_BackgroundError(interp); + } + Tcl_ResetResult(interp); + Tcl_Release((ClientData) xScrollCmd); + } + + if (yScrollCmd != NULL) { + Tcl_Obj *fractions = ScrollFractions( + gridPtr->yOrigin, gridPtr->yOrigin + viewY, + 0, gridPtr->yUsed); + result = Tcl_VarEval(interp, yScrollCmd, " ", + Tcl_GetString(fractions), (char *) NULL); + Tcl_DecrRefCount(fractions); + if (result != TCL_OK) { + Tcl_BackgroundError(interp); + } + Tcl_ResetResult(interp); + Tcl_Release((ClientData) yScrollCmd); + } + Tcl_Release((ClientData) interp); +} + +static int +GridXYScrollcommandCommand(tkwin, interp, y, objc, objv) + Tk_Window tkwin; /* Main window of the application. */ + Tcl_Interp *interp; /* Current interpreter. */ + int y; /* True if y, false if x. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ + Tk_Window master; + Gridder *masterPtr; + GridMaster *gridPtr; + int length; + char *value, **cmdPtr; + + if (objc > 4) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?command?"); + return TCL_ERROR; + } + + if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) { + return TCL_ERROR; + } + masterPtr = GetGrid(master); + + if (objc == 3) { + gridPtr = masterPtr->masterDataPtr; + cmdPtr = y ? &gridPtr->yScrollCmd : &gridPtr->xScrollCmd; + Tcl_SetResult(interp, *cmdPtr == NULL ? "" : *cmdPtr, TCL_VOLATILE); + return TCL_OK; + } + + InitMasterData(masterPtr); + gridPtr = masterPtr->masterDataPtr; + gridPtr->interp = interp; + cmdPtr = y ? &gridPtr->yScrollCmd : &gridPtr->xScrollCmd; + + if (*cmdPtr != NULL) { + ckfree(*cmdPtr); + } + value = Tcl_GetStringFromObj(objv[3], &length); + if (length == 0) { + *cmdPtr = NULL; + } else { + *cmdPtr = ckalloc((unsigned) (length + 1)); + strcpy(*cmdPtr, value); + } + return TCL_OK; +} /* *---------------------------------------------------------------------- * * GridAnchorCommand -- @@ -474,10 +828,201 @@ Tcl_DoWhenIdle(ArrangeGrid, masterPtr); } } return TCL_OK; } + +/* + *---------------------------------------------------------------------- + * + * GridColumnpeerCommand -- + * + * Implementation of the [grid columnpeer] subcommand. See the user + * documentation for details on what it does. + * + * Results: + * Standard Tcl result. + * + * Side effects: + * May recompute grid geometry. + * + *---------------------------------------------------------------------- + */ + +static int +GridColumnpeerCommand( + Tk_Window tkwin, /* Main window of the application. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *CONST objv[]) /* Argument objects. */ +{ + Tk_Window master, newpeer; + Gridder *masterPtr, *peerPtr, *newpeerPtr; + GridMaster *gridPtr; + + if (objc > 4) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?peer?"); + return TCL_ERROR; + } + + if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) { + return TCL_ERROR; + } + masterPtr = GetGrid(master); + InitMasterData(masterPtr); + + if (objc == 3) { + Tcl_Obj *resultPtr = Tcl_NewListObj(0, NULL); + peerPtr = masterPtr->masterDataPtr->nextColPeerPtr; + while (peerPtr != masterPtr) { + /* Add this master to result */ + Tcl_ListObjAppendElement(interp, resultPtr, Tcl_NewStringObj(Tk_PathName(peerPtr->tkwin), -1)); + peerPtr = peerPtr->masterDataPtr->nextColPeerPtr; + } + Tcl_SetObjResult(interp, resultPtr); + return TCL_OK; + } + + gridPtr = masterPtr->masterDataPtr; + + if (TkGetWindowFromObj(interp, tkwin, objv[3], &newpeer) != TCL_OK) { + return TCL_ERROR; + } + newpeerPtr = GetGrid(newpeer); + InitMasterData(newpeerPtr); + + /* + * Check peer list if it is already present. + */ + + peerPtr = masterPtr->masterDataPtr->nextColPeerPtr; + while (peerPtr != masterPtr) { + if (peerPtr == newpeerPtr) { + return TCL_OK; + } + InitMasterData(peerPtr); + peerPtr = peerPtr->masterDataPtr->nextColPeerPtr; + } + + /* + * Add peer to list. + */ + newpeerPtr->masterDataPtr->nextColPeerPtr = + masterPtr->masterDataPtr->nextColPeerPtr; + masterPtr->masterDataPtr->nextColPeerPtr = newpeerPtr; + + + /* + * Request relayout of all peers. + */ + + peerPtr = masterPtr->masterDataPtr->nextColPeerPtr; + while (peerPtr != NULL && peerPtr != masterPtr) { + if (!(peerPtr->flags & REQUESTED_RELAYOUT)) { + peerPtr->flags |= REQUESTED_RELAYOUT; + Tcl_DoWhenIdle(ArrangeGrid, (ClientData) peerPtr); + } + gridPtr = peerPtr->masterDataPtr; + peerPtr = gridPtr->nextColPeerPtr; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * GridRowpeerCommand -- + * + * Implementation of the [grid rowpeer] subcommand. See the user + * documentation for details on what it does. + * + * Results: + * Standard Tcl result. + * + * Side effects: + * May recompute grid geometry. + * + *---------------------------------------------------------------------- + */ + +static int +GridRowpeerCommand( + Tk_Window tkwin, /* Main window of the application. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *CONST objv[]) /* Argument objects. */ +{ + Tk_Window master, newpeer; + Gridder *masterPtr, *peerPtr, *newpeerPtr; + GridMaster *gridPtr; + + if (objc > 4) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?peer?"); + return TCL_ERROR; + } + + if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) { + return TCL_ERROR; + } + masterPtr = GetGrid(master); + InitMasterData(masterPtr); + + if (objc == 3) { + Tcl_Obj *resultPtr = Tcl_NewListObj(0, NULL); + peerPtr = masterPtr->masterDataPtr->nextRowPeerPtr; + while (peerPtr != masterPtr) { + /* Add this master to result */ + Tcl_ListObjAppendElement(interp, resultPtr, Tcl_NewStringObj(Tk_PathName(peerPtr->tkwin), -1)); + peerPtr = peerPtr->masterDataPtr->nextRowPeerPtr; + } + Tcl_SetObjResult(interp, resultPtr); + return TCL_OK; + } + + gridPtr = masterPtr->masterDataPtr; + + if (TkGetWindowFromObj(interp, tkwin, objv[3], &newpeer) != TCL_OK) { + return TCL_ERROR; + } + newpeerPtr = GetGrid(newpeer); + InitMasterData(newpeerPtr); + + /* + * Check peer list if it is already present. + */ + + peerPtr = masterPtr->masterDataPtr->nextRowPeerPtr; + while (peerPtr != masterPtr) { + if (peerPtr == newpeerPtr) { + return TCL_OK; + } + InitMasterData(peerPtr); + peerPtr = peerPtr->masterDataPtr->nextRowPeerPtr; + } + + /* + * Add peer to list. + */ + newpeerPtr->masterDataPtr->nextRowPeerPtr = + masterPtr->masterDataPtr->nextRowPeerPtr; + masterPtr->masterDataPtr->nextRowPeerPtr = newpeerPtr; + + /* + * Request relayout of all peers. + */ + + peerPtr = masterPtr->masterDataPtr->nextRowPeerPtr; + while (peerPtr != NULL && peerPtr != masterPtr) { + if (!(peerPtr->flags & REQUESTED_RELAYOUT)) { + peerPtr->flags |= REQUESTED_RELAYOUT; + Tcl_DoWhenIdle(ArrangeGrid, (ClientData) peerPtr); + } + gridPtr = peerPtr->masterDataPtr; + peerPtr = gridPtr->nextRowPeerPtr; + } + return TCL_OK; +} /* *---------------------------------------------------------------------- * * GridBboxCommand -- @@ -1762,12 +2307,12 @@ /* * 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 = ResolveConstraints(masterPtr, COLUMN); + height = ResolveConstraints(masterPtr, ROW); width += Tk_InternalBorderLeft(masterPtr->tkwin) + Tk_InternalBorderRight(masterPtr->tkwin); height += Tk_InternalBorderTop(masterPtr->tkwin) + Tk_InternalBorderBottom(masterPtr->tkwin); @@ -1808,10 +2353,30 @@ MAX(slotPtr->columnEnd, slotPtr->columnMax), slotPtr->columnPtr); usedY = AdjustOffsets(realHeight, MAX(slotPtr->rowEnd, slotPtr->rowMax), slotPtr->rowPtr); TkComputeAnchor(masterPtr->masterDataPtr->anchor, masterPtr->tkwin, 0, 0, usedX, usedY, &slotPtr->startX, &slotPtr->startY); + + /* Scrolling */ + /* FIXA: Does anchor or scroll takes priority if scrollcmd is not set? */ +// if ((slotPtr->xScrollCmd != NULL) || (slotPtr->yScrollCmd != NULL)) { + if (usedX > realWidth) { + slotPtr->startX = -slotPtr->xOrigin; + slotPtr->xUsed = usedX; + } else { + slotPtr->xOrigin = 0; + slotPtr->xUsed = realWidth; + } + if (usedY > realHeight) { + slotPtr->startY = -slotPtr->yOrigin; + slotPtr->yUsed = usedY; + } else { + slotPtr->yOrigin = 0; + slotPtr->yUsed = realHeight; + } + GridUpdateScrollbars(masterPtr); +// } /* * Now adjust the actual size of the slave to its cavity by computing the * cavity size, and adjusting the widget according to its stickyness. */ @@ -1897,13 +2462,11 @@ */ static int ResolveConstraints( 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). */ + int slotType) /* Either ROW or COLUMN. */ { 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. */ @@ -1930,10 +2493,11 @@ int uniformGroups; /* Number of currently used uniform groups. */ int uniformGroupsAlloced; /* Size of allocated space for uniform * groups. */ int weight, minSize; int prevGrow, accWeight, grow; + Gridder *peerPtr; /* * 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. */ @@ -2005,50 +2569,55 @@ * 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 if (rightEdge >= 0) { - 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 if (rightEdge >= 0) { - int size = slavePtr->size + layoutPtr[rightEdge].pad; - - if (size > layoutPtr[rightEdge].minSize) { - layoutPtr[rightEdge].minSize = size; - } - } - } - break; - } + peerPtr = masterPtr; + do { + switch (slotType) { + case COLUMN: + for (slavePtr = peerPtr->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 if (rightEdge >= 0) { + int size = slavePtr->size + layoutPtr[rightEdge].pad; + + if (size > layoutPtr[rightEdge].minSize) { + layoutPtr[rightEdge].minSize = size; + } + } + } + peerPtr = peerPtr->masterDataPtr->nextColPeerPtr; + break; + case ROW: + for (slavePtr = peerPtr->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 if (rightEdge >= 0) { + int size = slavePtr->size + layoutPtr[rightEdge].pad; + + if (size > layoutPtr[rightEdge].minSize) { + layoutPtr[rightEdge].minSize = size; + } + } + } + peerPtr = peerPtr->masterDataPtr->nextRowPeerPtr; + break; + } + } while (peerPtr != masterPtr); /* * Step 2b. * Consider demands on uniform sizes. */ @@ -2147,18 +2716,13 @@ 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 @@ -2494,17 +3058,35 @@ static void SetGridSize( Gridder *masterPtr) /* The geometry master for this grid. */ { register Gridder *slavePtr; /* Current slave window. */ + Gridder *peerPtr; int maxX = 0, maxY = 0; for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; - slavePtr = slavePtr->nextPtr) { + slavePtr = slavePtr->nextPtr) { maxX = MAX(maxX, slavePtr->numCols + slavePtr->column); maxY = MAX(maxY, slavePtr->numRows + slavePtr->row); } + peerPtr = masterPtr->masterDataPtr->nextColPeerPtr; + while (peerPtr != masterPtr) { + for (slavePtr = peerPtr->slavePtr; slavePtr != NULL; + slavePtr = slavePtr->nextPtr) { + maxX = MAX(maxX, slavePtr->numCols + slavePtr->column); + } + peerPtr = peerPtr->masterDataPtr->nextColPeerPtr; + } + peerPtr = masterPtr->masterDataPtr->nextRowPeerPtr; + while (peerPtr != masterPtr) { + for (slavePtr = peerPtr->slavePtr; slavePtr != NULL; + slavePtr = slavePtr->nextPtr) { + maxY = MAX(maxY, slavePtr->numRows + slavePtr->row); + } + peerPtr = peerPtr->masterDataPtr->nextRowPeerPtr; + } + masterPtr->masterDataPtr->columnEnd = maxX; masterPtr->masterDataPtr->rowEnd = maxY; CheckSlotData(masterPtr, maxX, COLUMN, CHECK_SPACE); CheckSlotData(masterPtr, maxY, ROW, CHECK_SPACE); } @@ -2714,10 +3296,19 @@ gridPtr->rowPtr = ckalloc(size); gridPtr->rowSpace = TYPICAL_SIZE; gridPtr->startX = 0; gridPtr->startY = 0; gridPtr->anchor = GRID_DEFAULT_ANCHOR; + gridPtr->xScrollCmd = NULL; + gridPtr->yScrollCmd = NULL; + gridPtr->nextColPeerPtr = masterPtr; + gridPtr->nextRowPeerPtr = masterPtr; + gridPtr->xOrigin = 0; + gridPtr->yOrigin = 0; + gridPtr->xUsed = 0; + gridPtr->yUsed = 0; + gridPtr->interp = NULL; memset(gridPtr->columnPtr, 0, size); memset(gridPtr->rowPtr, 0, size); } } @@ -2783,10 +3374,57 @@ TkFreeGeometryMaster(masterPtr->tkwin, "grid"); masterPtr->flags &= ~ALLOCED_MASTER; } } +/* Unlink from peer list. */ +static void +UnlinkPeer(Gridder *masterPtr, int rowOrColumn) +{ + Gridder *prevPeerPtr, *nextPeerPtr; + Gridder *peerPtr; + + if (rowOrColumn == ROW) { + nextPeerPtr = masterPtr->masterDataPtr->nextRowPeerPtr; + masterPtr->masterDataPtr->nextRowPeerPtr = masterPtr; + } else { + nextPeerPtr = masterPtr->masterDataPtr->nextColPeerPtr; + masterPtr->masterDataPtr->nextColPeerPtr = masterPtr; + } + if (nextPeerPtr == masterPtr) { + /* + * No peers + */ + + return; + } + + /* + * Loop through peers, request relayout and locate last among then. + */ + + peerPtr = nextPeerPtr; + do { + if (!(peerPtr->flags & REQUESTED_RELAYOUT)) { + peerPtr->flags |= REQUESTED_RELAYOUT; + Tcl_DoWhenIdle(ArrangeGrid, (ClientData) peerPtr); + } + prevPeerPtr = peerPtr; + if (rowOrColumn == ROW) { + peerPtr = peerPtr->masterDataPtr->nextRowPeerPtr; + } else { + peerPtr = peerPtr->masterDataPtr->nextColPeerPtr; + } + } while (peerPtr != masterPtr); + + if (rowOrColumn == ROW) { + prevPeerPtr->masterDataPtr->nextRowPeerPtr = nextPeerPtr; + } else { + prevPeerPtr->masterDataPtr->nextColPeerPtr = nextPeerPtr; + } +} + /* *---------------------------------------------------------------------- * * DestroyGrid -- * @@ -2870,10 +3508,15 @@ } else if (eventPtr->type == DestroyNotify) { register Gridder *gridPtr2, *nextPtr; if (gridPtr->masterPtr != NULL) { Unlink(gridPtr); + } + if (gridPtr->masterDataPtr != NULL) { + /* Unlink peer list. */ + UnlinkPeer(gridPtr, ROW); + UnlinkPeer(gridPtr, COLUMN); } for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL; gridPtr2 = nextPtr) { Tk_UnmapWindow(gridPtr2->tkwin); gridPtr2->masterPtr = NULL; ADDED gs.tcl Index: gs.tcl ================================================================== --- gs.tcl +++ gs.tcl @@ -0,0 +1,109 @@ +set rows 40 +set cols 40 +set head 2 + +frame .ft +frame .fl +frame .fr +frame .fb +frame .fc + +for {set row 0} {$row < $head} {incr row} { + for {set col 0} {$col < $cols} {incr col} { + set w .ft.l$row,$col + label $w -text "tt$col,$row" + grid $w -row $row -column $col -sticky we + if {($row + $col) & 1} { + $w configure -background green + } + } +} + +for {set row 0} {$row < $rows} {incr row} { + for {set col 0} {$col < $head} {incr col} { + set w .fl.l$row,$col + label $w -text "ll$col,$row" + grid $w -row $row -column $col -sticky we + if {($row + $col) & 1} { + $w configure -background green + } + } +} + +for {set row 0} {$row < $rows} {incr row} { + for {set col 0} {$col < $head} {incr col} { + set w .fr.l$row,$col + label $w -text "rr$col,$row" + grid $w -row $row -column $col -sticky we + if {($row + $col) & 1} { + $w configure -background green + } + } +} + +for {set row 0} {$row < $head} {incr row} { + for {set col 0} {$col < $cols} {incr col} { + set w .fb.l$row,$col + label $w -text "bb$col,$row" + grid $w -row $row -column $col -sticky we + if {($row + $col) & 1} { + $w configure -background green + } + } +} + +for {set row 0} {$row < $rows} {incr row} { + for {set col 0} {$col < $cols} {incr col} { + set w .fc.l$row,$col + label $w -text "$col,$row" + grid $w -row $row -column $col -sticky we + if {($row + $col) & 1} { + $w configure -background green + } + } +} + +proc MyXView {wlist args} { + foreach w $wlist { + eval grid xview \$w $args + } +} + +proc MyYView {wlist args} { + foreach w $wlist { + eval grid yview \$w $args + } +} + +scrollbar .sbx -orient horizontal -command "MyXView {.ft .fc .fb}" +scrollbar .sby -orient vertical -command "MyYView {.fl .fc .fr}" +grid xscrollcommand .fc ".sbx set" +grid yscrollcommand .fc ".sby set" + +ttk::separator .st -orient horizontal +ttk::separator .sb -orient horizontal +ttk::separator .sl -orient vertical +ttk::separator .sr -orient vertical + +grid x .sl .ft .sr x x -sticky news +grid .st - - - - - -sticky news +grid .fl x .fc x .fr .sby -sticky news +grid .sb - - - - - -sticky news +grid x x .fb x x x -sticky news +grid x x .sbx x x x -sticky news + +grid .sl .sr -rowspan 6 +grid columnconfigure . .fc -weight 1 +grid rowconfigure . .fc -weight 1 + +grid columnpeer .fc .ft +grid columnpeer .fc .fb +grid rowpeer .fc .fl +grid rowpeer .fc .fr +puts [grid columnpeer .fc] +puts [grid rowpeer .fc] +puts [grid size .ft] +puts [grid size .fc] +# Possible way to do it: +#grid masterconfigure .f -xscrollcommand ".sbx set" +#grid masterconfigure .f -yscrollcommand ".sby set"