home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tkisrc04.zip / tk / os2 / tkGrid.c < prev    next >
C/C++ Source or Header  |  1998-08-07  |  61KB  |  2,062 lines

  1. /* 
  2.  * tkGrid.c --
  3.  *
  4.  *    Grid based geometry manager.
  5.  *
  6.  * Copyright (c) 1996 by Sun Microsystems, Inc.
  7.  *
  8.  * See the file "license.terms" for information on usage and redistribution
  9.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  10.  *
  11.  *
  12.  * SCCS: @(#) tkGrid.c 1.21 96/02/21 10:50:58
  13.  */
  14.  
  15. #include "tkInt.h"
  16.  
  17. /*
  18.  * LayoutInfo structure.  We shouldn't be using hard-wired limits!
  19.  */
  20.  
  21. #define MAXGRIDSIZE 128
  22. #ifndef MAXINT
  23. #  define MAXINT 0x7fff
  24. #endif
  25. #define MINWEIGHT    0.0001        /* weight totals < this are considered to be zero */
  26.  
  27. /*
  28.  * Special characters to support relative layouts
  29.  */
  30.  
  31. #define REL_SKIP    'x'    /* skip this column */
  32. #define REL_HORIZ    '-'    /* extend previous widget horizontally */
  33. #define REL_VERT    '^'    /* extend previous widget verticallly */
  34.  
  35. /*
  36.  *  structure to hold collected constraints temporarily:
  37.  *  needs to use a "Constrain" thingy
  38.  */
  39.  
  40. typedef struct {
  41.     int width, height;        /* number of cells horizontally, vertically */
  42.     int lastRow;            /* last cell with a window in it */
  43.     int minWidth[MAXGRIDSIZE];    /* largest minWidth in each column */
  44.     int minHeight[MAXGRIDSIZE];    /* largest minHeight in each row */
  45.     double weightX[MAXGRIDSIZE];    /* largest weight in each column */
  46.     double weightY[MAXGRIDSIZE];    /* largest weight in each row */
  47. } LayoutInfo;
  48.  
  49. /* structure for holding row and column constraints */
  50.  
  51. typedef struct {
  52.     int used;        /* maximum element used */
  53.     int max;        /* maximum element allocated */
  54.     int *minsize;        /* array of minimum column/row sizes */
  55.     double *weight;    /* array of column/row weights */
  56. } Constrain;
  57.  
  58. /* For each window that the gridbag cares about (either because
  59.  * the window is managed by the gridbag or because the window
  60.  * has slaves that are managed by the gridbag), there is a
  61.  * structure of the following type:
  62.  */
  63.  
  64. typedef struct GridBag {
  65.     Tk_Window tkwin;        /* Tk token for window.  NULL means that
  66.                  * the window has been deleted, but the
  67.                  * packet hasn't had a chance to clean up
  68.                  * yet because the structure is still in
  69.                  * use. */
  70.     struct GridBag *masterPtr;    /* Master window within which this window
  71.                  * is managed (NULL means this window
  72.                  * isn't managed by the gridbag). */
  73.     struct GridBag *nextPtr;    /* Next window managed within same
  74.                  * parent.  List is priority-ordered:
  75.                  * first on list gets layed out first. */
  76.     struct GridBag *slavePtr;    /* First in list of slaves managed
  77.                  * inside this window (NULL means
  78.                  * no gridbag slaves). */
  79.  
  80.     int gridColumn, gridRow;
  81.     int gridWidth, gridHeight;
  82.  
  83.     int tempX, tempY;
  84.     int tempWidth, tempHeight;
  85.  
  86.     double weightX, weightY;
  87.     int minWidth, minHeight;
  88.  
  89.     int padX, padY;        /* Total additional pixels to leave around the
  90.                  * window (half of this space is left on each
  91.                  * side).  This is space *outside* the window:
  92.                  * we'll allocate extra space in frame but
  93.                  * won't enlarge window). */
  94.     int iPadX, iPadY;        /* Total extra pixels to allocate inside the
  95.                  * window (half this amount will appear on
  96.                  * each side). */
  97.     int startx, starty;        /* starting location of layout */
  98.  
  99.     int doubleBw;        /* Twice the window's last known border
  100.                  * width.  If this changes, the window
  101.                  * must be re-arranged within its parent. */
  102.     int *abortPtr;        /* If non-NULL, it means that there is a nested
  103.                  * call to ArrangeGrid already working on
  104.                  * this window.  *abortPtr may be set to 1 to
  105.                  * abort that nested call.  This happens, for
  106.                  * example, if tkwin or any of its slaves
  107.                  * is deleted. */
  108.     int flags;            /* Miscellaneous flags;  see below
  109.                  * for definitions. */
  110.  
  111.     Constrain row, column;        /* column and row constraints */
  112.  
  113.     int valid;
  114.     LayoutInfo *layoutCache;
  115. } GridBag;
  116.  
  117. /*
  118.  * Flag values for GridBag structures:
  119.  *
  120.  * REQUESTED_RELAYOUT:        1 means a Tk_DoWhenIdle request
  121.  *                has already been made to re-arrange
  122.  *                all the slaves of this window.
  123.  * STICK_NORTH          1 means this window sticks to the edgth of its
  124.  * STICK_EAST            cavity
  125.  * STICK_SOUTH
  126.  * STICK_WEST
  127.  *
  128.  * DONT_PROPAGATE:        1 means don't set this window's requested
  129.  *                size.  0 means if this window is a master
  130.  *                then Tk will set its requested size to fit
  131.  *                the needs of its slaves.
  132.  */
  133.  
  134. #define STICK_NORTH        1
  135. #define STICK_EAST        2
  136. #define STICK_SOUTH        4
  137. #define STICK_WEST        8
  138. #define STICK_ALL        (STICK_NORTH|STICK_EAST|STICK_SOUTH|STICK_WEST)
  139.  
  140. #define REQUESTED_RELAYOUT    16
  141. #define DONT_PROPAGATE        32
  142.  
  143. /*
  144.  * Hash table used to map from Tk_Window tokens to corresponding
  145.  * GridBag structures:
  146.  */
  147.  
  148. static Tcl_HashTable gridBagHashTable;
  149.  
  150. /*
  151.  * Have statics in this module been initialized?
  152.  */
  153.  
  154. static initialized = 0;
  155.  
  156. /*
  157.  * Prototypes for procedures used only in this file:
  158.  */
  159.  
  160. static void        ArrangeGrid _ANSI_ARGS_((ClientData clientData));
  161. static int        ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
  162.                 Tk_Window tkwin, int argc, char *argv[]));
  163. static void        DestroyGridBag _ANSI_ARGS_((char *memPtr));
  164. static void        GetCachedLayoutInfo _ANSI_ARGS_((GridBag *masterPtr));
  165. static GridBag *    GetGridBag _ANSI_ARGS_((Tk_Window tkwin));
  166. static void        GetLayoutInfo _ANSI_ARGS_((GridBag *masterPtr,
  167.                 LayoutInfo *r));
  168. static void        GetMinSize _ANSI_ARGS_((GridBag *masterPtr,
  169.                 LayoutInfo *info, int *minw, int *minh));
  170. static void        GridBagStructureProc _ANSI_ARGS_((
  171.                 ClientData clientData, XEvent *eventPtr));
  172. static void        GridLostSlaveProc _ANSI_ARGS_((ClientData clientData,
  173.                 Tk_Window tkwin));
  174. static void        GridReqProc _ANSI_ARGS_((ClientData clientData,
  175.                 Tk_Window tkwin));
  176. static void        GridBagStructureProc _ANSI_ARGS_((
  177.                 ClientData clientData, XEvent *eventPtr));
  178. static void        StickyToString _ANSI_ARGS_((int flags, char *result));
  179. static int        StringToSticky _ANSI_ARGS_((char *string));
  180. static void        Unlink _ANSI_ARGS_((GridBag *gridPtr));
  181.  
  182. static Tk_GeomMgr gridMgrType = {
  183.     "grid",            /* name */
  184.     GridReqProc,        /* requestProc */
  185.     GridLostSlaveProc,        /* lostSlaveProc */
  186. };
  187.  
  188. /*
  189.  *--------------------------------------------------------------
  190.  *
  191.  * Tk_GridCmd --
  192.  *
  193.  *    This procedure is invoked to process the "grid" Tcl command.
  194.  *    See the user documentation for details on what it does.
  195.  *
  196.  * Results:
  197.  *    A standard Tcl result.
  198.  *
  199.  * Side effects:
  200.  *    See the user documentation.
  201.  *
  202.  *--------------------------------------------------------------
  203.  */
  204.  
  205. int
  206. Tk_GridCmd(clientData, interp, argc, argv)
  207.     ClientData clientData;    /* Main window associated with
  208.                  * interpreter. */
  209.     Tcl_Interp *interp;        /* Current interpreter. */
  210.     int argc;            /* Number of arguments. */
  211.     char **argv;        /* Argument strings. */
  212. {
  213.     Tk_Window tkwin = (Tk_Window) clientData;
  214.     size_t length;
  215.     char c;
  216.   
  217.     if ((argc >= 2) && (argv[1][0] == '.')) {
  218.     return ConfigureSlaves(interp, tkwin, argc-1, argv+1);
  219.     }
  220.     if (argc < 3) {
  221.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  222.         argv[0], " option arg ?arg ...?\"", (char *) NULL);
  223.     return TCL_ERROR;
  224.     }
  225.     c = argv[1][0];
  226.     length = strlen(argv[1]);
  227.   
  228.     if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
  229.     Tk_Window master;
  230.     GridBag *masterPtr;
  231.     int row, column;
  232.     int i, x, y;
  233.     int prevX, prevY;
  234.     int width, height;
  235.     double weight;
  236.     int diff;
  237.  
  238.     if (argc != 5) {
  239.         Tcl_AppendResult(interp, "Wrong number of arguments: ",
  240.             "must be \"",argv[0],
  241.             " bbox <master> <column> <row>\"", (char *) NULL);
  242.         return TCL_ERROR;
  243.     }
  244.         
  245.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  246.     if (master == NULL) {
  247.         return TCL_ERROR;
  248.     }
  249.     if (Tcl_GetInt(interp, argv[3], &column) != TCL_OK) {
  250.         return TCL_ERROR;
  251.     }
  252.     if (Tcl_GetInt(interp, argv[4], &row) != TCL_OK) {
  253.         return TCL_ERROR;
  254.     }
  255.     masterPtr = GetGridBag(master);
  256.  
  257.     /* make sure the grid is up to snuff */
  258.  
  259.     while ((masterPtr->flags & REQUESTED_RELAYOUT)) {
  260.         Tk_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
  261.         ArrangeGrid((ClientData) masterPtr);
  262.     }
  263.     GetCachedLayoutInfo(masterPtr);
  264.  
  265.     if (row < 0 || column < 0) {
  266.         *interp->result = '\0';
  267.         return TCL_OK;
  268.     }
  269.     if (column >= masterPtr->layoutCache->width ||
  270.         row >= masterPtr->layoutCache->height) {
  271.         *interp->result = '\0';
  272.         return TCL_OK;
  273.     }
  274.     x = masterPtr->startx;
  275.     y = masterPtr->starty;
  276.     GetMinSize(masterPtr, masterPtr->layoutCache, &width, &height);
  277.  
  278.     diff = Tk_Width(masterPtr->tkwin) - (width + masterPtr->iPadX);
  279.     for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++)
  280.         weight += masterPtr->layoutCache->weightX[i];
  281.  
  282.     prevX = 0;            /* Needed to prevent gcc warning. */
  283.     for (i=0; i<=column; i++) {
  284.         int dx = 0;
  285.         if (weight > MINWEIGHT) {
  286.         dx = (int)((((double)diff) * masterPtr->layoutCache->weightX[i])
  287.             / weight);
  288.         }
  289.         prevX = x;
  290.         x += masterPtr->layoutCache->minWidth[i] + dx;
  291.     }
  292.     diff = Tk_Height(masterPtr->tkwin) - (height + masterPtr->iPadY);
  293.     for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++) {
  294.         weight += masterPtr->layoutCache->weightY[i];
  295.     }
  296.     prevY = 0;            /* Needed to prevent gcc warning. */
  297.     for (i=0; i<=row; i++) {
  298.         int dy = 0;
  299.         if (weight > MINWEIGHT) {
  300.         dy = (int)((((double)diff) * masterPtr->layoutCache->weightY[i])
  301.             / weight);
  302.         }
  303.         prevY = y;
  304.         y += masterPtr->layoutCache->minHeight[i] + dy;
  305.     }
  306.     sprintf(interp->result,"%d %d %d %d",prevX,prevY,x - prevX,y - prevY);
  307.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  308.     if (argv[2][0] != '.') {
  309.         Tcl_AppendResult(interp, "bad argument \"", argv[2],
  310.             "\": must be name of window", (char *) NULL);
  311.         return TCL_ERROR;
  312.     }
  313.     return ConfigureSlaves(interp, tkwin, argc-2, argv+2);
  314.     } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
  315.     Tk_Window slave;
  316.     GridBag *slavePtr;
  317.     int i;
  318.     
  319.     for (i = 2; i < argc; i++) {
  320.         slave = Tk_NameToWindow(interp, argv[i], tkwin);
  321.         if (slave == NULL) {
  322.         return TCL_ERROR;
  323.         }
  324.         slavePtr = GetGridBag(slave);
  325.         if (slavePtr->masterPtr != NULL) {
  326.         Tk_ManageGeometry(slave, (Tk_GeomMgr *) NULL,
  327.             (ClientData) NULL);
  328.         Unlink(slavePtr);
  329.         Tk_UnmapWindow(slavePtr->tkwin);
  330.         }
  331.     }
  332.     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
  333.     register GridBag *slavePtr;
  334.     Tk_Window slave;
  335.     char buffer[64];
  336.     
  337.     if (argc != 3) {
  338.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  339.             argv[0], " info window\"", (char *) NULL);
  340.         return TCL_ERROR;
  341.     }
  342.     slave = Tk_NameToWindow(interp, argv[2], tkwin);
  343.     if (slave == NULL) {
  344.         return TCL_ERROR;
  345.     }
  346.     slavePtr = GetGridBag(slave);
  347.     if (slavePtr->masterPtr == NULL) {
  348.         interp->result[0] = '\0';
  349.         return TCL_OK;
  350.     }
  351.     
  352.     Tcl_AppendElement(interp, "-in");
  353.     Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin));
  354.     sprintf(buffer, " -column %d -row %d -columnspan %d -rowspan %d",
  355.         slavePtr->gridColumn, slavePtr->gridRow,
  356.         slavePtr->gridWidth, slavePtr->gridHeight);
  357.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  358.     sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
  359.         slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
  360.         slavePtr->padY/2);
  361.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  362.     StickyToString(slavePtr->flags,buffer);
  363.     Tcl_AppendResult(interp, " -sticky ", buffer, (char *) NULL);
  364. /*
  365.     sprintf(buffer, " -weightx %.2f -weighty %.2f",
  366.         slavePtr->weightX, slavePtr->weightY);
  367.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  368. */
  369.     } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
  370.     Tk_Window master;
  371.     GridBag *masterPtr;
  372.     int propagate;
  373.     
  374.     if (argc > 4) {
  375.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  376.             argv[0], " propagate window ?boolean?\"",
  377.             (char *) NULL);
  378.         return TCL_ERROR;
  379.     }
  380.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  381.     if (master == NULL) {
  382.         return TCL_ERROR;
  383.     }
  384.     masterPtr = GetGridBag(master);
  385.     if (argc == 3) {
  386.         interp->result = (masterPtr->flags & DONT_PROPAGATE) ? "0" : "1";
  387.         return TCL_OK;
  388.     }
  389.     if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
  390.         return TCL_ERROR;
  391.     }
  392.     if (propagate) {
  393.         masterPtr->flags &= ~DONT_PROPAGATE;
  394.       
  395.         /*
  396.          * Re-arrange the master to allow new geometry information to
  397.          * propagate upwards to the master\'s master.
  398.          */
  399.       
  400.         if (masterPtr->abortPtr != NULL) {
  401.         *masterPtr->abortPtr = 1;
  402.         }
  403.         masterPtr->valid = 0;
  404.         if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
  405.         masterPtr->flags |= REQUESTED_RELAYOUT;
  406.         Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
  407.         }
  408.     } else {
  409.         masterPtr->flags |= DONT_PROPAGATE;
  410.     }
  411.     } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)) {
  412.     Tk_Window master;
  413.     GridBag *masterPtr;
  414.  
  415.     if (argc != 3) {
  416.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  417.             argv[0], " size window\"", (char *) NULL);
  418.         return TCL_ERROR;
  419.     }
  420.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  421.     if (master == NULL)
  422.         return TCL_ERROR;
  423.     masterPtr = GetGridBag(master);
  424.     GetCachedLayoutInfo(masterPtr);
  425.  
  426.     sprintf(interp->result, "%d %d", masterPtr->layoutCache->width,
  427.         masterPtr->layoutCache->height);
  428.     } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
  429.     Tk_Window master;
  430.     GridBag *masterPtr, *slavePtr;
  431.     int i, value;
  432.     int row = -1, column = -1;
  433.  
  434.     if (argc < 3 || argc%2 ==0) {
  435.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  436.             argv[0], " slaves window ?-option value...?\"",
  437.             (char *) NULL);
  438.         return TCL_ERROR;
  439.     }
  440.  
  441.     for (i=3; i<argc; i+=2) {
  442.         if (*argv[i] != '-' || (length = strlen(argv[i])) < 2) {
  443.         Tcl_AppendResult(interp, "Invalid args: should be \"",
  444.             argv[0], " slaves window ?-option value...?\"",
  445.             (char *) NULL);
  446.         return TCL_ERROR;
  447.         }
  448.         if (Tcl_GetInt(interp, argv[i+1], &value) != TCL_OK) {
  449.         return TCL_ERROR;
  450.         }
  451.         if (value < 0) {
  452.         Tcl_AppendResult(interp, argv[i],
  453.             " is an invalid value: should NOT be < 0",
  454.             (char *) NULL);
  455.         return TCL_ERROR;
  456.         }
  457.         if (strncmp(argv[i], "-column", length) == 0) {
  458.         column = value;
  459.         } else if (strncmp(argv[i], "-row", length) == 0) {
  460.         row = value;
  461.         } else {
  462.         Tcl_AppendResult(interp, argv[i],
  463.             " is an invalid option: should be \"",
  464.             "-row, -column\"",
  465.             (char *) NULL);
  466.         return TCL_ERROR;
  467.         }
  468.     }
  469.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  470.     if (master == NULL) {
  471.         return TCL_ERROR;
  472.     }
  473.     masterPtr = GetGridBag(master);
  474.  
  475.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  476.                          slavePtr = slavePtr->nextPtr) {
  477.         if (column>=0 && (slavePtr->gridColumn > column
  478.             || slavePtr->gridColumn+slavePtr->gridWidth-1 < column)) {
  479.         continue;
  480.         }
  481.         if (row>=0 && (slavePtr->gridRow > row ||
  482.             slavePtr->gridRow+slavePtr->gridHeight-1 < row)) {
  483.         continue;
  484.         }
  485.         Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin));
  486.     }
  487.  
  488.     /*
  489.      * grid columnconfigure <master> <index> -option
  490.      * grid columnconfigure <master> <index> -option value -option value
  491.      * grid rowconfigure <master> <index> -option
  492.      * grid rowconfigure <master> <index> -option value -option value
  493.      */
  494.    
  495.     } else if(((c=='c') && (strncmp(argv[1], "columnconfigure", length) == 0)) ||
  496.             ((c=='r') && (strncmp(argv[1], "rowconfigure", length) == 0))) {
  497.     Tk_Window master;
  498.     GridBag *masterPtr;
  499.     Constrain *con;
  500.     int index, i, size;
  501.     double weight;
  502.  
  503.     if (argc != 5 && (argc < 5 || argc%2 == 1)) {
  504.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  505.             " ", argv[1], " master index ?-option value...?\"",
  506.             (char *)NULL);
  507.         return TCL_ERROR;
  508.     }
  509.  
  510.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  511.     if (master == NULL) {
  512.         return TCL_ERROR;
  513.     }
  514.     masterPtr = GetGridBag(master);
  515.     con = (c=='c') ? &(masterPtr->column) : &(masterPtr->row);
  516.  
  517.     if (Tcl_GetInt(interp, argv[3], &index) != TCL_OK) {
  518.         return TCL_ERROR;
  519.     }
  520.     if (index < 0 || index >= MAXGRIDSIZE) {
  521.         Tcl_AppendResult(interp, argv[3], " is out of range",
  522.             (char *)NULL);
  523.         return TCL_ERROR;
  524.     }
  525.  
  526.     /*
  527.      *  make sure the row/column constraint array is allocated.  This
  528.      *  Should be changed to avoid hard-wired limits.  We'll wimp out
  529.      *  for now.
  530.      */
  531.  
  532.     if (con->max == 0) {
  533.         unsigned int size;
  534.         con->max = MAXGRIDSIZE;
  535.         con->used = 0;
  536.  
  537.         size = MAXGRIDSIZE * sizeof(con->minsize[0]);
  538.         con->minsize = (int *) ckalloc(size);
  539.         memset(con->minsize, 0, size);
  540.  
  541.         size = MAXGRIDSIZE * sizeof(con->weight[0]);
  542.         con->weight = (double *) ckalloc(size);
  543.         memset(con->weight, 0, size);
  544.     }
  545.  
  546.     for (i=4; i<argc; i+=2) {
  547.         if (*argv[i] != '-' || (length = strlen(argv[i])) < 2) {
  548.         Tcl_AppendResult(interp, "Invalid arg: \"",
  549.             argv[0], "\" expecting -minsize or -weight",
  550.             (char *) NULL);
  551.         return TCL_ERROR;
  552.         }
  553.         if (strncmp(argv[i], "-minsize", length) == 0) {
  554.         if (argc == 5) {
  555.             size = con->used <= index ?  0 : con->minsize[index];
  556.             sprintf(interp->result, "%d", size);
  557.         } else if (Tk_GetPixels(interp, master, argv[i+1], &size)
  558.             != TCL_OK) {
  559.             return TCL_ERROR;
  560.         } else {
  561.             con->minsize[index] = size;
  562.             if (size > 0 && index >= con->used) 
  563.             con->used = index+1;
  564.             else if (size == 0 && index+1 == con->used) {
  565.             while (index >= 0  && (con->minsize[index]==0) &&
  566.                 (con->weight[index] == 0.0)) {
  567.                 index--;
  568.             }
  569.             con->used = index + 1;
  570.             }
  571.         }
  572.         } else if (strncmp(argv[i], "-weight", length) == 0) {
  573.         if (argc == 5) {
  574.             weight = con->used <= index ?  0 : con->weight[index];
  575.             sprintf(interp->result, "%.2f", weight);
  576.         } else if (Tcl_GetDouble(interp, argv[i+1], &weight) != TCL_OK) {
  577.             return TCL_ERROR;
  578.         } else {
  579.             con->weight[index] = weight;
  580.             if (weight > MINWEIGHT && index >= con->used) 
  581.             con->used = index+1;
  582.             else if (weight == 0.0 && index+1 == con->used) {
  583.             while (index >= 0 && (con->minsize[index]==0) &&
  584.                 (con->weight[index] == 0.0)) {
  585.                 index--;
  586.             }
  587.             con->used = index + 1;
  588.             }
  589.         }
  590.         } else {
  591.         Tcl_AppendResult(interp, argv[i],
  592.             " is an invalid option: should be \"",
  593.             "-minsize, -weight\"",
  594.             (char *) NULL);
  595.         return TCL_ERROR;
  596.         }
  597.     }
  598.  
  599.     /* if we changed a property, re-arrange the table */
  600.  
  601.     if (argc != 5) {
  602.         if (masterPtr->abortPtr != NULL) {
  603.         *masterPtr->abortPtr = 1;
  604.         }
  605.         masterPtr->valid = 0;
  606.         if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
  607.         masterPtr->flags |= REQUESTED_RELAYOUT;
  608.         Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
  609.         }
  610.     }
  611.     } else if((c == 'l') && (strncmp(argv[1], "location", length) == 0)) {
  612.     Tk_Window master;
  613.     GridBag *masterPtr;
  614.     int x, y, i, j, w, h;
  615.     int width, height;
  616.     double weight;
  617.     int diff;
  618.  
  619.     if (argc != 5) {
  620.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  621.             argv[0], " location master x y\"", (char *)NULL);
  622.         return TCL_ERROR;
  623.     }
  624.  
  625.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  626.     if (master == NULL) {
  627.         return TCL_ERROR;
  628.     }
  629.     masterPtr = GetGridBag(master);
  630.  
  631.     if (Tk_GetPixels(interp, master, argv[3], &x) != TCL_OK) {
  632.         return TCL_ERROR;
  633.     }
  634.     if (Tk_GetPixels(interp, master, argv[4], &y) != TCL_OK) {
  635.         return TCL_ERROR;
  636.     }
  637.  
  638.     /* make sure the grid is up to snuff */
  639.  
  640.     while ((masterPtr->flags & REQUESTED_RELAYOUT)) {
  641.         Tk_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
  642.         ArrangeGrid((ClientData) masterPtr);
  643.     }
  644.     GetCachedLayoutInfo(masterPtr);
  645.     GetMinSize(masterPtr, masterPtr->layoutCache, &width, &height);
  646.  
  647.     diff = Tk_Width(masterPtr->tkwin) - (width + masterPtr->iPadX);
  648.     for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++) {
  649.         weight += masterPtr->layoutCache->weightX[i];
  650.     }
  651.     w = masterPtr->startx;
  652.     if (w > x) {
  653.         i = -1;
  654.     } else {
  655.         for (i=0; i<masterPtr->layoutCache->width; i++) {
  656.         int dx = 0;
  657.         if (weight > MINWEIGHT) {
  658.             dx = (int)((((double)diff) * masterPtr->layoutCache->weightX[i])
  659.                 / weight);
  660.             }
  661.         w += masterPtr->layoutCache->minWidth[i] + dx;
  662.         if (w > x) {
  663.             break;
  664.         }
  665.         }
  666.     }
  667.  
  668.     diff = Tk_Height(masterPtr->tkwin) - (height + masterPtr->iPadY);
  669.     for (weight=0.0, j = 0; j < masterPtr->layoutCache->height; j++)
  670.         weight += masterPtr->layoutCache->weightY[j];
  671.     h = masterPtr->starty;
  672.     if (h > y) {
  673.         j = -1;
  674.     } else {
  675.         for (j=0; j<masterPtr->layoutCache->height; j++) {
  676.         int dy = 0;
  677.         if (weight > MINWEIGHT) {
  678.             dy = (int)((((double)diff) * masterPtr->layoutCache->weightY[j])
  679.                 / weight);
  680.         }
  681.         h += masterPtr->layoutCache->minHeight[j] + dy;
  682.         if (h > y) {
  683.             break;
  684.         }
  685.         }
  686.     }
  687.     sprintf(interp->result, "%d %d", i, j);
  688.     } else {
  689.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  690.         "\":  must be bbox, columnconfigure, configure, forget, info, ",
  691.         "location, propagate, rowconfigure, size, or slaves",
  692.         (char *) NULL);
  693.     return TCL_ERROR;
  694.     }
  695.     return TCL_OK;
  696. }
  697.  
  698. /*
  699.  *--------------------------------------------------------------
  700.  *
  701.  * GridReqProc --
  702.  *
  703.  *    This procedure is invoked by Tk_GeometryRequest for
  704.  *    windows managed by the gridbag.
  705.  *
  706.  * Results:
  707.  *    None.
  708.  *
  709.  * Side effects:
  710.  *    Arranges for tkwin, and all its managed siblings, to
  711.  *    be re-arranged at the next idle point.
  712.  *
  713.  *--------------------------------------------------------------
  714.  */
  715.  
  716. /* ARGSUSED */
  717. static void
  718. GridReqProc(clientData, tkwin)
  719.     ClientData clientData;    /* GridBag's information about
  720.                  * window that got new preferred
  721.                  * geometry.  */
  722.     Tk_Window tkwin;        /* Other Tk-related information
  723.                  * about the window. */
  724. {
  725.     register GridBag *gridPtr = (GridBag *) clientData;
  726.  
  727.     gridPtr = gridPtr->masterPtr;
  728.     gridPtr->valid = 0;
  729.     if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
  730.     gridPtr->flags |= REQUESTED_RELAYOUT;
  731.     Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
  732.     }
  733. }
  734.  
  735.  
  736. /*
  737.  *--------------------------------------------------------------
  738.  *
  739.  * GridLostSlaveProc --
  740.  *
  741.  *    This procedure is invoked by Tk whenever some other geometry
  742.  *    claims control over a slave that used to be managed by us.
  743.  *
  744.  * Results:
  745.  *    None.
  746.  *
  747.  * Side effects:
  748.  *    Forgets all grid-related information about the slave.
  749.  *
  750.  *--------------------------------------------------------------
  751.  */
  752.  
  753.     /* ARGSUSED */
  754. static void
  755. GridLostSlaveProc(clientData, tkwin)
  756.     ClientData clientData;    /* GridBag structure for slave window that
  757.                  * was stolen away. */
  758.     Tk_Window tkwin;        /* Tk's handle for the slave window. */
  759. {
  760.     register GridBag *slavePtr = (GridBag *) clientData;
  761.  
  762.     if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
  763.     Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
  764.     }
  765.     Unlink(slavePtr);
  766.     Tk_UnmapWindow(slavePtr->tkwin);
  767. }
  768.  
  769. /*
  770.  * Fill in an instance of the above structure for the current set
  771.  * of managed children.  This requires two passes through the
  772.  * set of children, first to figure out what cells they occupy
  773.  * and how many rows and columns there are, and then to distribute
  774.  * the weights and min sizes amoung the rows/columns.
  775.  *
  776.  * This also caches the minsizes for all the children when they are
  777.  * first encountered.
  778.  */
  779.  
  780. static void
  781. GetLayoutInfo(masterPtr, r)
  782.     GridBag *masterPtr;
  783.     LayoutInfo *r;
  784. {
  785.     register GridBag *slavePtr;
  786.     int i, k, px, py, pixels_diff, nextSize;
  787.     double weight_diff, weight;
  788.     register int curX, curY, curWidth, curHeight, curRow, curCol;
  789.     int xMax[MAXGRIDSIZE];
  790.     int yMax[MAXGRIDSIZE];
  791.  
  792.     /*
  793.      * Pass #1
  794.      *
  795.      * Figure out the dimensions of the layout grid.
  796.      */
  797.  
  798.     r->width = r->height = 0;
  799.     curRow = curCol = -1;
  800.     memset(xMax, 0, sizeof(int) * MAXGRIDSIZE);
  801.     memset(yMax, 0, sizeof(int) * MAXGRIDSIZE);
  802.  
  803.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  804.                      slavePtr = slavePtr->nextPtr) {
  805.  
  806.     curX = slavePtr->gridColumn;
  807.     curY = slavePtr->gridRow;
  808.     curWidth = slavePtr->gridWidth;
  809.     curHeight = slavePtr->gridHeight;
  810.  
  811.     /* Adjust the grid width and height */
  812.     for (px = curX + curWidth; r->width < px; r->width++) {
  813.         /* Null body. */
  814.     }
  815.     for (py = curY + curHeight; r->height < py; r->height++) {
  816.         /* Null body. */
  817.     }
  818.  
  819.     /* Adjust the xMax and yMax arrays */
  820.     for (i = curX; i < (curX + curWidth); i++) {
  821.         yMax[i] = py;
  822.     }
  823.     for (i = curY; i < (curY + curHeight); i++) {
  824.         xMax[i] = px;
  825.     }
  826.  
  827.     /* Cache the current slave's size. */
  828.     slavePtr->minWidth = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw;
  829.     slavePtr->minHeight = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw;
  830.     }
  831.  
  832.     /*
  833.      * Apply minimum row/column dimensions
  834.      */ 
  835.     if (r->width < masterPtr->column.used) {
  836.     r->width = masterPtr->column.used;
  837.     }
  838.     r->lastRow = r->height;
  839.     if (r->height < masterPtr->row.used) {
  840.     r->height = masterPtr->row.used;
  841.     }
  842.  
  843.     /*
  844.      * Pass #2
  845.      */
  846.  
  847.     curRow = curCol = -1;
  848.     memset(xMax, 0, sizeof(int) * MAXGRIDSIZE);
  849.     memset(yMax, 0, sizeof(int) * MAXGRIDSIZE);
  850.  
  851.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  852.                      slavePtr = slavePtr->nextPtr) {
  853.     curX = slavePtr->gridColumn;
  854.     curY = slavePtr->gridRow;
  855.     curWidth = slavePtr->gridWidth;
  856.     curHeight = slavePtr->gridHeight;
  857.  
  858.     px = curX + curWidth;
  859.     py = curY + curHeight;
  860.  
  861.     for (i = curX; i < (curX + curWidth); i++) {
  862.         yMax[i] = py;
  863.     }
  864.     for (i = curY; i < (curY + curHeight); i++) {
  865.         xMax[i] = px;
  866.     }
  867.  
  868.     /* Assign the new values to the gridbag slave */
  869.     slavePtr->tempX = curX;
  870.     slavePtr->tempY = curY;
  871.     slavePtr->tempWidth = curWidth;
  872.     slavePtr->tempHeight = curHeight;
  873.     }
  874.  
  875.     /*
  876.      * Pass #3
  877.      *
  878.      * Distribute the minimun widths and weights:
  879.      */
  880.  
  881.     /* Initialize arrays to zero */
  882.     memset(r->minWidth, 0, r->width * sizeof(int));
  883.     memset(r->minHeight, 0, r->height * sizeof(int));
  884.     memset(r->weightX, 0, r->width * sizeof(double));
  885.     memset(r->weightY, 0, r->height * sizeof(double));
  886.     nextSize = MAXINT;
  887.  
  888.     for (i = 1; i != MAXINT; i = nextSize, nextSize = MAXINT) {
  889.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  890.                          slavePtr = slavePtr->nextPtr) {
  891.  
  892.         if (slavePtr->tempWidth == i) {
  893.         px = slavePtr->tempX + slavePtr->tempWidth; /* right column */
  894.  
  895.         /* 
  896.          * Figure out if we should use this slave\'s weight.  If the weight
  897.          * is less than the total weight spanned by the width of the cell,
  898.          * then discard the weight.  Otherwise split it the difference
  899.          * according to the existing weights.
  900.          */
  901.  
  902.         weight_diff = slavePtr->weightX;
  903.         for (k = slavePtr->tempX; k < px; k++)
  904.             weight_diff -= r->weightX[k];
  905.         if (weight_diff > 0.0) {
  906.             weight = 0.0;
  907.             for (k = slavePtr->tempX; k < px; k++)
  908.             weight += r->weightX[k];
  909.             for (k = slavePtr->tempX; weight > MINWEIGHT; k++) {
  910.             double wt = r->weightX[k];
  911.             double dx = (wt * weight_diff) / weight;
  912.             r->weightX[k] += dx;
  913.             weight_diff -= dx;
  914.             weight -= wt;
  915.             }
  916.             /* Assign the remainder to the rightmost cell */
  917.             r->weightX[px-1] += weight_diff;
  918.         }
  919.  
  920.         /*
  921.          * Calculate the minWidth array values.
  922.          * First, figure out how wide the current slave needs to be.
  923.          * Then, see if it will fit within the current minWidth values.
  924.          * If it won\'t fit, add the difference according to the weightX array.
  925.          */
  926.  
  927.         pixels_diff = slavePtr->minWidth + slavePtr->padX + slavePtr->iPadX;
  928.         for (k = slavePtr->tempX; k < px; k++)
  929.             pixels_diff -= r->minWidth[k];
  930.         if (pixels_diff > 0) {
  931.             weight = 0.0;
  932.             for (k = slavePtr->tempX; k < px; k++)
  933.             weight += r->weightX[k];
  934.             for (k = slavePtr->tempX; weight > MINWEIGHT; k++) {
  935.             double wt = r->weightX[k];
  936.             int dx = (int)((wt * ((double)pixels_diff)) / weight);
  937.             r->minWidth[k] += dx;
  938.             pixels_diff -= dx;
  939.             weight -= wt;
  940.             }
  941.             /* Any leftovers go into the rightmost cell */
  942.             r->minWidth[px-1] += pixels_diff;
  943.         }
  944.         }
  945.         else if (slavePtr->tempWidth > i && slavePtr->tempWidth < nextSize)
  946.         nextSize = slavePtr->tempWidth;
  947.  
  948.  
  949.         if (slavePtr->tempHeight == i) {
  950.         py = slavePtr->tempY + slavePtr->tempHeight; /* bottom row */
  951.  
  952.         /* 
  953.          * Figure out if we should use this slave\'s weight.  If the weight
  954.          * is less than the total weight spanned by the height of the cell,
  955.          * then discard the weight.  Otherwise split it the difference
  956.          * according to the existing weights.
  957.          */
  958.  
  959.         weight_diff = slavePtr->weightY;
  960.         for (k = slavePtr->tempY; k < py; k++)
  961.             weight_diff -= r->weightY[k];
  962.         if (weight_diff > 0.0) {
  963.             weight = 0.0;
  964.             for (k = slavePtr->tempY; k < py; k++)
  965.             weight += r->weightY[k];
  966.             for (k = slavePtr->tempY; weight > MINWEIGHT; k++) {
  967.             double wt = r->weightY[k];
  968.             double dy = (wt * weight_diff) / weight;
  969.             r->weightY[k] += dy;
  970.             weight_diff -= dy;
  971.             weight -= wt;
  972.             }
  973.             /* Assign the remainder to the bottom cell */
  974.             r->weightY[py-1] += weight_diff;
  975.         }
  976.  
  977.         /*
  978.          * Calculate the minHeight array values.
  979.          * First, figure out how tall the current slave needs to be.
  980.          * Then, see if it will fit within the current minHeight values.
  981.          * If it won\'t fit, add the difference according to the weightY array.
  982.          */
  983.  
  984.         pixels_diff = slavePtr->minHeight + slavePtr->padY + slavePtr->iPadY;
  985.         for (k = slavePtr->tempY; k < py; k++)
  986.             pixels_diff -= r->minHeight[k];
  987.         if (pixels_diff > 0) {
  988.             weight = 0.0;
  989.             for (k = slavePtr->tempY; k < py; k++)
  990.             weight += r->weightY[k];
  991.             for (k = slavePtr->tempY; weight > MINWEIGHT; k++) {
  992.             double wt = r->weightY[k];
  993.             int dy = (int)((wt * ((double)pixels_diff)) / weight);
  994.             r->minHeight[k] += dy;
  995.             pixels_diff -= dy;
  996.             weight -= wt;
  997.             }
  998.             /* Any leftovers go into the bottom cell */
  999.             r->minHeight[py-1] += pixels_diff;
  1000.         }
  1001.         }
  1002.         else if (slavePtr->tempHeight > i && slavePtr->tempHeight < nextSize)
  1003.         nextSize = slavePtr->tempHeight;
  1004.     }
  1005.     }
  1006.  
  1007.     /*
  1008.      * Apply minimum row/column dimensions
  1009.      */
  1010.     for (i=0; i<masterPtr->column.used; i++) {
  1011.     if (r->minWidth[i] < masterPtr->column.minsize[i])
  1012.         r->minWidth[i] = masterPtr->column.minsize[i];
  1013.     if (r->weightX[i] < masterPtr->column.weight[i])
  1014.         r->weightX[i] = masterPtr->column.weight[i];
  1015.     }
  1016.     for (i=0; i<masterPtr->row.used; i++) {
  1017.     if (r->minHeight[i] < masterPtr->row.minsize[i])
  1018.         r->minHeight[i] = masterPtr->row.minsize[i];
  1019.     if (r->weightY[i] < masterPtr->row.weight[i])
  1020.         r->weightY[i] = masterPtr->row.weight[i];
  1021.     }
  1022. }
  1023.  
  1024. /*
  1025.  * Cache the layout info after it is calculated.
  1026.  */
  1027. static void
  1028. GetCachedLayoutInfo(masterPtr)
  1029.     GridBag *masterPtr;
  1030. {
  1031.     if (masterPtr->valid == 0) {
  1032.     if (!masterPtr->layoutCache)
  1033.         masterPtr->layoutCache = (LayoutInfo *)ckalloc(sizeof(LayoutInfo));
  1034.  
  1035.     GetLayoutInfo(masterPtr, masterPtr->layoutCache);
  1036.     masterPtr->valid = 1;
  1037.     }
  1038. }
  1039.  
  1040. /*
  1041.  * Adjusts the x, y, width, and height fields to the correct
  1042.  * values depending on the constraint geometry and pads.
  1043.  */
  1044.  
  1045. static void
  1046. AdjustForGravity(gridPtr, x, y, width, height)
  1047.     GridBag *gridPtr;
  1048.     int *x;
  1049.     int *y;
  1050.     int *width;
  1051.     int *height;
  1052. {
  1053.     int diffx=0, diffy=0;
  1054.     int sticky = gridPtr->flags&STICK_ALL;
  1055.  
  1056.     *x += gridPtr->padX/2;
  1057.     *width -= gridPtr->padX;
  1058.     *y += gridPtr->padY/2;
  1059.     *height -= gridPtr->padY;
  1060.  
  1061.     if (*width > (gridPtr->minWidth + gridPtr->iPadX)) {
  1062.     diffx = *width - (gridPtr->minWidth + gridPtr->iPadX);
  1063.     *width = gridPtr->minWidth + gridPtr->iPadX;
  1064.     }
  1065.  
  1066.     if (*height > (gridPtr->minHeight + gridPtr->iPadY)) {
  1067.     diffy = *height - (gridPtr->minHeight + gridPtr->iPadY);
  1068.     *height = gridPtr->minHeight + gridPtr->iPadY;
  1069.     }
  1070.  
  1071.     if (sticky&STICK_EAST && sticky&STICK_WEST)
  1072.     *width += diffx;
  1073.     if (sticky&STICK_NORTH && sticky&STICK_SOUTH)
  1074.     *height += diffy;
  1075.     if (!(sticky&STICK_WEST)) {
  1076.     if (sticky&STICK_EAST)
  1077.         *x += diffx;
  1078.     else
  1079.         *x += diffx/2;
  1080.     }
  1081.     if (!(sticky&STICK_NORTH)) {
  1082.     if (sticky&STICK_SOUTH)
  1083.         *y += diffy;
  1084.     else
  1085.         *y += diffy/2;
  1086.     }
  1087. }
  1088.  
  1089. /*
  1090.  * Figure out the minimum size (not counting the X border) of the
  1091.  * master based on the information from GetLayoutInfo()
  1092.  */
  1093.  
  1094. static void
  1095. GetMinSize(masterPtr, info, minw, minh)
  1096.     GridBag *masterPtr;
  1097.     LayoutInfo *info;
  1098.     int *minw;
  1099.     int *minh;
  1100. {
  1101.     int i, t;
  1102.     int intBWidth;    /* Width of internal border in parent window,
  1103.              * if any. */
  1104.  
  1105.     intBWidth = Tk_InternalBorderWidth(masterPtr->tkwin);
  1106.  
  1107.     t = 0;
  1108.     for(i = 0; i < info->width; i++)
  1109.     t += info->minWidth[i];
  1110.     *minw = t + 2*intBWidth;
  1111.  
  1112.     t = 0;
  1113.     for(i = 0; i < info->height; i++)
  1114.     t += info->minHeight[i];
  1115.     *minh = t + 2*intBWidth;
  1116. }
  1117.  
  1118. /*
  1119.  *--------------------------------------------------------------
  1120.  *
  1121.  * ArrangeGrid --
  1122.  *
  1123.  *    This procedure is invoked (using the Tk_DoWhenIdle
  1124.  *    mechanism) to re-layout a set of windows managed by
  1125.  *    the gridbag.  It is invoked at idle time so that a
  1126.  *    series of gridbag requests can be merged into a single
  1127.  *    layout operation.
  1128.  *
  1129.  * Results:
  1130.  *    None.
  1131.  *
  1132.  * Side effects:
  1133.  *    The slaves of masterPtr may get resized or moved.
  1134.  *
  1135.  *--------------------------------------------------------------
  1136.  */
  1137.  
  1138. static void
  1139. ArrangeGrid(clientData)
  1140.     ClientData clientData;    /* Structure describing parent whose slaves
  1141.                  * are to be re-layed out. */
  1142. {
  1143.     register GridBag *masterPtr = (GridBag *) clientData;
  1144.     register GridBag *slavePtr;    
  1145.     int abort;
  1146.     int i, x, y, width, height;
  1147.     int diffw, diffh;
  1148.     double weight;
  1149.     Tk_Window parent, ancestor;
  1150.     LayoutInfo info;
  1151.     int intBWidth;    /* Width of internal border in parent window,
  1152.              * if any. */
  1153.     int iPadX, iPadY;
  1154.  
  1155.     masterPtr->flags &= ~REQUESTED_RELAYOUT;
  1156.  
  1157.     /*
  1158.      * If the parent has no slaves anymore, then don't do anything
  1159.      * at all:  just leave the parent's size as-is.
  1160.      * Even if row and column constraints have been set!
  1161.      */
  1162.  
  1163.     if (masterPtr->slavePtr == NULL) {
  1164.     return;
  1165.     }
  1166.  
  1167.     /*
  1168.      * Abort any nested call to ArrangeGrid for this window, since
  1169.      * we'll do everything necessary here, and set up so this call
  1170.      * can be aborted if necessary.  
  1171.      */
  1172.  
  1173.     if (masterPtr->abortPtr != NULL) {
  1174.     *masterPtr->abortPtr = 1;
  1175.     }
  1176.     masterPtr->abortPtr = &abort;
  1177.     abort = 0;
  1178.     Tk_Preserve((ClientData) masterPtr);
  1179.  
  1180.     /*
  1181.      * Pass #1: scan all the slaves to figure out the total amount
  1182.      * of space needed.
  1183.      */
  1184.  
  1185.     GetLayoutInfo(masterPtr, &info);
  1186.     GetMinSize(masterPtr, &info, &width, &height);
  1187.  
  1188.     if (((width != Tk_ReqWidth(masterPtr->tkwin))
  1189.         || (height != Tk_ReqHeight(masterPtr->tkwin)))
  1190.         && !(masterPtr->flags & DONT_PROPAGATE)) {
  1191.     Tk_GeometryRequest(masterPtr->tkwin, width, height);
  1192.     masterPtr->flags |= REQUESTED_RELAYOUT;
  1193.     masterPtr->valid = 0;
  1194.     Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
  1195.     goto done;
  1196.     }
  1197.  
  1198.     /*
  1199.      * If the parent isn't mapped then don't do anything more:  wait
  1200.      * until it gets mapped again.  Need to get at least to here to
  1201.      * reflect size needs up the window hierarchy, but there's no
  1202.      * point in actually mapping the slaves.
  1203.      */
  1204.  
  1205.     if (!Tk_IsMapped(masterPtr->tkwin)) {
  1206.     goto done;
  1207.     }
  1208.  
  1209.  
  1210.     /*
  1211.      * If the current dimensions of the window don't match the desired
  1212.      * dimensions, then adjust the minWidth and minHeight arrays
  1213.      * according to the weights.
  1214.      */
  1215.  
  1216.     diffw = Tk_Width(masterPtr->tkwin) - (width + masterPtr->iPadX);
  1217.     if (diffw != 0) {
  1218.     weight = 0.0;
  1219.     for (i = 0; i < info.width; i++)
  1220.         weight += info.weightX[i];
  1221.     if (weight > MINWEIGHT) {
  1222.         for (i = 0; i < info.width; i++) {
  1223.         int dx = (int)(( ((double)diffw) * info.weightX[i]) / weight);
  1224.         info.minWidth[i] += dx;
  1225.         width += dx;
  1226.         if (info.minWidth[i] < 0) {
  1227.             width -= info.minWidth[i];
  1228.             info.minWidth[i] = 0;
  1229.         }
  1230.         }
  1231.     }
  1232.     diffw = Tk_Width(masterPtr->tkwin) - (width + masterPtr->iPadX);
  1233.     }
  1234.     else {
  1235.     diffw = 0;
  1236.     }
  1237.  
  1238.     diffh = Tk_Height(masterPtr->tkwin) - (height + masterPtr->iPadY);
  1239.     if (diffh != 0) {
  1240.     weight = 0.0;
  1241.     for (i = 0; i < info.height; i++)
  1242.         weight += info.weightY[i];
  1243.     if (weight > MINWEIGHT) {
  1244.         for (i = 0; i < info.height; i++) {
  1245.         int dy = (int)(( ((double)diffh) * info.weightY[i]) / weight);
  1246.         info.minHeight[i] += dy;
  1247.         height += dy;
  1248.         if (info.minHeight[i] < 0) {
  1249.             height -= info.minHeight[i];
  1250.             info.minHeight[i] = 0;
  1251.         }
  1252.         }
  1253.     }
  1254.     diffh = Tk_Height(masterPtr->tkwin) - (height + masterPtr->iPadY);
  1255.     }
  1256.     else {
  1257.     diffh = 0;
  1258.     }
  1259.  
  1260.     /*
  1261.      * Now do the actual layout of the slaves using the layout information
  1262.      * that has been collected.
  1263.      */
  1264.  
  1265.     iPadX = masterPtr->iPadX/2;
  1266.     iPadY = masterPtr->iPadY/2;
  1267.     intBWidth = Tk_InternalBorderWidth(masterPtr->tkwin);
  1268.  
  1269.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  1270.                      slavePtr = slavePtr->nextPtr) {
  1271.  
  1272.     masterPtr->startx = x = diffw/2 + intBWidth + iPadX;
  1273.     for(i = 0; i < slavePtr->tempX; i++)
  1274.         x += info.minWidth[i];
  1275.  
  1276.     masterPtr->starty = y = diffh/2 + intBWidth + iPadY;
  1277.     for(i = 0; i < slavePtr->tempY; i++)
  1278.         y += info.minHeight[i];
  1279.  
  1280.     width = 0;
  1281.     for(i = slavePtr->tempX; i < (slavePtr->tempX + slavePtr->tempWidth); i++)
  1282.         width += info.minWidth[i];
  1283.  
  1284.     height = 0;
  1285.     for(i = slavePtr->tempY; i < (slavePtr->tempY + slavePtr->tempHeight); i++)
  1286.         height += info.minHeight[i];
  1287.  
  1288.     AdjustForGravity(slavePtr, &x, &y, &width, &height);
  1289.  
  1290.     /*
  1291.      * If the window in which slavePtr is managed is not its
  1292.      * parent in the window hierarchy, translate the coordinates
  1293.      * to the coordinate system of the real X parent.
  1294.      */
  1295.  
  1296.     parent = Tk_Parent(slavePtr->tkwin);
  1297.     for (ancestor = masterPtr->tkwin; ancestor != parent;
  1298.                       ancestor = Tk_Parent(ancestor)) {
  1299.         x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width;
  1300.         y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width;
  1301.     }
  1302.  
  1303.     /*
  1304.      * If the window is too small to be interesting then
  1305.      * unmap it.  Otherwise configure it and then make sure
  1306.      * it's mapped.
  1307.      */
  1308.  
  1309.     if ((width <= 0) || (height <= 0)) {
  1310.         Tk_UnmapWindow(slavePtr->tkwin);
  1311.     }
  1312.     else {
  1313.         if ((x != Tk_X(slavePtr->tkwin))
  1314.             || (y != Tk_Y(slavePtr->tkwin))
  1315.             || (width != Tk_Width(slavePtr->tkwin))
  1316.             || (height != Tk_Height(slavePtr->tkwin))) {
  1317.         Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height);
  1318.         }
  1319.         if (abort) {
  1320.         goto done;
  1321.         }
  1322.         Tk_MapWindow(slavePtr->tkwin);
  1323.     }
  1324.  
  1325.     /*
  1326.      * Changes to the window's structure could cause almost anything
  1327.      * to happen, including deleting the parent or child.  If this
  1328.      * happens, we'll be told to abort.
  1329.      */
  1330.  
  1331.     if (abort) {
  1332.         goto done;
  1333.     }
  1334.     }
  1335.  
  1336.     done:
  1337.     masterPtr->abortPtr = NULL;
  1338.     Tk_Release((ClientData) masterPtr);
  1339. }
  1340.  
  1341.  
  1342.  
  1343. /*
  1344.  *--------------------------------------------------------------
  1345.  *
  1346.  * GetGridBag --
  1347.  *
  1348.  *    This internal procedure is used to locate a GridBag
  1349.  *    structure for a given window, creating one if one
  1350.  *    doesn't exist already.
  1351.  *
  1352.  * Results:
  1353.  *    The return value is a pointer to the GridBag structure
  1354.  *    corresponding to tkwin.
  1355.  *
  1356.  * Side effects:
  1357.  *    A new gridbag structure may be created.  If so, then
  1358.  *    a callback is set up to clean things up when the
  1359.  *    window is deleted.
  1360.  *
  1361.  *--------------------------------------------------------------
  1362.  */
  1363.  
  1364. static GridBag *
  1365. GetGridBag(tkwin)
  1366.     Tk_Window tkwin;        /* Token for window for which
  1367.                  * gridbag structure is desired. */
  1368. {
  1369.     register GridBag *gridPtr;
  1370.     Tcl_HashEntry *hPtr;
  1371.     int new;
  1372.  
  1373.     if (!initialized) {
  1374.     initialized = 1;
  1375.     Tcl_InitHashTable(&gridBagHashTable, TCL_ONE_WORD_KEYS);
  1376.     }
  1377.  
  1378.     /*
  1379.      * See if there's already gridbag for this window.  If not,
  1380.      * then create a new one.
  1381.      */
  1382.  
  1383.     hPtr = Tcl_CreateHashEntry(&gridBagHashTable, (char *) tkwin, &new);
  1384.     if (!new) {
  1385.     return (GridBag *) Tcl_GetHashValue(hPtr);
  1386.     }
  1387.     gridPtr = (GridBag *) ckalloc(sizeof(GridBag));
  1388.     gridPtr->tkwin = tkwin;
  1389.     gridPtr->masterPtr = NULL;
  1390.     gridPtr->nextPtr = NULL;
  1391.     gridPtr->slavePtr = NULL;
  1392.  
  1393.     gridPtr->gridColumn = gridPtr->gridRow = -1;
  1394.     gridPtr->gridWidth = gridPtr->gridHeight = 1;
  1395.     gridPtr->weightX = gridPtr->weightY = 0.0;
  1396.     gridPtr->minWidth = gridPtr->minHeight = 0;
  1397.  
  1398.     gridPtr->padX = gridPtr->padY = 0;
  1399.     gridPtr->iPadX = gridPtr->iPadY = 0;
  1400.     gridPtr->startx = gridPtr->starty = 0;
  1401.     gridPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
  1402.     gridPtr->abortPtr = NULL;
  1403.     gridPtr->flags = 0;
  1404.  
  1405.     gridPtr->column.max = 0;
  1406.     gridPtr->row.max = 0;
  1407.     gridPtr->column.used = 0;
  1408.     gridPtr->row.used = 0;
  1409.  
  1410.     gridPtr->valid = 0;
  1411.     gridPtr->layoutCache = NULL;
  1412.  
  1413.     Tcl_SetHashValue(hPtr, gridPtr);
  1414.     Tk_CreateEventHandler(tkwin, StructureNotifyMask,
  1415.         GridBagStructureProc, (ClientData) gridPtr);
  1416.     return gridPtr;
  1417. }
  1418.  
  1419. /*
  1420.  *----------------------------------------------------------------------
  1421.  *
  1422.  * Unlink --
  1423.  *
  1424.  *    Remove a gridbag from its parent's list of slaves.
  1425.  *
  1426.  * Results:
  1427.  *    None.
  1428.  *
  1429.  * Side effects:
  1430.  *    The parent will be scheduled for re-arranging.
  1431.  *
  1432.  *----------------------------------------------------------------------
  1433.  */
  1434.  
  1435. static void
  1436. Unlink(gridPtr)
  1437.     register GridBag *gridPtr;        /* Window to unlink. */
  1438. {
  1439.     register GridBag *masterPtr, *gridPtr2;
  1440.  
  1441.     masterPtr = gridPtr->masterPtr;
  1442.     if (masterPtr == NULL) {
  1443.     return;
  1444.     }
  1445.     if (masterPtr->slavePtr == gridPtr) {
  1446.     masterPtr->slavePtr = gridPtr->nextPtr;
  1447.     }
  1448.     else {
  1449.     for (gridPtr2 = masterPtr->slavePtr; ; gridPtr2 = gridPtr2->nextPtr) {
  1450.         if (gridPtr2 == NULL) {
  1451.         panic("Unlink couldn't find previous window");
  1452.         }
  1453.         if (gridPtr2->nextPtr == gridPtr) {
  1454.         gridPtr2->nextPtr = gridPtr->nextPtr;
  1455.         break;
  1456.         }
  1457.     }
  1458.     }
  1459.     masterPtr->valid = 0;
  1460.     if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
  1461.     masterPtr->flags |= REQUESTED_RELAYOUT;
  1462.     Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
  1463.     }
  1464.     if (masterPtr->abortPtr != NULL) {
  1465.     *masterPtr->abortPtr = 1;
  1466.     }
  1467.  
  1468.     gridPtr->masterPtr = NULL;
  1469. }
  1470.  
  1471.  
  1472.  
  1473. /*
  1474.  *----------------------------------------------------------------------
  1475.  *
  1476.  * DestroyGridBag --
  1477.  *
  1478.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  1479.  *    to clean up the internal structure of a gridbag at a safe time
  1480.  *    (when no-one is using it anymore).
  1481.  *
  1482.  * Results:
  1483.  *    None.
  1484.  *
  1485.  * Side effects:
  1486.  *    Everything associated with the gridbag is freed up.
  1487.  *
  1488.  *----------------------------------------------------------------------
  1489.  */
  1490.  
  1491. static void
  1492. DestroyGridBag(memPtr)
  1493.     char *memPtr;        /* Info about window that is now dead. */
  1494. {
  1495.     register GridBag *gridPtr = (GridBag *) memPtr;
  1496.  
  1497.     if (gridPtr->column.max) {
  1498.     ckfree((char *) gridPtr->column.minsize);
  1499.     ckfree((char *) gridPtr->column.weight);
  1500.     }
  1501.     if (gridPtr->row.max) {
  1502.     ckfree((char *) gridPtr->row.minsize);
  1503.     ckfree((char *) gridPtr->row.weight);
  1504.     }
  1505.     if (gridPtr->layoutCache)
  1506.     ckfree((char *) gridPtr->layoutCache);
  1507.  
  1508.     ckfree((char *) gridPtr);
  1509. }
  1510.  
  1511. /*
  1512.  *----------------------------------------------------------------------
  1513.  *
  1514.  * GridBagStructureProc --
  1515.  *
  1516.  *    This procedure is invoked by the Tk event dispatcher in response
  1517.  *    to StructureNotify events.
  1518.  *
  1519.  * Results:
  1520.  *    None.
  1521.  *
  1522.  * Side effects:
  1523.  *    If a window was just deleted, clean up all its gridbag-related
  1524.  *    information.  If it was just resized, re-configure its slaves, if
  1525.  *    any.
  1526.  *
  1527.  *----------------------------------------------------------------------
  1528.  */
  1529.  
  1530. static void
  1531. GridBagStructureProc(clientData, eventPtr)
  1532.     ClientData clientData;        /* Our information about window
  1533.                      * referred to by eventPtr. */
  1534.     XEvent *eventPtr;            /* Describes what just happened. */
  1535. {
  1536.     register GridBag *gridPtr = (GridBag *) clientData;
  1537.  
  1538.     if (eventPtr->type == ConfigureNotify) {
  1539.     gridPtr->valid = 0;
  1540.     if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
  1541.         gridPtr->flags |= REQUESTED_RELAYOUT;
  1542.         Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
  1543.     }
  1544.     if (gridPtr->doubleBw != 2*Tk_Changes(gridPtr->tkwin)->border_width) {
  1545.         if ((gridPtr->masterPtr != NULL) &&
  1546.             !(gridPtr->masterPtr->flags & REQUESTED_RELAYOUT)) {
  1547.         gridPtr->doubleBw = 2*Tk_Changes(gridPtr->tkwin)->border_width;
  1548.         gridPtr->masterPtr->flags |= REQUESTED_RELAYOUT;
  1549.         Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr->masterPtr);
  1550.         }
  1551.     }
  1552.     }
  1553.     else if (eventPtr->type == DestroyNotify) {
  1554.     register GridBag *gridPtr2, *nextPtr;
  1555.  
  1556.     if (gridPtr->masterPtr != NULL) {
  1557.         Unlink(gridPtr);
  1558.     }
  1559.     for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
  1560.                        gridPtr2 = nextPtr) {
  1561.         Tk_UnmapWindow(gridPtr2->tkwin);
  1562.         gridPtr2->masterPtr = NULL;
  1563.         nextPtr = gridPtr2->nextPtr;
  1564.         gridPtr2->nextPtr = NULL;
  1565.     }
  1566.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&gridBagHashTable,
  1567.         (char *) gridPtr->tkwin));
  1568.     if (gridPtr->flags & REQUESTED_RELAYOUT) {
  1569.         Tk_CancelIdleCall(ArrangeGrid, (ClientData) gridPtr);
  1570.     }
  1571.     gridPtr->tkwin = NULL;
  1572.     Tk_EventuallyFree((ClientData) gridPtr, DestroyGridBag);
  1573.     }
  1574.     else if (eventPtr->type == MapNotify) {
  1575.     gridPtr->valid = 0;
  1576.     if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
  1577.         gridPtr->flags |= REQUESTED_RELAYOUT;
  1578.         Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
  1579.     }
  1580.     }
  1581.     else if (eventPtr->type == UnmapNotify) {
  1582.     register GridBag *gridPtr2;
  1583.  
  1584.     for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
  1585.                        gridPtr2 = gridPtr2->nextPtr) {
  1586.         Tk_UnmapWindow(gridPtr2->tkwin);
  1587.     }
  1588.     }
  1589. }
  1590.  
  1591.  
  1592.  
  1593. /*
  1594.  *----------------------------------------------------------------------
  1595.  *
  1596.  * ConfigureSlaves --
  1597.  *
  1598.  *    This implements the guts of the "grid configure" command.  Given
  1599.  *    a list of slaves and configuration options, it arranges for the
  1600.  *    gridbag to manage the slaves and sets the specified options.
  1601.  *
  1602.  * Results:
  1603.  *    TCL_OK is returned if all went well.  Otherwise, TCL_ERROR is
  1604.  *    returned and interp->result is set to contain an error message.
  1605.  *
  1606.  * Side effects:
  1607.  *    Slave windows get taken over by the gridbag.
  1608.  *
  1609.  *----------------------------------------------------------------------
  1610.  */
  1611.  
  1612. static int
  1613. ConfigureSlaves(interp, tkwin, argc, argv)
  1614.     Tcl_Interp *interp;    /* Interpreter for error reporting. */
  1615.     Tk_Window tkwin;        /* Any window in application containing
  1616.                  * slaves.  Used to look up slave names. */
  1617.     int argc;            /* Numb = 0er of elements in argv. */
  1618.     char *argv[];        /* Argument strings:  contains one or more
  1619.                  * window names followed by any number
  1620.                  * of "option value" pairs.  Caller must
  1621.                  * make sure that there is at least one
  1622.                  * window name. */
  1623. {
  1624.     GridBag *masterPtr, *slavePtr, *prevPtr;
  1625.     Tk_Window other, slave, parent, ancestor;
  1626.     int i, j, numWindows, c, length, tmp, positionGiven;
  1627.     int currentColumn=0, numColumns=1;
  1628.     int gotLayout = 0;
  1629.     int gotWidth = 0;
  1630.     int width;
  1631.  
  1632.     /*
  1633.      * Find out how many windows are specified. (shouldn't use harwired symbols)
  1634.      */
  1635.  
  1636.     for (numWindows = 0; numWindows < argc; numWindows++) {
  1637.     if (argv[numWindows][0] != '.'
  1638.          && strcmp(argv[numWindows],"-")!=0
  1639.          && strcmp(argv[numWindows],"^")!=0
  1640.          && strcmp(argv[numWindows],"x")!=0) {
  1641.         break;
  1642.     }
  1643.     }
  1644.     slave = NULL;
  1645.  
  1646.     /*
  1647.      * Iterate over all of the slave windows, parsing the configuration
  1648.      * options for each slave.  It's a bit wasteful to re-parse the
  1649.      * options for each slave, but things get too messy if we try to
  1650.      * parse the arguments just once at the beginning.  For example,
  1651.      * if a slave already is managed we want to just change a few
  1652.      * existing values without resetting everything.  If there are
  1653.      * multiple windows, the -in option only gets processed for the
  1654.      * first window.
  1655.      */
  1656.  
  1657.     masterPtr = NULL;
  1658.     prevPtr = NULL;
  1659.     positionGiven = 0;
  1660.     for (j = 0; j < numWindows; j++) {
  1661.  
  1662.     /* adjust default widget location for non-widgets */
  1663.     if (*argv[j] != '.') {
  1664.         switch (*argv[j]) {
  1665.         case '^':    /* extend the widget in the previous row 
  1666.                  * Since we don't know who the master is yet,
  1667.                  * handle these in a separate pass at the end
  1668.                  */
  1669.             /* no break */
  1670.         case REL_SKIP:    /* skip over the next column */
  1671.             currentColumn++;
  1672.             break;
  1673.         case REL_HORIZ:    /* increase the span, already dealt with */
  1674.             /* not quite right */
  1675.             if (j>0 && (*argv[j-1] == REL_SKIP || *argv[j-1] == '^')) {
  1676.             Tcl_AppendResult(interp, "Invalid grid combination:",
  1677.                 " \"-\" can't follow \"", argv[j-1], "\"",NULL);
  1678.             return TCL_ERROR;
  1679.             }
  1680.             break;
  1681.         default:
  1682.             panic("Invalid grid position indicator");
  1683.         }
  1684.         continue;
  1685.     }
  1686.  
  1687.     for (numColumns=1; j+numColumns < numWindows && *argv[j+numColumns] == REL_HORIZ;
  1688.         numColumns++) {
  1689.         /* null body */
  1690.     }
  1691.     slave = Tk_NameToWindow(interp, argv[j], tkwin);
  1692.     if (slave == NULL) {
  1693.         return TCL_ERROR;
  1694.     }
  1695.     if (Tk_IsTopLevel(slave)) {
  1696.         Tcl_AppendResult(interp, "can't manage \"", argv[j],
  1697.             "\": it's a top-level window", (char *) NULL);
  1698.         return TCL_ERROR;
  1699.     }
  1700.     slavePtr = GetGridBag(slave);
  1701.  
  1702.     /*
  1703.      * The following statement is taken from tkPack.c:
  1704.      *
  1705.      * "If the slave isn't currently managed, reset all of its
  1706.      * configuration information to default values (there could
  1707.      * be old values left from a previous packer)."
  1708.      *
  1709.      * I disagree with this statement.  If a slave is disabled (using
  1710.      * "forget") and then re-enabled, I submit that 90% of the time the
  1711.      * programmer will want it to retain its old configuration information.
  1712.      * If the programmer doesn't want this behavior, then she can reset the
  1713.      * defaults for herself, but she will never have to worry about keeping
  1714.      * track of the old state. 
  1715.      */
  1716.  
  1717.     for (i = numWindows; i < argc; i+=2) {
  1718.         if ((i+2) > argc) {
  1719.         Tcl_AppendResult(interp, "extra option \"", argv[i],
  1720.             "\" (option with no value?)", (char *) NULL);
  1721.         return TCL_ERROR;
  1722.         }
  1723.         length = strlen(argv[i]);
  1724.         if (length < 2) {
  1725.         goto badOption;
  1726.         }
  1727.         c = argv[i][1];
  1728.         if ((c == 'i') && (strcmp(argv[i], "-in") == 0)) {
  1729.         if (j == 0) {
  1730.             other = Tk_NameToWindow(interp, argv[i+1], tkwin);
  1731.             if (other == NULL) {
  1732.             return TCL_ERROR;
  1733.             }
  1734.             if (other == slave) {
  1735.             sprintf(interp->result,"Window can't be managed in itself");
  1736.             return TCL_ERROR;
  1737.             }
  1738.             masterPtr = GetGridBag(other);
  1739.             prevPtr = masterPtr->slavePtr;
  1740.             if (prevPtr != NULL) {
  1741.             while (prevPtr->nextPtr != NULL) {
  1742.                 prevPtr = prevPtr->nextPtr;
  1743.             }
  1744.             }
  1745.             positionGiven = 1;
  1746.         }
  1747.         } else if ((c == 'i') && (strcmp(argv[i], "-ipadx") == 0)) {
  1748.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  1749.             || (tmp < 0)) {
  1750.             Tcl_ResetResult(interp);
  1751.             Tcl_AppendResult(interp, "bad ipadx value \"", argv[i+1],
  1752.                 "\": must be positive screen distance",
  1753.                 (char *) NULL);
  1754.             return TCL_ERROR;
  1755.         }
  1756.         slavePtr->iPadX = tmp*2;
  1757.         } else if ((c == 'i') && (strcmp(argv[i], "-ipady") == 0)) {
  1758.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  1759.             || (tmp< 0)) {
  1760.             Tcl_ResetResult(interp);
  1761.             Tcl_AppendResult(interp, "bad ipady value \"", argv[i+1],
  1762.                 "\": must be positive screen distance",
  1763.                 (char *) NULL);
  1764.             return TCL_ERROR;
  1765.         }
  1766.         slavePtr->iPadY = tmp*2;
  1767.         } else if ((c == 'p') && (strcmp(argv[i], "-padx") == 0)) {
  1768.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  1769.             || (tmp< 0)) {
  1770.             Tcl_ResetResult(interp);
  1771.             Tcl_AppendResult(interp, "bad padx value \"", argv[i+1],
  1772.                 "\": must be positive screen distance",
  1773.                 (char *) NULL);
  1774.             return TCL_ERROR;
  1775.         }
  1776.         slavePtr->padX = tmp*2;
  1777.         } else if ((c == 'p') && (strcmp(argv[i], "-pady") == 0)) {
  1778.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  1779.             || (tmp< 0)) {
  1780.             Tcl_ResetResult(interp);
  1781.             Tcl_AppendResult(interp, "bad pady value \"", argv[i+1],
  1782.                 "\": must be positive screen distance",
  1783.                 (char *) NULL);
  1784.             return TCL_ERROR;
  1785.         }
  1786.         slavePtr->padY = tmp*2;
  1787.         } else if ((c == 'c') && (strcmp(argv[i], "-column") == 0)) {
  1788.         if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
  1789.             Tcl_ResetResult(interp);
  1790.             Tcl_AppendResult(interp, "bad column value \"", argv[i+1],
  1791.                 "\": must be a non-negative integer", (char *)NULL);
  1792.             return TCL_ERROR;
  1793.         }
  1794.         slavePtr->gridColumn = tmp;
  1795.         } else if ((c == 'r') && (strcmp(argv[i], "-row") == 0)) {
  1796.         if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
  1797.             Tcl_ResetResult(interp);
  1798.             Tcl_AppendResult(interp, "bad grid value \"", argv[i+1],
  1799.                 "\": must be a non-negative integer", (char *)NULL);
  1800.             return TCL_ERROR;
  1801.         }
  1802.         slavePtr->gridRow = tmp;
  1803.         } else if ((c == 'c') && (strcmp(argv[i], "-columnspan") == 0)) {
  1804.         if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp <= 0) {
  1805.             Tcl_ResetResult(interp);
  1806.             Tcl_AppendResult(interp, "bad columnspan value \"", argv[i+1],
  1807.                 "\": must be a positive integer", (char *)NULL);
  1808.             return TCL_ERROR;
  1809.         }
  1810.         slavePtr->gridWidth = tmp;
  1811.         gotWidth++;
  1812.         } else if ((c == 'r') && (strcmp(argv[i], "-rowspan") == 0)) {
  1813.         if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK) {
  1814.             Tcl_ResetResult(interp);
  1815.             Tcl_AppendResult(interp, "bad rowspan value \"", argv[i+1],
  1816.                 "\": must be a positive integer", (char *)NULL);
  1817.             return TCL_ERROR;
  1818.         }
  1819.         slavePtr->gridHeight = tmp;
  1820. /*
  1821.         } else if ((c == 'w') &&
  1822.             (!strcmp(argv[i], "-weightx") || !strcmp(argv[i], "-wx"))) {
  1823.         if (Tcl_GetDouble(interp, argv[i+1], &tmp_dbl) != TCL_OK) {
  1824.             Tcl_ResetResult(interp);
  1825.             Tcl_AppendResult(interp, "bad weight value \"", argv[i+1],
  1826.                 "\": must be a double", (char *)NULL);
  1827.             return TCL_ERROR;
  1828.         }
  1829.         slavePtr->weightX = tmp_dbl;
  1830.         }
  1831.         else if ((c == 'w') &&
  1832.             (!strcmp(argv[i], "-weighty") || !strcmp(argv[i], "-wy"))) {
  1833.         if (Tcl_GetDouble(interp, argv[i+1], &tmp_dbl) != TCL_OK) {
  1834.             Tcl_ResetResult(interp);
  1835.             Tcl_AppendResult(interp, "bad weight value \"", argv[i+1],
  1836.                 "\": must be a double", (char *)NULL);
  1837.             return TCL_ERROR;
  1838.         }
  1839.         slavePtr->weightY = tmp_dbl;
  1840. */
  1841.         } else if ((c == 's') && strcmp(argv[i], "-sticky") == 0) {
  1842.         int sticky = StringToSticky(argv[i+1]);
  1843.         if (sticky == -1) {
  1844.             Tcl_AppendResult(interp, "bad stickyness value \"", argv[i+1],
  1845.                 "\": must be a string containing n, e, s, and/or w", (char *)NULL);
  1846.             return TCL_ERROR;
  1847.         }
  1848.         slavePtr->flags = sticky | (slavePtr->flags & ~STICK_ALL);
  1849.         } else {
  1850.         badOption:
  1851.         Tcl_AppendResult(interp, "unknown or ambiguous option \"",
  1852.             argv[i], "\": must be -in, -sticky, ",
  1853.             "-row, -column, -rowspan, -columnspan, ",
  1854.             "-ipadx, -ipady, -padx or -pady.",
  1855.             (char *) NULL);
  1856.         return TCL_ERROR;
  1857.         }
  1858.     }
  1859.  
  1860.     /*
  1861.      * If no position in a gridbag list was specified and the slave
  1862.      * is already managed, then leave it in its current location in
  1863.      * its current gridbag list.
  1864.      */
  1865.  
  1866.     if (!positionGiven && (slavePtr->masterPtr != NULL)) {
  1867.         masterPtr = slavePtr->masterPtr;
  1868.         goto scheduleLayout;
  1869.     }
  1870.  
  1871.     /*
  1872.      * If the slave is going to be put back after itself then
  1873.      * skip the whole operation, since it won't work anyway.
  1874.      */
  1875.  
  1876.     if (prevPtr == slavePtr) {
  1877.         masterPtr = slavePtr->masterPtr;
  1878.         goto scheduleLayout;
  1879.     }
  1880.     
  1881.     /*
  1882.      * If the "-in" option has not been specified, arrange for the
  1883.      * slave to go at the end of the order for its parent.
  1884.      */
  1885.     
  1886.     if (!positionGiven) {
  1887.         masterPtr = GetGridBag(Tk_Parent(slave));
  1888.         prevPtr = masterPtr->slavePtr;
  1889.         if (prevPtr != NULL) {
  1890.         while (prevPtr->nextPtr != NULL) {
  1891.             prevPtr = prevPtr->nextPtr;
  1892.         }
  1893.         }
  1894.     }
  1895.  
  1896.     /*
  1897.      * Make sure that the slave's parent is either the master or
  1898.      * an ancestor of the master.
  1899.      */
  1900.     
  1901.     parent = Tk_Parent(slave);
  1902.     for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
  1903.         if (ancestor == parent) {
  1904.         break;
  1905.         }
  1906.         if (Tk_IsTopLevel(ancestor)) {
  1907.         Tcl_AppendResult(interp, "can't put ", argv[j],
  1908.             " inside ", Tk_PathName(masterPtr->tkwin),
  1909.             (char *) NULL);
  1910.         return TCL_ERROR;
  1911.         }
  1912.     }
  1913.  
  1914.     /*
  1915.      * Unlink the slave if it's currently managed, then position it
  1916.      * after prevPtr.
  1917.      */
  1918.  
  1919.     if (slavePtr->masterPtr != NULL) {
  1920.         Unlink(slavePtr);
  1921.     }
  1922. /*MM* add { */
  1923.  
  1924.     /*
  1925.      * I moved this here from its original location below because I was
  1926.      * running into a sort of a deadly embrace where the master
  1927.      * requires the slave to have a row and column defined when
  1928.      * computing the layout.  Not sure if this is the right way to
  1929.      * fix it, but at this point the current slave is not under the
  1930.      * management of its master.
  1931.      */
  1932.     if (slavePtr->gridRow == -1) {
  1933.         if (!gotLayout++) GetCachedLayoutInfo(masterPtr);
  1934.         slavePtr->gridRow = masterPtr->layoutCache->lastRow;
  1935.     }
  1936.  
  1937. /*MM* } */
  1938.     slavePtr->masterPtr = masterPtr;
  1939.     if (prevPtr == NULL) {
  1940.         slavePtr->nextPtr = masterPtr->slavePtr;
  1941.         masterPtr->slavePtr = slavePtr;
  1942.     } else {
  1943.         slavePtr->nextPtr = prevPtr->nextPtr;
  1944.         prevPtr->nextPtr = slavePtr;
  1945.     }
  1946.     Tk_ManageGeometry(slave, &gridMgrType, (ClientData) slavePtr);
  1947.     prevPtr = slavePtr;
  1948.  
  1949.     /* assign default row and column */
  1950.  
  1951.     if (slavePtr->gridColumn == -1) {
  1952.         slavePtr->gridColumn = currentColumn;
  1953.     }
  1954.     slavePtr->gridWidth += numColumns - 1;
  1955. /*MM* del
  1956.     if (slavePtr->gridRow == -1) {
  1957.         if (!gotLayout++) GetCachedLayoutInfo(masterPtr);
  1958.         slavePtr->gridRow = masterPtr->layoutCache->lastRow;
  1959.     } 
  1960. */
  1961.  
  1962.     /*
  1963.      * Arrange for the parent to be re-arranged at the first
  1964.      * idle moment.
  1965.      */
  1966.  
  1967.     scheduleLayout:
  1968.     if (masterPtr->abortPtr != NULL) {
  1969.         *masterPtr->abortPtr = 1;
  1970.     }
  1971.     masterPtr->valid = 0;
  1972.     if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
  1973.         masterPtr->flags |= REQUESTED_RELAYOUT;
  1974.         Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
  1975.     }
  1976.     currentColumn += slavePtr->gridWidth;
  1977.     numColumns = 1;
  1978.     }
  1979.  
  1980.     /* now look for all the "^"'s */
  1981.  
  1982.     for (j = 0; j < numWindows; j++) {
  1983.     struct GridBag *otherPtr;
  1984.         char *lastWindow;    /* use this window to base current row/col on */
  1985.     int match;        /* found a match for the ^ */
  1986.  
  1987.         if (*argv[j] == '.') {
  1988.         lastWindow = argv[j];
  1989.     }
  1990.     if (*argv[j] != '^') {
  1991.         continue;
  1992.     }
  1993.     for (width=1; width+j < numWindows && *argv[j+width] == '^'; width++) {
  1994.         /* Null Body */
  1995.     }
  1996.     other = Tk_NameToWindow(interp, lastWindow, tkwin);
  1997.     otherPtr = GetGridBag(other);
  1998.     if (!gotLayout++) GetCachedLayoutInfo(masterPtr);
  1999.  
  2000.     for (match=0, slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  2001.                      slavePtr = slavePtr->nextPtr) {
  2002.  
  2003.         if (slavePtr->gridWidth == width
  2004.             && slavePtr->gridColumn == otherPtr->gridColumn + otherPtr->gridWidth
  2005.             && slavePtr->gridRow + slavePtr->gridHeight == otherPtr->gridRow) {
  2006.         slavePtr->gridHeight++;
  2007.         match++;
  2008.         }
  2009.         lastWindow = Tk_PathName(slavePtr->tkwin);
  2010.     }
  2011.     if (!match) {
  2012.         Tcl_AppendResult(interp, "can't find slave to extend with \"^\"",
  2013.             " after ",lastWindow,
  2014.             (char *) NULL);
  2015.         return TCL_ERROR;
  2016.     }
  2017.     j += width - 1;
  2018.     }
  2019.     return TCL_OK;
  2020. }
  2021.  
  2022. /* convert "Sticky" bits into a string */
  2023.  
  2024. static void
  2025. StickyToString(flags, result)
  2026.     int flags;        /* the sticky flags */
  2027.     char *result;    /* where to put the result */
  2028. {
  2029.     int count = 0;
  2030.     if (flags&STICK_NORTH) result[count++] = 'n';
  2031.     if (flags&STICK_EAST) result[count++] = 'e';
  2032.     if (flags&STICK_SOUTH) result[count++] = 's';
  2033.     if (flags&STICK_WEST) result[count++] = 'w';
  2034.     if (count) {
  2035.     result[count] = '\0';
  2036.     } else {
  2037.     sprintf(result,"{}");
  2038.     }
  2039. }
  2040.  
  2041. /* convert sticky string to flags */
  2042.  
  2043. static int
  2044. StringToSticky(string)
  2045.     char *string;
  2046. {
  2047.     int sticky = 0;
  2048.     char c;
  2049.  
  2050.     while ((c = *string++) != '\0') {
  2051.     switch (c) {
  2052.         case 'n': case 'N': sticky |= STICK_NORTH; break;
  2053.         case 'e': case 'E': sticky |= STICK_EAST;  break;
  2054.         case 's': case 'S': sticky |= STICK_SOUTH; break;
  2055.         case 'w': case 'W': sticky |= STICK_WEST;  break;
  2056.         case ' ': case ',': case '\t': case '\r': case '\n': break;
  2057.         default: return -1;
  2058.     }
  2059.     }
  2060.     return sticky;
  2061. }        
  2062.