Index: generic/tkGrid.c ================================================================== --- generic/tkGrid.c +++ generic/tkGrid.c @@ -142,10 +142,19 @@ * 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*/ + 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 +271,13 @@ 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 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, @@ -294,10 +306,12 @@ 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 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); @@ -308,10 +322,49 @@ 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; + + if ((xOrigin == gridPtr->xOrigin) && (yOrigin == gridPtr->yOrigin)) { + return; + } + + gridPtr->xOrigin = xOrigin; + gridPtr->yOrigin = yOrigin; + + if (!(masterPtr->flags & REQUESTED_RELAYOUT)) { + masterPtr->flags |= REQUESTED_RELAYOUT; + Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr); + } +} /* *---------------------------------------------------------------------- * * Tk_GridCmd -- @@ -335,25 +388,27 @@ 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", "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_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 +420,106 @@ 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); + } + return TCL_OK; + } case GRID_ANCHOR: return GridAnchorCommand(tkwin, interp, objc, objv); case GRID_BBOX: return GridBboxCommand(tkwin, interp, objc, objv); case GRID_CONFIGURE: @@ -404,10 +555,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 -- @@ -1808,10 +2125,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 */ + + 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. */ @@ -2714,10 +3051,17 @@ 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->xOrigin = 0; + gridPtr->yOrigin = 0; + gridPtr->xUsed = 0; + gridPtr->yUsed = 0; + gridPtr->interp = NULL; memset(gridPtr->columnPtr, 0, size); memset(gridPtr->rowPtr, 0, size); } } ADDED gs.tcl Index: gs.tcl ================================================================== --- gs.tcl +++ gs.tcl @@ -0,0 +1,25 @@ + + +frame .f +for {set row 0} {$row < 20} {incr row} { + for {set col 0} {$col < 10} {incr col} { + set w .f.l$row,$col + label $w -text "$col,$row" + grid $w -row $row -column $col -sticky we + } +} + +scrollbar .sbx -orient horizontal -command "grid xview .f" +scrollbar .sby -orient vertical -command "grid yview .f" +grid xscrollcommand .f ".sbx set" +grid yscrollcommand .f ".sby set" + +grid .f .sby -sticky news +grid .sbx -sticky we +grid columnconfigure . 0 -weight 1 +grid rowconfigure . 0 -weight 1 + + +# Possible way to do it: +#grid masterconfigure .f -xscrollcommand ".sbx set" +#grid masterconfigure .f -yscrollcommand ".sby set"