Logo Search packages:      
Sourcecode: tcltk8.0-ja version File versions  Download package

tkGrid.c

/*
 * 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.2 1998/09/14 18:23:12 stanton 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

/*
 * Hash table used to map from Tk_Window tokens to corresponding
 * Grid structures:
 */

static Tcl_HashTable gridHashTable;
static int initialized = 0;

/*
 * 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 */

      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) {
          sprintf(interp->result, "%d %d %d %d",0,0,0,0);
          return(TCL_OK);
      }

      SetGridSize(masterPtr);
      endX = MAX(gridPtr->columnEnd, gridPtr->columnMax);
      endY = MAX(gridPtr->rowEnd, gridPtr->rowMax);

      if ((endX == 0) || (endY == 0)) {
          sprintf(interp->result, "%d %d %d %d",0,0,0,0);
          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(interp->result, "%d %d %d %d",
            x + gridPtr->startX, y + gridPtr->startY, width, height);
    } 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;
                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[70];
    
      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) {
          interp->result[0] = '\0';
          return TCL_OK;
      }
    
      Tcl_AppendElement(interp, "-in");
      Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin));
      sprintf(buffer, " -column %d -row %d -columnspan %d -rowspan %d",
            slavePtr->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 */

      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) {
          sprintf(interp->result, "%d %d", -1, -1);
          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) {
          Tk_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(interp->result, "%d %d", i, j);
    } 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) {
          interp->result = (masterPtr->flags & DONT_PROPAGATE) ? "0" : "1";
          return TCL_OK;
      }
      if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
          return TCL_ERROR;
      }
      if ((!propagate) ^ (masterPtr->flags&DONT_PROPAGATE)) {
          masterPtr->flags  ^= DONT_PROPAGATE;
      
          /*
           * Re-arrange the master to allow new geometry information to
           * propagate upwards to the master's master.
           */
      
          if (masterPtr->abortPtr != NULL) {
            *masterPtr->abortPtr = 1;
          }
          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) {
          SetGridSize(masterPtr);
          gridPtr = masterPtr->masterDataPtr;
          sprintf(interp->result, "%d %d",
            MAX(gridPtr->columnEnd, gridPtr->columnMax),
            MAX(gridPtr->rowEnd, gridPtr->rowMax));
      } else {
          sprintf(interp->result, "%d %d",0, 0);
      }
    } 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)) {
            sprintf(interp->result,"-minsize %d -pad %d -weight %d",
                  slotPtr[slot].minSize,slotPtr[slot].pad,
                  slotPtr[slot].weight);
            return (TCL_OK);
          } else if (argc == 4) {
            sprintf(interp->result,"-minsize %d -pad %d -weight %d", 0,0,0);
            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) {
                  int value =  ok == TCL_OK ? slotPtr[slot].minSize : 0;
                  sprintf(interp->result,"%d",value);
                } 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) {
                  int value =  ok == TCL_OK ? slotPtr[slot].weight : 0;
                  sprintf(interp->result,"%d",value);
                } 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) {
                  int value =  ok == TCL_OK ? slotPtr[slot].pad : 0;
                  sprintf(interp->result,"%d",value);
                } 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 *) Tcl_Alloc(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;

    if (!initialized) {
      initialized = 1;
      Tcl_InitHashTable(&gridHashTable, TCL_ONE_WORD_KEYS);
    }

    /*
     * See if there's already grid for this window.  If not,
     * then create a new one.
     */

    hPtr = Tcl_CreateHashEntry(&gridHashTable, (char *) tkwin, &new);
    if (!new) {
      return (Gridder *) Tcl_GetHashValue(hPtr);
    }
    gridPtr = (Gridder *) Tcl_Alloc(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 *) Tcl_Alloc(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 *) Tcl_Alloc(sizeof(GridMaster));
      size = sizeof(SlotInfo) * TYPICAL_SIZE;

      gridPtr->columnEnd = 0;
      gridPtr->columnMax = 0;
      gridPtr->columnPtr = (SlotInfo *) Tcl_Alloc(size);
      gridPtr->columnSpace = 0;
      gridPtr->columnSpace = TYPICAL_SIZE;
      gridPtr->rowEnd = 0;
      gridPtr->rowMax = 0;
      gridPtr->rowPtr = (SlotInfo *) Tcl_Alloc(size);
      gridPtr->rowSpace = 0;
      gridPtr->rowSpace = TYPICAL_SIZE;

      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 Tk_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;

    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(&gridHashTable,
            (char *) gridPtr->tkwin));
      if (gridPtr->flags & REQUESTED_RELAYOUT) {
          Tk_CancelIdleCall(ArrangeGrid, (ClientData) gridPtr);
      }
      gridPtr->tkwin = NULL;
      Tk_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 interp->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) {
                sprintf(interp->result,"Window can't be managed in itself");
                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;
      }

      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 - 1;
          } else {
            lastRow = 0;
          }
          lastColumn = 0;
      } else {
          other = Tk_NameToWindow(interp, lastWindow, tkwin);
          otherPtr = GetGrid(other);
          lastRow = otherPtr->row;
          lastColumn = otherPtr->column + otherPtr->numCols;
      }

      for (match=0, slavePtr = masterPtr->slavePtr; slavePtr != NULL;
                               slavePtr = slavePtr->nextPtr) {

          if (slavePtr->numCols == width
                && slavePtr->column == lastColumn
                && slavePtr->row + slavePtr->numRows == lastRow) {
            slavePtr->numRows++;
            match++;
          }
          lastWindow = Tk_PathName(slavePtr->tkwin);
      }
      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;
}           

Generated by  Doxygen 1.6.0   Back to index