/[dtapublic]/projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkgrid.c
ViewVC logotype

Diff of /projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkgrid.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

to_be_filed/sf_code/esrgpcpj/shared/tk_base/tkgrid.c revision 29 by dashley, Sat Oct 8 07:08:47 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