home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / TCL / BLT / BLT1.7L1 / BLT1 / blt-1.7 / src / bltTable.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-22  |  102.5 KB  |  3,506 lines

  1.  
  2. /*
  3.  * bltTable.c --
  4.  *
  5.  *    This module implements a table geometry manager for the
  6.  *    Tk toolkit.
  7.  *
  8.  * Copyright 1993-1994 by AT&T Bell Laboratories.
  9.  * Permission to use, copy, modify, and distribute this software
  10.  * and its documentation for any purpose and without fee is hereby
  11.  * granted, provided that the above copyright notice appear in all
  12.  * copies and that both that the copyright notice and warranty
  13.  * disclaimer appear in supporting documentation, and that the
  14.  * names of AT&T Bell Laboratories any of their entities not be used
  15.  * in advertising or publicity pertaining to distribution of the
  16.  * software without specific, written prior permission.
  17.  *
  18.  * AT&T disclaims all warranties with regard to this software, including
  19.  * all implied warranties of merchantability and fitness.  In no event
  20.  * shall AT&T be liable for any special, indirect or consequential
  21.  * damages or any damages whatsoever resulting from loss of use, data
  22.  * or profits, whether in an action of contract, negligence or other
  23.  * tortuous action, arising out of or in connection with the use or
  24.  * performance of this software.
  25.  *
  26.  * Table geometry manager created by George Howlett.
  27.  */
  28.  
  29. /*
  30.  * To do:
  31.  *
  32.  * 2) No way to detect if window is already a slave of another geometry
  33.  *    manager.
  34.  *
  35.  * 3) No way to detect if window is already a master of another geometry
  36.  *    manager.  This one is bad because is can cause a bad interaction
  37.  *    with the window manager.
  38.  *
  39.  * 5) Relative sizing of partitions?
  40.  *
  41.  * 8) Change row/column allocation procedures?
  42.  *
  43.  */
  44.  
  45. #include "blt.h"
  46. #ifdef HAVE_LIMITS_H
  47. #include <limits.h>
  48. #endif
  49. #include <X11/Xutil.h>
  50. #include <X11/Xatom.h>
  51.  
  52. #ifndef TABLE_VERSION
  53. #define TABLE_VERSION "1.0"
  54. #endif
  55.  
  56. #define TRUE     1
  57. #define FALSE     0
  58.  
  59. #ifndef ABS
  60. #define ABS(x)        (((x)<0)?(-(x)):(x))
  61. #endif /* ABS */
  62.  
  63. #ifndef SHRT_MAX
  64. #define SHRT_MAX    0x7FFF
  65. #endif /* SHRT_MAX */
  66.  
  67. #ifndef USHRT_MAX
  68. #define    USHRT_MAX    0xFFFF
  69. #endif /* USHRT_MAX */
  70.  
  71. #define NUMENTRIES(t,type) \
  72.     (((type) == ROW_PARTITION_TYPE) ? (t)->numRows : (t)->numCols)
  73.  
  74. /*
  75.  * The following enumerated values are used as bit flags.
  76.  */
  77. typedef enum {
  78.     FILL_NONE, FILL_X, FILL_Y, FILL_BOTH
  79. } FillFlags;
  80.  
  81. typedef enum {
  82.     RESIZE_NONE, RESIZE_EXPAND, RESIZE_SHRINK, RESIZE_BOTH
  83. } ResizeFlags;
  84.  
  85. static char *fillStrings[] =
  86. {
  87.     "none", "x", "y", "both"
  88. };
  89. static char *resizeStrings[] =
  90. {
  91.     "none", "expand", "shrink", "both"
  92. };
  93.  
  94. /*
  95.  * Default bounds for both partitions and slave window requested sizes.
  96.  */
  97. #define DEF_MAX_LIMIT    SHRT_MAX
  98. #define DEF_MIN_LIMIT    0
  99.  
  100. #define WITHOUT_PAD     0
  101. #define WITH_PAD    1
  102.  
  103. /*
  104.  * Default values for slave window attributes.
  105.  */
  106. #define DEF_FILL    FILL_NONE
  107. #define DEF_IPAD_X    0
  108. #define DEF_IPAD_Y    0
  109. #define DEF_PAD_X    0
  110. #define DEF_PAD_Y    0
  111. #define DEF_COLUMN_SPAN    1
  112. #define DEF_ROW_SPAN    1
  113. #define DEF_ANCHOR    TK_ANCHOR_CENTER
  114.  
  115. static int initialized = 0;
  116. static Tcl_HashTable masterWindows, slaveWindows;
  117.  
  118. /*
  119.  * Sun's bundled and unbundled C compilers choke when using function
  120.  * typedefs that are declared static (but can handle "extern") such as
  121.  *
  122.  *     static Tk_OptionParseProc parseProc;
  123.  *      static Tk_OptionPrintProc printProc;
  124.  *
  125.  * Workaround: provide the long forward declarations here.
  126. */
  127. static int ParseLimits _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
  128.     Tk_Window tkwin, char *value, char *widgRec, int offset));
  129. static char *PrintLimits _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
  130.     char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
  131.  
  132. static Tk_CustomOption LimitsOption =
  133. {
  134.     ParseLimits, PrintLimits, (ClientData)0
  135. };
  136.  
  137. static int ParseFill _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
  138.     Tk_Window tkwin, char *value, char *widgRec, int offset));
  139. static char *PrintFill _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
  140.     char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
  141.  
  142. static Tk_CustomOption FillOption =
  143. {
  144.     ParseFill, PrintFill, (ClientData)0
  145. };
  146.  
  147. static int ParseResize _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
  148.     Tk_Window tkwin, char *value, char *widgRec, int offset));
  149. static char *PrintResize _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
  150.     char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
  151.  
  152. static Tk_CustomOption ResizeOption =
  153. {
  154.     ParseResize, PrintResize, (ClientData)0
  155. };
  156.  
  157. typedef struct {
  158.     int max, min, nom;
  159. } Limits;
  160.  
  161. typedef enum {
  162.     ROW_PARTITION_TYPE, COLUMN_PARTITION_TYPE
  163. } PartitionTypes;
  164.  
  165. static char *partitionNames[] =
  166. {
  167.     "row", "column"
  168. };
  169.  
  170. /*
  171.  * Partition --
  172.  *
  173.  *     A partition creates a definable space (row or column) in the
  174.  *    table. It may have requested minimum or maximum values which
  175.  *    constrain the size of it.
  176.  *
  177.  */
  178. typedef struct {
  179.     int size;            /* Current size of the partition. This size
  180.                  * is bounded by minSize and maxSize. */
  181.     int nomSize;        /* The nominal size (neither expanded nor
  182.                  * shrunk) of the partition based upon the
  183.                  * requested sizes of the slave windows in
  184.                  * this partition. */
  185.     int minSize, maxSize;    /* Size constraints on the partition */
  186.     int offset;            /* Offset of the partition (in pixels) from
  187.                  * the origin of the master window */
  188.     int span;            /* Minimum spanning window in partition */
  189.  
  190.     /* user-definable fields */
  191.  
  192.     ResizeFlags resize;        /* Indicates if the partition should shrink
  193.                  * or expand from its nominal size. */
  194.     int pad;            /* Pads the partition beyond its nominal
  195.                  * size */
  196.     Limits reqSize;        /* Requested bounds for the size of the
  197.                  * partition. The partition will not expand
  198.                  * or shrink beyond these limits, regardless
  199.                  * of how it was specified (max slave size).
  200.                  * This includes any extra padding which may
  201.                  * be specified. */
  202. } Partition;
  203.  
  204. #define DEF_TBL_RESIZE    "both"
  205.  
  206. static Tk_ConfigSpec rowConfigSpecs[] =
  207. {
  208.     {TK_CONFIG_CUSTOM, "-height", (char *)NULL, (char *)NULL,
  209.     (char *)NULL, Tk_Offset(Partition, reqSize), TK_CONFIG_NULL_OK,
  210.     &LimitsOption},
  211.     {TK_CONFIG_PIXELS, "-pady", (char *)NULL, (char *)NULL,
  212.     (char *)NULL, Tk_Offset(Partition, pad), 0},
  213.     {TK_CONFIG_CUSTOM, "-resize", (char *)NULL, (char *)NULL,
  214.     DEF_TBL_RESIZE, Tk_Offset(Partition, resize),
  215.     TK_CONFIG_DONT_SET_DEFAULT, &ResizeOption},
  216.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  217. };
  218.  
  219. static Tk_ConfigSpec columnConfigSpecs[] =
  220. {
  221.     {TK_CONFIG_PIXELS, "-padx", (char *)NULL, (char *)NULL,
  222.     (char *)NULL, Tk_Offset(Partition, pad), 0},
  223.     {TK_CONFIG_CUSTOM, "-resize", (char *)NULL, (char *)NULL,
  224.     DEF_TBL_RESIZE, Tk_Offset(Partition, resize),
  225.     TK_CONFIG_DONT_SET_DEFAULT, &ResizeOption},
  226.     {TK_CONFIG_CUSTOM, "-width", (char *)NULL, (char *)NULL,
  227.     (char *)NULL, Tk_Offset(Partition, reqSize), TK_CONFIG_NULL_OK,
  228.     &LimitsOption},
  229.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  230. };
  231.  
  232. static Tk_ConfigSpec *partConfigSpecs[2] =
  233. {
  234.     rowConfigSpecs, columnConfigSpecs
  235. };
  236.  
  237. struct Table;
  238.  
  239. /*
  240.  * Cubicle --
  241.  *
  242.  *     A cubicle is the frame which contains a slave window and its
  243.  *    padding. It may span multiple partitions and have requested
  244.  *    limits which constrain the size of it.  Currently, only a
  245.  *    single window can sit in a cubicle.
  246.  */
  247. typedef struct {
  248.     Tk_Window tkwin;        /* Slave window */
  249.     struct Table *tablePtr;    /* Table managing this window */
  250.     int x, y;            /* Last known position of the slave window
  251.                  * in its parent window.  Used to determine
  252.                  * if the window has moved since last
  253.                  * layout. */
  254.     int extBW;            /* Last known border width of slave window */
  255.  
  256.  
  257.     Limits reqWidth, reqHeight;    /* Configurable bounds for width and height
  258.                  * requests made by the slave window */
  259.     int rowSpan;        /* Number of rows spanned by slave */
  260.     int rowIndex;        /* Starting row of span */
  261.     int colSpan;        /* Number of columns spanned by slave */
  262.     int colIndex;        /* Starting column of span */
  263.  
  264.     Tk_Anchor anchor;        /* Anchor type: indicates how the window is
  265.                  * positioned if extra space is available in
  266.                  * the cubicle */
  267.     int padX, padY;        /* Extra padding around the slave window */
  268.     int ipadX, ipadY;        /* Extra padding inside of the slave window
  269.                  * (in addition to the requested size of
  270.                  * the window) */
  271.     FillFlags fill;        /* Fill style flag */
  272.     Blt_ListEntry *rowEntryPtr;    /* Pointer to cubicle in table's row sorted
  273.                  * list */
  274.     Blt_ListEntry *colEntryPtr;    /* Pointer to cubicle in table's column
  275.                      * sorted list */
  276. } Cubicle;
  277.  
  278. #define DEF_TBL_FILL    "none"
  279. #define DEF_TBL_ANCHOR    "center"
  280. #define DEF_TBL_SPAN    "1"
  281.  
  282. static Tk_ConfigSpec cubicleConfigSpecs[] =
  283. {
  284.     {TK_CONFIG_ANCHOR, "-anchor", (char *)NULL, (char *)NULL,
  285.     DEF_TBL_ANCHOR, Tk_Offset(Cubicle, anchor),
  286.     TK_CONFIG_DONT_SET_DEFAULT},
  287.     {TK_CONFIG_INT, "-columnspan", "columnSpan", (char *)NULL,
  288.     DEF_TBL_SPAN, Tk_Offset(Cubicle, colSpan),
  289.     TK_CONFIG_DONT_SET_DEFAULT},
  290.     {TK_CONFIG_SYNONYM, "-cspan", "columnSpan", (char *)NULL,
  291.     (char *)NULL, Tk_Offset(Cubicle, colSpan), 0},
  292.     {TK_CONFIG_CUSTOM, "-fill", (char *)NULL, (char *)NULL,
  293.     DEF_TBL_FILL, Tk_Offset(Cubicle, fill),
  294.     TK_CONFIG_DONT_SET_DEFAULT, &FillOption},
  295.     {TK_CONFIG_PIXELS, "-padx", (char *)NULL, (char *)NULL,
  296.     (char *)NULL, Tk_Offset(Cubicle, padX), 0},
  297.     {TK_CONFIG_PIXELS, "-pady", (char *)NULL, (char *)NULL,
  298.     (char *)NULL, Tk_Offset(Cubicle, padY), 0},
  299.     {TK_CONFIG_PIXELS, "-ipadx", (char *)NULL, (char *)NULL,
  300.     (char *)NULL, Tk_Offset(Cubicle, ipadX), 0},
  301.     {TK_CONFIG_PIXELS, "-ipady", (char *)NULL, (char *)NULL,
  302.     (char *)NULL, Tk_Offset(Cubicle, ipadY), 0},
  303.     {TK_CONFIG_CUSTOM, "-reqheight", (char *)NULL, (char *)NULL,
  304.     (char *)NULL, Tk_Offset(Cubicle, reqHeight), TK_CONFIG_NULL_OK,
  305.     &LimitsOption},
  306.     {TK_CONFIG_CUSTOM, "-reqwidth", (char *)NULL, (char *)NULL,
  307.     (char *)NULL, Tk_Offset(Cubicle, reqWidth), TK_CONFIG_NULL_OK,
  308.     &LimitsOption},
  309.     {TK_CONFIG_INT, "-rowspan", "rowSpan", (char *)NULL,
  310.     DEF_TBL_SPAN, Tk_Offset(Cubicle, rowSpan),
  311.     TK_CONFIG_DONT_SET_DEFAULT},
  312.     {TK_CONFIG_SYNONYM, "-rspan", "rowSpan", (char *)NULL,
  313.     (char *)NULL, Tk_Offset(Cubicle, rowSpan), 0},
  314.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  315. };
  316.  
  317. /*
  318.  * Cubicles are stored in two linked lists in the Table structure.
  319.  * They are keyed by their starting row,column position.  The
  320.  * following union makes a one-word key out of the row and column
  321.  * components.  This, of course, assumes that a short int is half the
  322.  * size of an int.  Which is NOT the case on a cray;
  323. */
  324. typedef union {
  325.     struct Position {
  326.     unsigned short row;
  327.     unsigned short column;
  328.     } position;
  329.     unsigned int index;
  330. } SlaveKey;
  331.  
  332. /*
  333.  * This is the default number of elements in the statically
  334.  * pre-allocated column and row arrays.  This number should reflect a
  335.  * useful number of row and columns, which fit most applications.
  336. */
  337. #define DEF_ARRAY_SIZE    32
  338.  
  339. /*
  340.  * Table structure
  341.  */
  342. typedef struct Table {
  343.     int flags;            /* See the flags definitions below. */
  344.     Tk_Window searchWin;    /* Main window of window hierarchy */
  345.     Tk_Window tkwin;        /* The master window in which the slave
  346.                  * windows are arranged. */
  347.     Tcl_Interp *interp;        /* Interpreter associated with all windows */
  348.     Blt_LinkedList *listPtr;    /* Points to either list of row or column
  349.                  * sorted cubicles */
  350.     Blt_LinkedList rowSorted;    /* List of cubicles sorted by increasing
  351.                  * order of rows spanned and row,column
  352.                  * indices */
  353.     Blt_LinkedList colSorted;    /* List of cubicles sorted by increasing
  354.                  * order of columns spanned and column,row
  355.                  * indices */
  356.     /*
  357.      * Pre-allocated row and column partitions.
  358.      */
  359.     Partition colSpace[DEF_ARRAY_SIZE];
  360.     Partition rowSpace[DEF_ARRAY_SIZE];
  361.  
  362.     Partition *colPtr;        /* Pointer to array of column information:
  363.                  * Initially points to colSpace */
  364.     Partition *rowPtr;        /* Pointer to array of row information:
  365.                  * Initially points to rowSpace */
  366.     int rowSize;        /* Number of rows allocated */
  367.     int numRows;        /* Number of rows currently used */
  368.     int colSize;        /* Number of columns allocated */
  369.     int numCols;        /* Number of columns currently used */
  370.     int width, height;        /* Dimensions of the master window */
  371.     int reqWidth, reqHeight;    /* Normal width and height of table */
  372. } Table;
  373.  
  374. /*
  375.  * Table flags definitions
  376.  */
  377. #define ARRANGE_PENDING 1    /* A call to ArrangeTable is pending. This
  378.                  * flag allows multiple layout changes to be
  379.                  * requested before the table is actually
  380.                  * reconfigured. */
  381. #define REQUEST_LAYOUT     2    /* Get the requested sizes of the slave
  382.                  * windows before expanding/shrinking the
  383.                  * size of the master window.  It's
  384.                  * necessary to recompute the layout every
  385.                  * time a partition or cubicle is added,
  386.                  * reconfigured, or deleted, but not when
  387.                  * the master window is resized. */
  388. #define NON_PARENT    4    /* The table is managing slaves which are
  389.                  * not children of the master window. This
  390.                  * requires that they are moved when the
  391.                  * master window is moved (a definite
  392.                  * performance hit). */
  393. /*
  394.  * Forward declarations
  395.  */
  396. static void ArrangeTable _ANSI_ARGS_((ClientData clientData));
  397. static void DestroyTable _ANSI_ARGS_((ClientData clientData));
  398. static void DestroyCubicle _ANSI_ARGS_((Cubicle *cubiPtr));
  399. static void TableEventProc _ANSI_ARGS_((ClientData clientData,
  400.     XEvent *eventPtr));
  401. static void InitPartitions _ANSI_ARGS_((Partition *partPtr, int length));
  402. static void LinkRowEntry _ANSI_ARGS_((Cubicle *cubiPtr));
  403. static void LinkColumnEntry _ANSI_ARGS_((Cubicle *cubiPtr));
  404.  
  405. extern int strcasecmp _ANSI_ARGS_((CONST char *s1, CONST char *s2));
  406.  
  407. /*
  408.  *----------------------------------------------------------------------
  409.  *
  410.  * ParseLimits --
  411.  *
  412.  *    Converts the list of elements into zero or more pixel values
  413.  *    which determine the range of pixel values possible.  An element
  414.  *    can be in any form accepted by Tk_GetPixels. The list has a
  415.  *    different meaning based upon the number of elements.
  416.  *
  417.  *        # of elements:
  418.  *        0 - the limits are reset to the defaults.
  419.  *        1 - the minimum and maximum values are set to this
  420.  *            value, freezing the range at a single value.
  421.  *        2 - first element is the minimum, the second is the
  422.  *            maximum.
  423.  *        3 - first element is the minimum, the second is the
  424.  *            maximum, and the third is the nominal value.
  425.  *
  426.  * Results:
  427.  *    The return value is a standard Tcl result.  The min and
  428.  *    max fields of the range are set.
  429.  *
  430.  *----------------------------------------------------------------------
  431.  */
  432. /*ARGSUSED*/
  433. static int
  434. ParseLimits(clientData, interp, tkwin, value, widgRec, offset)
  435.     ClientData clientData;    /* not used */
  436.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  437.     Tk_Window tkwin;        /* Window of table */
  438.     char *value;        /* New width list */
  439.     char *widgRec;        /* Widget record */
  440.     int offset;            /* Offset of limits */
  441. {
  442.     Limits *limitsPtr = (Limits *)(widgRec + offset);
  443.     int numElem;
  444.     char **elemArr;
  445.     int result = TCL_ERROR;
  446.     int lim[3];
  447.     register int i;
  448.     int size;
  449.  
  450.     if (value == NULL) {
  451.     limitsPtr->nom = limitsPtr->min = DEF_MIN_LIMIT;
  452.     limitsPtr->max = DEF_MAX_LIMIT;
  453.     return TCL_OK;
  454.     }
  455.     if (Tcl_SplitList(interp, value, &numElem, &elemArr) != TCL_OK) {
  456.     return TCL_ERROR;
  457.     }
  458.     if (numElem > 3) {
  459.     Tcl_AppendResult(interp, "wrong # limits \"", value, "\"",
  460.         (char *)NULL);
  461.     goto error;
  462.     }
  463.     for (i = 0; i < numElem; i++) {
  464.     if (((elemArr[i][0] == 'i') || (elemArr[i][0] == 'I')) &&
  465.         (strcasecmp(elemArr[i], "inf") == 0)) {
  466.         size = DEF_MAX_LIMIT;
  467.     } else if (Tk_GetPixels(interp, tkwin, elemArr[i], &size) != TCL_OK) {
  468.         goto error;
  469.     }
  470.     if ((size < DEF_MIN_LIMIT) || (size > DEF_MAX_LIMIT)) {
  471.         Tcl_AppendResult(interp, "invalid limit \"", value, "\"",
  472.         (char *)NULL);
  473.         goto error;
  474.     }
  475.     lim[i] = size;
  476.     }
  477.     switch (numElem) {
  478.     case 1:
  479.     limitsPtr->max = limitsPtr->min = lim[0];
  480.     limitsPtr->nom = DEF_MIN_LIMIT;
  481.     break;
  482.     case 2:
  483.     if (lim[1] < lim[0]) {
  484.         Tcl_AppendResult(interp, "invalid limits \"", value, "\"",
  485.         (char *)NULL);
  486.         goto error;
  487.     }
  488.     limitsPtr->min = lim[0];
  489.     limitsPtr->max = lim[1];
  490.     limitsPtr->nom = DEF_MIN_LIMIT;
  491.     break;
  492.     case 3:
  493.     if (lim[1] < lim[0]) {
  494.         Tcl_AppendResult(interp, "invalid range \"", value, "\"",
  495.         (char *)NULL);
  496.         goto error;
  497.     }
  498.     if ((lim[2] < lim[0]) || (lim[2] > lim[1])) {
  499.         Tcl_AppendResult(interp, "invalid nominal \"", value, "\"",
  500.         (char *)NULL);
  501.         goto error;
  502.     }
  503.     limitsPtr->min = lim[0];
  504.     limitsPtr->max = lim[1];
  505.     limitsPtr->nom = lim[2];
  506.     break;
  507.     }
  508.     result = TCL_OK;
  509.   error:
  510.     free(elemArr);
  511.     return result;
  512. }
  513.  
  514.  
  515. static void
  516. LimitsToString(min, max, nom, string)
  517.     int min, max, nom;
  518.     char string[];
  519. {
  520.     if (nom > DEF_MIN_LIMIT) {
  521.     sprintf(string, "%d %d %d", min, max, nom);
  522.     } else if (min == max) {
  523.     sprintf(string, "%d", max);
  524.     } else if ((min != DEF_MIN_LIMIT) || (max != DEF_MAX_LIMIT)) {
  525.     sprintf(string, "%d %d", min, max);
  526.     } else {
  527.     string[0] = '\0';
  528.     }
  529. }
  530.  
  531. /*
  532.  *----------------------------------------------------------------------
  533.  *
  534.  * PrintLimits --
  535.  *
  536.  *    Convert the limits of the pixel values allowed into a list.
  537.  *
  538.  * Results:
  539.  *    The string representation of the limits is returned.
  540.  *
  541.  *----------------------------------------------------------------------
  542.  */
  543. /*ARGSUSED*/
  544. static char *
  545. PrintLimits(clientData, tkwin, widgRec, offset, freeProcPtr)
  546.     ClientData clientData;    /* not used */
  547.     Tk_Window tkwin;        /* not used */
  548.     char *widgRec;        /* Row/column structure record */
  549.     int offset;            /* Offset of window Partition record */
  550.     Tcl_FreeProc **freeProcPtr;    /* Memory deallocation routine */
  551. {
  552.     Limits *limitsPtr = (Limits *)(widgRec + offset);
  553.     char *result;
  554.     char string[200];
  555.  
  556.     LimitsToString(limitsPtr->min, limitsPtr->max, limitsPtr->nom, string);
  557.     result = strdup(string);
  558.     *freeProcPtr = TCL_DYNAMIC;
  559.     return (result);
  560. }
  561.  
  562. /*
  563.  *----------------------------------------------------------------------
  564.  *
  565.  * ParseFill --
  566.  *
  567.  *    Converts the fill style string into its numeric representation.
  568.  *    This configuration option affects how the slave window is expanded
  569.  *    if there is extra space in the cubicle in which it sits.
  570.  *
  571.  *    Valid style strings are:
  572.  *
  573.  *        "none"     Don't expand the window to fill the cubicle.
  574.  *         "x"     Expand only the window's width.
  575.  *        "y"     Expand only the window's height.
  576.  *        "both"   Expand both the window's height and width.
  577.  *
  578.  *----------------------------------------------------------------------
  579.  */
  580. /*ARGSUSED*/
  581. static int
  582. ParseFill(clientData, interp, tkwin, value, widgRec, offset)
  583.     ClientData clientData;    /* not used */
  584.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  585.     Tk_Window tkwin;        /* not used */
  586.     char *value;        /* Fill style string */
  587.     char *widgRec;        /* Cubicle structure record */
  588.     int offset;            /* Offset of style in record */
  589. {
  590.     FillFlags *fillPtr = (FillFlags *)(widgRec + offset);
  591.     int length;
  592.     char c;
  593.  
  594.     c = value[0];
  595.     length = strlen(value);
  596.     if ((c == 'n') && (strncmp(value, "none", length) == 0)) {
  597.     *fillPtr = FILL_NONE;
  598.     } else if ((c == 'x') && (strncmp(value, "x", length) == 0)) {
  599.     *fillPtr = FILL_X;
  600.     } else if ((c == 'y') && (strncmp(value, "y", length) == 0)) {
  601.     *fillPtr = FILL_Y;
  602.     } else if ((c == 'b') && (strncmp(value, "both", length) == 0)) {
  603.     *fillPtr = FILL_BOTH;
  604.     } else {
  605.     Tcl_AppendResult(interp, "bad fill argument \"", value,
  606.         "\": should be none, x, y, or both", (char *)NULL);
  607.     return TCL_ERROR;
  608.     }
  609.     return (TCL_OK);
  610. }
  611.  
  612. /*
  613.  *----------------------------------------------------------------------
  614.  *
  615.  * PrintFill --
  616.  *
  617.  *    Returns the fill style string based upon the fill flags.
  618.  *
  619.  * Results:
  620.  *    The fill style string is returned.
  621.  *
  622.  *----------------------------------------------------------------------
  623.  */
  624. /*ARGSUSED*/
  625. static char *
  626. PrintFill(clientData, tkwin, widgRec, offset, freeProcPtr)
  627.     ClientData clientData;    /* not used */
  628.     Tk_Window tkwin;        /* not used */
  629.     char *widgRec;        /* Row/column structure record */
  630.     int offset;            /* Offset of fill in Partition record */
  631.     Tcl_FreeProc **freeProcPtr;    /* not used */
  632. {
  633.     FillFlags fill = *(FillFlags *)(widgRec + offset);
  634.  
  635.     return (fillStrings[(int)fill]);
  636. }
  637.  
  638. /*
  639.  *----------------------------------------------------------------------
  640.  *
  641.  * ParseResize --
  642.  *
  643.  *    Converts the resize mode into its numeric representation.
  644.  *    Valid mode strings are "none", "expand", "shrink", or "both".
  645.  *
  646.  *----------------------------------------------------------------------
  647.  */
  648. /*ARGSUSED*/
  649. static int
  650. ParseResize(clientData, interp, tkwin, value, widgRec, offset)
  651.     ClientData clientData;    /* not used */
  652.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  653.     Tk_Window tkwin;        /* not used */
  654.     char *value;        /* Resize style string */
  655.     char *widgRec;        /* Cubicle structure record */
  656.     int offset;            /* Offset of style in record */
  657. {
  658.     ResizeFlags *resizePtr = (ResizeFlags *)(widgRec + offset);
  659.     int length;
  660.     char c;
  661.  
  662.     c = value[0];
  663.     length = strlen(value);
  664.     if ((c == 'n') && (strncmp(value, "none", length) == 0)) {
  665.     *resizePtr = RESIZE_NONE;
  666.     } else if ((c == 'b') && (strncmp(value, "both", length) == 0)) {
  667.     *resizePtr = RESIZE_BOTH;
  668.     } else if ((c == 'e') && (strncmp(value, "expand", length) == 0)) {
  669.     *resizePtr = RESIZE_EXPAND;
  670.     } else if ((c == 's') && (strncmp(value, "shrink", length) == 0)) {
  671.     *resizePtr = RESIZE_SHRINK;
  672.     } else {
  673.     Tcl_AppendResult(interp, "bad resize argument \"", value,
  674.         "\": should be none, expand, shrink, or both", (char *)NULL);
  675.     return TCL_ERROR;
  676.     }
  677.     return (TCL_OK);
  678. }
  679.  
  680. /*
  681.  *----------------------------------------------------------------------
  682.  *
  683.  * PrintResize --
  684.  *
  685.  *    Returns resize mode string based upon the resize flags.
  686.  *
  687.  * Results:
  688.  *    The resize mode string is returned.
  689.  *
  690.  *----------------------------------------------------------------------
  691.  */
  692. /*ARGSUSED*/
  693. static char *
  694. PrintResize(clientData, tkwin, widgRec, offset, freeProcPtr)
  695.     ClientData clientData;    /* not used */
  696.     Tk_Window tkwin;        /* not used */
  697.     char *widgRec;        /* Row/column structure record */
  698.     int offset;            /* Offset of resize in Partition record */
  699.     Tcl_FreeProc **freeProcPtr;    /* not used */
  700. {
  701.     ResizeFlags resize = *(ResizeFlags *)(widgRec + offset);
  702.  
  703.     return (resizeStrings[(int)resize]);
  704. }
  705.  
  706. /*
  707.  *--------------------------------------------------------------
  708.  *
  709.  * TableEventProc --
  710.  *
  711.  *    This procedure is invoked by the Tk event handler when
  712.  *    the master window is reconfigured or destroyed.
  713.  *
  714.  *    The table will be rearranged at the next idle point if
  715.  *    the master window has been resized or moved. There's a
  716.  *    distinction made between parent and non-parent master
  717.  *    window arrangements.  If the master window is moved and
  718.  *    it's the parent of its slaves, the slaves are moved
  719.  *    automatically.  If it's not the parent, the slaves need
  720.  *    to be moved.  This can be a performance hit in rare cases
  721.  *    where we're scrolling the master window (by moving it)
  722.  *    and there are lots of slave windows.
  723.  *
  724.  * Results:
  725.  *    None.
  726.  *
  727.  * Side effects:
  728.  *    Arranges for the table associated with tkwin to have its
  729.  *    layout re-computed and drawn at the next idle point.
  730.  *
  731.  *--------------------------------------------------------------
  732.  */
  733. static void
  734. TableEventProc(clientData, eventPtr)
  735.     ClientData clientData;    /* Information about window */
  736.     XEvent *eventPtr;        /* Information about event */
  737. {
  738.     register Table *tablePtr = (Table *)clientData;
  739.  
  740.     if (eventPtr->type == ConfigureNotify) {
  741.     if ((Blt_GetListLength(tablePtr->listPtr) > 0) &&
  742.         !(tablePtr->flags & ARRANGE_PENDING) &&
  743.         ((tablePtr->width != Tk_Width(tablePtr->tkwin)) ||
  744.         (tablePtr->height != Tk_Height(tablePtr->tkwin))
  745.         || (tablePtr->flags & NON_PARENT))) {
  746.         tablePtr->flags |= ARRANGE_PENDING;
  747.         Tk_DoWhenIdle(ArrangeTable, (ClientData)tablePtr);
  748.     }
  749.     } else if (eventPtr->type == DestroyNotify) {
  750.     if (tablePtr->flags & ARRANGE_PENDING) {
  751.         Tk_CancelIdleCall(ArrangeTable, (ClientData)tablePtr);
  752.     }
  753.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&masterWindows,
  754.         (char *)tablePtr->tkwin));
  755.     tablePtr->tkwin = NULL;
  756.     Tk_EventuallyFree((ClientData)tablePtr, DestroyTable);
  757.     }
  758. }
  759.  
  760. /*
  761.  *----------------------------------------------------------------------
  762.  *
  763.  * SlaveEventProc --
  764.  *
  765.  *    This procedure is invoked by the Tk event handler when
  766.  *    StructureNotify events occur for a slave window.  When a slave
  767.  *    window is destroyed, it frees the corresponding cubicle structure
  768.  *    and arranges for the table layout to be re-computed at the next
  769.  *    idle point.
  770.  *
  771.  * Results:
  772.  *    None.
  773.  *
  774.  * Side effects:
  775.  *    If the slave window was deleted, the Cubicle structure gets cleaned
  776.  *    up and the table is rearranged.
  777.  *
  778.  *----------------------------------------------------------------------
  779.  */
  780.  
  781. static void
  782. SlaveEventProc(clientData, eventPtr)
  783.     ClientData clientData;    /* Pointer to Slave structure for window
  784.                  * referred to by eventPtr. */
  785.     XEvent *eventPtr;        /* Describes what just happened. */
  786. {
  787.     Cubicle *cubiPtr = (Cubicle *)clientData;
  788.  
  789.     if (eventPtr->type == ConfigureNotify) {
  790.     int extBW;
  791.  
  792.     extBW = Tk_Changes(cubiPtr->tkwin)->border_width;
  793.     cubiPtr->tablePtr->flags |= REQUEST_LAYOUT;
  794.     if (!(cubiPtr->tablePtr->flags & ARRANGE_PENDING) &&
  795.         (cubiPtr->extBW != extBW)) {
  796.         cubiPtr->extBW = extBW;
  797.         cubiPtr->tablePtr->flags |= ARRANGE_PENDING;
  798.         Tk_DoWhenIdle(ArrangeTable, (ClientData)cubiPtr->tablePtr);
  799.     }
  800.     } else if (eventPtr->type == DestroyNotify) {
  801.     cubiPtr->tablePtr->flags |= REQUEST_LAYOUT;
  802.     if (!(cubiPtr->tablePtr->flags & ARRANGE_PENDING)) {
  803.         cubiPtr->tablePtr->flags |= ARRANGE_PENDING;
  804.         Tk_DoWhenIdle(ArrangeTable, (ClientData)cubiPtr->tablePtr);
  805.     }
  806.     DestroyCubicle(cubiPtr);
  807.     }
  808. }
  809.  
  810. /*
  811.  *--------------------------------------------------------------
  812.  *
  813.  * SlaveReqProc --
  814.  *
  815.  *    This procedure is invoked by Tk_GeometryRequest for slave
  816.  *    windows managed by the table geometry manager.
  817.  *
  818.  * Results:
  819.  *    None.
  820.  *
  821.  * Side effects:
  822.  *    Arranges for the table associated with the slave window to
  823.  *    have its layout re-computed and arranged at the next idle
  824.  *    point.
  825.  *
  826.  *--------------------------------------------------------------
  827.  */
  828. /* ARGSUSED */
  829. static void
  830. SlaveReqProc(clientData, tkwin)
  831.     ClientData clientData;    /* Information about window that got new
  832.                  * preferred geometry.  */
  833.     Tk_Window tkwin;        /* Other Tk-related information about the
  834.                      * window. */
  835. {
  836.     Cubicle *cubiPtr = (Cubicle *)clientData;
  837.  
  838.     cubiPtr->tablePtr->flags |= REQUEST_LAYOUT;
  839.     if (!(cubiPtr->tablePtr->flags & ARRANGE_PENDING)) {
  840.     cubiPtr->tablePtr->flags |= ARRANGE_PENDING;
  841.     Tk_DoWhenIdle(ArrangeTable, (ClientData)cubiPtr->tablePtr);
  842.     }
  843. }
  844.  
  845. /*
  846.  *----------------------------------------------------------------------
  847.  *
  848.  * FindCubicle --
  849.  *
  850.  *    Searches for the Cubicle structure corresponding to the given
  851.  *    window.
  852.  *
  853.  * Results:
  854.  *    If a structure associated with the window exists, a pointer to
  855.  *    that structure is returned. Otherwise NULL is returned and if
  856.  *    the TCL_LEAVE_ERR_MSG flag is set, an error message is left in
  857.  *    interp->result.
  858.  *
  859.  *----------------------------------------------------------------------
  860.  */
  861.  
  862. static Cubicle *
  863. FindCubicle(interp, tkwin, flags)
  864.     Tcl_Interp *interp;
  865.     Tk_Window tkwin;        /* Slave window associated with table entry */
  866.     int flags;
  867. {
  868.     Tcl_HashEntry *entryPtr;
  869.  
  870.     entryPtr = Tcl_FindHashEntry(&slaveWindows, (char *)tkwin);
  871.     if (entryPtr == NULL) {
  872.     if (flags & TCL_LEAVE_ERR_MSG) {
  873.         Tcl_AppendResult(interp, "\"", Tk_PathName(tkwin),
  874.         "\" is not managed by any table", (char *)NULL);
  875.     }
  876.     return NULL;
  877.     }
  878.     return ((Cubicle *)Tcl_GetHashValue(entryPtr));
  879. }
  880.  
  881. /*
  882.  *----------------------------------------------------------------------
  883.  *
  884.  * CreateCubicle --
  885.  *
  886.  *    This procedure creates and initializes a new Cubicle structure
  887.  *    to contain a slave window.  A valid slave window must have a
  888.  *    parent window that is either a) the master window or b) a mutual
  889.  *    ancestor of the master window.
  890.  *
  891.  * Results:
  892.  *    Returns a pointer to the new structure describing the new table
  893.  *    slave window entry.  If an error occurred, then the return
  894.  *    value is NULL and an error message is left in interp->result.
  895.  *
  896.  * Side effects:
  897.  *    Memory is allocated and initialized for the Cubicle structure.
  898.  *
  899.  *----------------------------------------------------------------------
  900.  */
  901. static Cubicle *
  902. CreateCubicle(tablePtr, tkwin)
  903.     Table *tablePtr;
  904.     Tk_Window tkwin;
  905. {
  906.     register Cubicle *cubiPtr;
  907.     int dummy;
  908.     Tk_Window parent, ancestor;
  909.     Tcl_HashEntry *entryPtr;
  910.     int notParent = FALSE;    /* Indicates that the master window is not the
  911.                  * parent of the new slave window. */
  912.  
  913.     /*
  914.      * A valid slave window has a parent window that either
  915.      *
  916.      *    1) is the master window, or
  917.      *       2) is a mutual ancestor of the master window.
  918.      */
  919.     ancestor = Tk_Parent(tkwin);
  920.     for (parent = tablePtr->tkwin; (parent != ancestor) &&
  921.     (!Tk_IsTopLevel(parent)); parent = Tk_Parent(parent)) {
  922.     notParent = TRUE;
  923.     }
  924.     if (ancestor != parent) {
  925.     Tcl_AppendResult(tablePtr->interp, "can't manage \"",
  926.         Tk_PathName(tkwin), "\" in table \"",
  927.         Tk_PathName(tablePtr->tkwin), "\"", (char *)NULL);
  928.     return NULL;
  929.     }
  930.     cubiPtr = (Cubicle *)malloc(sizeof(Cubicle));
  931.     if (cubiPtr == NULL) {
  932.     tablePtr->interp->result = "can't allocate cubicle";
  933.     return NULL;
  934.     }
  935.     if (notParent) {
  936.     tablePtr->flags |= NON_PARENT;
  937.     }
  938.     /* Initialize the cubicle structure */
  939.  
  940.     cubiPtr->x = cubiPtr->y = 0;
  941.     cubiPtr->tkwin = tkwin;
  942.     cubiPtr->tablePtr = tablePtr;
  943.     cubiPtr->extBW = Tk_Changes(tkwin)->border_width;
  944.     cubiPtr->fill = DEF_FILL;
  945.     cubiPtr->ipadX = DEF_IPAD_X;
  946.     cubiPtr->ipadY = DEF_IPAD_Y;
  947.     cubiPtr->padX = DEF_PAD_X;
  948.     cubiPtr->padY = DEF_PAD_Y;
  949.     cubiPtr->anchor = DEF_ANCHOR;
  950.     cubiPtr->rowSpan = DEF_ROW_SPAN;
  951.     cubiPtr->colSpan = DEF_COLUMN_SPAN;
  952.     cubiPtr->reqWidth.min = cubiPtr->reqHeight.min = DEF_MIN_LIMIT;
  953.     cubiPtr->reqWidth.max = cubiPtr->reqHeight.max = DEF_MAX_LIMIT;
  954.     cubiPtr->reqWidth.nom = cubiPtr->reqHeight.nom = DEF_MIN_LIMIT;
  955.     cubiPtr->rowEntryPtr = cubiPtr->colEntryPtr = NULL;
  956.     entryPtr = Tcl_CreateHashEntry(&slaveWindows, (char *)tkwin, &dummy);
  957.     Tcl_SetHashValue(entryPtr, (char *)cubiPtr);
  958.     Tk_CreateEventHandler(tkwin, StructureNotifyMask, SlaveEventProc,
  959.     (ClientData)cubiPtr);
  960.     Tk_ManageGeometry(tkwin, SlaveReqProc, (ClientData)cubiPtr);
  961.     return (cubiPtr);
  962. }
  963.  
  964. /*
  965.  *----------------------------------------------------------------------
  966.  *
  967.  * DestroyCubicle --
  968.  *
  969.  *    Removes the Cubicle structure from the hash table and frees
  970.  *    the memory allocated by it.  If the table is still in use
  971.  *    (i.e. was not called from DestoryTable), remove its entries
  972.  *    from the lists of row and column sorted partitions.
  973.  *
  974.  * Results:
  975.  *    None.
  976.  *
  977.  * Side effects:
  978.  *    Everything associated with the cubicle is freed up.
  979.  *
  980.  *----------------------------------------------------------------------
  981.  */
  982. static void
  983. DestroyCubicle(cubiPtr)
  984.     Cubicle *cubiPtr;
  985. {
  986.     Tcl_HashEntry *entryPtr;
  987.  
  988.     if (cubiPtr->rowEntryPtr != NULL) {
  989.     Blt_DeleteListEntry(&(cubiPtr->tablePtr->rowSorted),
  990.         cubiPtr->rowEntryPtr);
  991.     }
  992.     if (cubiPtr->colEntryPtr != NULL) {
  993.     Blt_DeleteListEntry(&(cubiPtr->tablePtr->colSorted),
  994.         cubiPtr->colEntryPtr);
  995.     }
  996.     Tk_DeleteEventHandler(cubiPtr->tkwin, StructureNotifyMask,
  997.     SlaveEventProc, (ClientData)cubiPtr);
  998.     Tk_ManageGeometry(cubiPtr->tkwin, (Tk_GeometryProc *) NULL,
  999.     (ClientData)cubiPtr);
  1000.     entryPtr = Tcl_FindHashEntry(&slaveWindows, (char *)cubiPtr->tkwin);
  1001.     Tcl_DeleteHashEntry(entryPtr);
  1002.     free((char *)cubiPtr);
  1003. }
  1004.  
  1005. /*
  1006.  *----------------------------------------------------------------------
  1007.  *
  1008.  * ConfigureCubicle --
  1009.  *
  1010.  *    This procedure is called to process an argv/argc list, plus
  1011.  *    the Tk option database, in order to configure (or reconfigure)
  1012.  *    one or more cubicles associated with a slave window which is
  1013.  *    managed by the table geometry manager.
  1014.  *
  1015.  * Note:
  1016.  *    Currently only the one slave window can be queried while many
  1017.  *    can be reconfigured at a time.
  1018.  *
  1019.  * Results:
  1020.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  1021.  *    returned, then interp->result contains an error message.
  1022.  *
  1023.  * Side effects:
  1024.  *    The table layout is recomputed and rearranged at the next
  1025.  *    idle point.
  1026.  *
  1027.  *----------------------------------------------------------------------
  1028.  */
  1029. static int
  1030. ConfigureCubicle(clientData, interp, argc, argv)
  1031.     ClientData clientData;
  1032.     Tcl_Interp *interp;
  1033.     int argc;
  1034.     char **argv;
  1035. {
  1036.     Tk_Window searchWin = (Tk_Window)clientData;
  1037.     Cubicle *cubiPtr;
  1038.     Tk_Window tkwin;
  1039.     Table *tablePtr;
  1040.     int numSlaves, numOptions;
  1041.     int oldRowSpan, oldColSpan;
  1042.     register int i;
  1043.  
  1044.     /* Find the number of slave windows to be configured */
  1045.     numSlaves = 0;
  1046.     for (i = 0; i < argc; i++) {
  1047.     if (argv[i][0] != '.') {
  1048.         break;
  1049.     }
  1050.     numSlaves++;
  1051.     }
  1052.     /* And the number of options to be applied */
  1053.     numOptions = argc - numSlaves;
  1054.  
  1055.     for (i = 0; i < numSlaves; i++) {
  1056.     tkwin = Tk_NameToWindow(interp, argv[i], searchWin);
  1057.     if (tkwin == NULL) {
  1058.         return TCL_ERROR;
  1059.     }
  1060.     cubiPtr = FindCubicle(interp, tkwin, TCL_LEAVE_ERR_MSG);
  1061.     if (cubiPtr == NULL) {
  1062.         return TCL_ERROR;
  1063.     }
  1064.     if (numOptions == 0) {
  1065.         return (Tk_ConfigureInfo(interp, tkwin, cubicleConfigSpecs,
  1066.             (char *)cubiPtr, (char *)NULL, 0));
  1067.     } else if (numOptions == 1) {
  1068.         return (Tk_ConfigureInfo(interp, tkwin, cubicleConfigSpecs,
  1069.             (char *)cubiPtr, argv[numSlaves], 0));
  1070.     }
  1071.     oldRowSpan = cubiPtr->rowSpan;
  1072.     oldColSpan = cubiPtr->colSpan;
  1073.     if (Tk_ConfigureWidget(interp, tkwin, cubicleConfigSpecs,
  1074.         numOptions, argv + numSlaves, (char *)cubiPtr,
  1075.         TK_CONFIG_ARGV_ONLY) != TCL_OK) {
  1076.         return TCL_ERROR;
  1077.     }
  1078.     if ((cubiPtr->colSpan < 1) || (cubiPtr->colSpan > USHRT_MAX)) {
  1079.         Tcl_AppendResult(interp, "bad column span specified for \"",
  1080.         Tk_PathName(tkwin), "\"", (char *)NULL);
  1081.         return TCL_ERROR;
  1082.     }
  1083.     if ((cubiPtr->rowSpan < 1) || (cubiPtr->rowSpan > USHRT_MAX)) {
  1084.         Tcl_AppendResult(interp, "bad row span specified for \"",
  1085.         Tk_PathName(tkwin), "\"", (char *)NULL);
  1086.         return TCL_ERROR;
  1087.     }
  1088.     tablePtr = cubiPtr->tablePtr;
  1089.     if (oldColSpan != cubiPtr->colSpan) {
  1090.         Blt_UnlinkListEntry(&(tablePtr->colSorted), cubiPtr->colEntryPtr);
  1091.         LinkColumnEntry(cubiPtr);
  1092.     }
  1093.     if (oldRowSpan != cubiPtr->rowSpan) {
  1094.         Blt_UnlinkListEntry(&(tablePtr->rowSorted), cubiPtr->rowEntryPtr);
  1095.         LinkRowEntry(cubiPtr);
  1096.     }
  1097.     tablePtr->flags |= REQUEST_LAYOUT;
  1098.     if (!(tablePtr->flags & ARRANGE_PENDING)) {
  1099.         tablePtr->flags |= ARRANGE_PENDING;
  1100.         Tk_DoWhenIdle(ArrangeTable, (ClientData)tablePtr);
  1101.     }
  1102.     }
  1103.     return TCL_OK;
  1104. }
  1105.  
  1106. /*
  1107.  *----------------------------------------------------------------------
  1108.  *
  1109.  * FindTable --
  1110.  *
  1111.  *    Searches for a table associated with the window given by its
  1112.  *    pathname.  This window represents the master window of the table.
  1113.  *    Errors may occur because 1) pathname does not represent a valid
  1114.  *    Tk window or 2) the window is not associated with any table
  1115.  *    as its master window.
  1116.  *
  1117.  * Results:
  1118.  *    If a table entry exists, a pointer to the Table structure is
  1119.  *    returned. Otherwise NULL is returned and if the TCL_LEAVE_ERR_MSG
  1120.  *    flag is set, an error message is left is interp->result.
  1121.  *
  1122.  *----------------------------------------------------------------------
  1123.  */
  1124. static Table *
  1125. FindTable(interp, pathName, searchWin, flags)
  1126.     Tcl_Interp *interp;        /* Interpreter to report errors back to */
  1127.     char *pathName;        /* Path name of the master window */
  1128.     Tk_Window searchWin;    /* Main window: used to search for window
  1129.                  * associated with pathname */
  1130.     int flags;            /* If non-zero, don't reset interp->result */
  1131. {
  1132.     Tcl_HashEntry *entryPtr;
  1133.     Tk_Window tkwin;
  1134.  
  1135.     tkwin = Tk_NameToWindow(interp, pathName, searchWin);
  1136.     if (tkwin == NULL) {
  1137.     if (!(flags & TCL_LEAVE_ERR_MSG)) {
  1138.         Tcl_ResetResult(interp);
  1139.     }
  1140.     return NULL;
  1141.     }
  1142.     entryPtr = Tcl_FindHashEntry(&masterWindows, (char *)tkwin);
  1143.     if (entryPtr == NULL) {
  1144.     if (flags & TCL_LEAVE_ERR_MSG) {
  1145.         Tcl_AppendResult(interp, "no table associated with window \"",
  1146.         pathName, "\"", (char *)NULL);
  1147.     }
  1148.     return NULL;
  1149.     }
  1150.     return ((Table *)Tcl_GetHashValue(entryPtr));
  1151. }
  1152.  
  1153. /*
  1154.  *----------------------------------------------------------------------
  1155.  *
  1156.  * CreateTable --
  1157.  *
  1158.  *    This procedure creates and initializes a new Table structure
  1159.  *    with tkwin as its master window. The internal structures
  1160.  *    associated with the table are initialized.
  1161.  
  1162.  * Results:
  1163.  *    Returns the pointer to the new Table structure describing the
  1164.  *    new table geometry manager.  If an error occurred, the return
  1165.  *    value will be NULL and an error message is left in interp->result.
  1166.  *
  1167.  * Side effects:
  1168.  *    Memory is allocated and initialized, an event handler is set up
  1169.  *    to watch tkwin, etc.
  1170.  *
  1171.  *----------------------------------------------------------------------
  1172.  */
  1173. static Table *
  1174. CreateTable(interp, pathName, searchWin)
  1175.     Tcl_Interp *interp;        /* Interpreter associated with table */
  1176.     char *pathName;        /* Path name of the master window to be
  1177.                  * associated with the new table */
  1178.     Tk_Window searchWin;    /* Main window */
  1179. {
  1180.     register Table *tablePtr;
  1181.     Tk_Window tkwin;
  1182.  
  1183.     tkwin = Tk_NameToWindow(interp, pathName, searchWin);
  1184.     if (tkwin == NULL) {
  1185.     return NULL;
  1186.     }
  1187.     tablePtr = (Table *)calloc(1, sizeof(Table));
  1188.     if (tablePtr != NULL) {
  1189.     int dummy;
  1190.     Tcl_HashEntry *entryPtr;
  1191.  
  1192.     tablePtr->tkwin = tkwin;
  1193.     tablePtr->searchWin = searchWin;
  1194.     tablePtr->interp = interp;
  1195.     tablePtr->listPtr = &(tablePtr->rowSorted);
  1196.     tablePtr->flags = 0;
  1197.     tablePtr->rowSize = tablePtr->colSize = DEF_ARRAY_SIZE;
  1198.     tablePtr->numRows = tablePtr->numCols = 0;
  1199.     tablePtr->rowPtr = tablePtr->rowSpace;
  1200.     Blt_InitLinkedList(&(tablePtr->rowSorted), TCL_ONE_WORD_KEYS);
  1201.     InitPartitions(tablePtr->rowPtr, DEF_ARRAY_SIZE);
  1202.  
  1203.     tablePtr->colPtr = tablePtr->colSpace;
  1204.     Blt_InitLinkedList(&(tablePtr->colSorted), TCL_ONE_WORD_KEYS);
  1205.     InitPartitions(tablePtr->colPtr, DEF_ARRAY_SIZE);
  1206.  
  1207.     Tk_CreateEventHandler(tablePtr->tkwin, StructureNotifyMask,
  1208.         TableEventProc, (ClientData)tablePtr);
  1209.     entryPtr = Tcl_CreateHashEntry(&masterWindows, (char *)tkwin, &dummy);
  1210.     Tcl_SetHashValue(entryPtr, (ClientData)tablePtr);
  1211.     } else {
  1212.     Tcl_AppendResult(interp, "can't create table \"", pathName, "\"",
  1213.         (char *)NULL);
  1214.     }
  1215.     return (tablePtr);
  1216. }
  1217.  
  1218. /*
  1219.  *----------------------------------------------------------------------
  1220.  *
  1221.  * DestroyTable --
  1222.  *
  1223.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  1224.  *    to clean up the Table structure at a safe time (when no-one is
  1225.  *    using it anymore).
  1226.  *
  1227.  * Results:
  1228.  *    None.
  1229.  *
  1230.  * Side effects:
  1231.  *    Everything associated with the table geometry manager is freed up.
  1232.  *
  1233.  *----------------------------------------------------------------------
  1234.  */
  1235. static void
  1236. DestroyTable(clientData)
  1237.     ClientData clientData;    /* Table structure */
  1238. {
  1239.     register Table *tablePtr = (Table *)clientData;
  1240.     Blt_ListEntry *entryPtr;
  1241.     Cubicle *cubiPtr;
  1242.  
  1243.     for (entryPtr = Blt_FirstListEntry(tablePtr->listPtr);
  1244.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1245.     cubiPtr = (Cubicle *)Blt_GetListValue(entryPtr);
  1246.     cubiPtr->rowEntryPtr = cubiPtr->colEntryPtr = NULL;
  1247.     DestroyCubicle(cubiPtr);
  1248.     }
  1249.     Blt_ClearList(&(tablePtr->rowSorted));
  1250.     Blt_ClearList(&(tablePtr->colSorted));
  1251.  
  1252.     if ((tablePtr->rowPtr != NULL) &&
  1253.     (tablePtr->rowPtr != tablePtr->rowSpace)) {
  1254.     free((char *)tablePtr->rowPtr);
  1255.     }
  1256.     if ((tablePtr->colPtr != NULL) &&
  1257.     (tablePtr->colPtr != tablePtr->colSpace)) {
  1258.     free((char *)tablePtr->colPtr);
  1259.     }
  1260.     free((char *)tablePtr);
  1261. }
  1262.  
  1263. /*
  1264.  *----------------------------------------------------------------------
  1265.  *
  1266.  * InitPartitions --
  1267.  *
  1268.  *    Initializes the values of the newly created elements in
  1269.  *    the partition array.
  1270.  *
  1271.  * Results:
  1272.  *    None.
  1273.  *
  1274.  * Side effects:
  1275.  *    The elements of the array of Partition structures is
  1276.  *    initialized.
  1277.  *
  1278.  *----------------------------------------------------------------------
  1279.  */
  1280. static void
  1281. InitPartitions(partPtr, length)
  1282.     register Partition *partPtr;/* Array of partitions to be initialized */
  1283.     int length;            /* Number of elements in array */
  1284. {
  1285.     register int i;
  1286.  
  1287.     for (i = 0; i < length; i++) {
  1288.     partPtr->resize = RESIZE_BOTH;
  1289.     partPtr->reqSize.nom = partPtr->reqSize.min =
  1290.         partPtr->size = DEF_MIN_LIMIT;
  1291.     partPtr->reqSize.max = DEF_MAX_LIMIT;
  1292.     partPtr->nomSize = 0;
  1293.     partPtr->pad = 0;
  1294.     partPtr->span = 0;
  1295.     partPtr++;
  1296.     }
  1297. }
  1298.  
  1299. /*
  1300.  *----------------------------------------------------------------------
  1301.  *
  1302.  * ExtendArray --
  1303.  *
  1304.  *    Resizes the partition array to a larger size.
  1305.  *
  1306.  * Results:
  1307.  *    A pointer to the newly extended array is returned.
  1308.  *
  1309.  *----------------------------------------------------------------------
  1310.  */
  1311. static Partition *
  1312. ExtendArray(partArr, oldSize, newSize)
  1313.     Partition *partArr;        /*  */
  1314.     int oldSize, newSize;
  1315. {
  1316.     Partition *newArr;
  1317.  
  1318.     newArr = (Partition *)malloc(newSize * sizeof(Partition));
  1319.     if (newArr != NULL) {
  1320.     if (oldSize > 0) {
  1321.         memcpy((char *)newArr, (char *)partArr,
  1322.         oldSize * sizeof(Partition));
  1323.     }
  1324.     InitPartitions(&(newArr[oldSize]), (int)(newSize - oldSize));
  1325.     }
  1326.     return (newArr);
  1327. }
  1328.  
  1329. /*
  1330.  *----------------------------------------------------------------------
  1331.  *
  1332.  * AssertColumn --
  1333.  *
  1334.  *    Checks the size of the column partitions and extends the
  1335.  *    size if a larger array is needed.
  1336.  *
  1337.  * Results:
  1338.  *    Returns 1 if the column exists.  Otherwise 0 is returned and
  1339.  *    interp->result contains an error message.
  1340.  *
  1341.  * Side effects:
  1342.  *    The size of the column partition array may be extended and
  1343.  *    initialized.
  1344.  *
  1345.  *----------------------------------------------------------------------
  1346.  */
  1347. static int
  1348. AssertColumn(tablePtr, column)
  1349.     Table *tablePtr;
  1350.     int column;
  1351. {
  1352.     if (column >= tablePtr->colSize) {
  1353.     int newSize;
  1354.     Partition *newArr;
  1355.  
  1356.     if (column >= USHRT_MAX) {
  1357.         Tcl_AppendResult(tablePtr->interp, "too many columns in \"",
  1358.         Tk_PathName(tablePtr->tkwin), "\"", (char *)NULL);
  1359.         return 0;
  1360.     }
  1361.     newSize = 2 * tablePtr->colSize;
  1362.     while (newSize <= column) {
  1363.         newSize += newSize;
  1364.     }
  1365.     newArr = ExtendArray(tablePtr->colPtr, tablePtr->colSize, newSize);
  1366.     if (newArr == NULL) {
  1367.         Tcl_AppendResult(tablePtr->interp, "can't extend columns in table",
  1368.         " \"", Tk_PathName(tablePtr->tkwin), "\": ",
  1369.         Tcl_PosixError(tablePtr->interp));
  1370.         return 0;
  1371.     }
  1372.     if (tablePtr->colPtr != tablePtr->colSpace) {
  1373.         free((char *)tablePtr->colPtr);
  1374.     }
  1375.     tablePtr->colPtr = newArr;
  1376.     tablePtr->colSize = newSize;
  1377.     }
  1378.     if (column >= tablePtr->numCols) {
  1379.     tablePtr->numCols = column + 1;
  1380.     }
  1381.     return 1;
  1382. }
  1383.  
  1384. /*
  1385.  *----------------------------------------------------------------------
  1386.  *
  1387.  * AssertRow --
  1388.  *
  1389.  *    Checks the size of the row partitions and extends the
  1390.  *    size if a larger array is needed.
  1391.  *
  1392.  * Results:
  1393.  *    Returns 1 if the row exists.  Otherwise 0 is returned
  1394.  *    and interp->result contains an error message.
  1395.  *
  1396.  * Side effects:
  1397.  *    The size of the row partition array may be extended and
  1398.  *    initialized.
  1399.  *
  1400.  *----------------------------------------------------------------------
  1401.  */
  1402. static int
  1403. AssertRow(tablePtr, row)
  1404.     Table *tablePtr;
  1405.     int row;
  1406. {
  1407.     if (row >= tablePtr->rowSize) {
  1408.     register int newSize;
  1409.     Partition *newArr;
  1410.  
  1411.     if (row >= USHRT_MAX) {
  1412.         Tcl_AppendResult(tablePtr->interp, "too many rows in \"",
  1413.         Tk_PathName(tablePtr->tkwin), "\"", (char *)NULL);
  1414.         return 0;
  1415.     }
  1416.     newSize = 2 * tablePtr->rowSize;
  1417.     while (newSize <= row) {
  1418.         newSize += newSize;
  1419.     }
  1420.     newArr = ExtendArray(tablePtr->rowPtr, tablePtr->rowSize, newSize);
  1421.     if (newArr == NULL) {
  1422.         Tcl_AppendResult(tablePtr->interp, "can't extend rows in table \"",
  1423.         Tk_PathName(tablePtr->tkwin), "\": ",
  1424.         Tcl_PosixError(tablePtr->interp));
  1425.         return 0;
  1426.     }
  1427.     if (tablePtr->rowPtr != tablePtr->rowSpace) {
  1428.         free((char *)tablePtr->rowPtr);
  1429.     }
  1430.     tablePtr->rowPtr = newArr;
  1431.     tablePtr->rowSize = newSize;
  1432.     }
  1433.     if (row >= tablePtr->numRows) {
  1434.     tablePtr->numRows = row + 1;
  1435.     }
  1436.     return 1;
  1437. }
  1438.  
  1439. /*
  1440.  *----------------------------------------------------------------------
  1441.  *
  1442.  * ParseIndex --
  1443.  *
  1444.  *    Parse the entry index string and return the row and column
  1445.  *    numbers in their respective parameters.  The format of a
  1446.  *    table entry index is <row>,<column> where <row> is the row
  1447.  *    number and <column> is the column number.  Rows and columns
  1448.  *    are numbered starting from zero.
  1449.  *
  1450.  * Results:
  1451.  *    Returns a standard Tcl result.  If TCL_OK is returned, the
  1452.  *    row and column numbers are returned via rowPtr and columnPtr
  1453.  *    respectively.
  1454.  *
  1455.  *----------------------------------------------------------------------
  1456.  */
  1457. static int
  1458. ParseIndex(interp, indexStr, rowPtr, columnPtr)
  1459.     Tcl_Interp *interp;
  1460.     char *indexStr;
  1461.     int *rowPtr;
  1462.     int *columnPtr;
  1463. {
  1464.     char *columnStr, *rowStr;
  1465.     long row, column;
  1466.  
  1467.     rowStr = indexStr;
  1468.     columnStr = strchr(indexStr, ',');
  1469.     if (columnStr == NULL) {
  1470.     Tcl_AppendResult(interp, "invalid index \"", indexStr,
  1471.         "\": should be \"row,column\"", (char *)NULL);
  1472.     return TCL_ERROR;
  1473.  
  1474.     }
  1475.     *columnStr++ = '\0';
  1476.     if ((Tcl_ExprLong(interp, rowStr, &row) != TCL_OK) ||
  1477.     (Tcl_ExprLong(interp, columnStr, &column) != TCL_OK)) {
  1478.     return TCL_ERROR;
  1479.     }
  1480.     if ((row < 0) || (row > USHRT_MAX)) {
  1481.     Tcl_AppendResult(interp, "row index \"", rowStr,
  1482.         "\" is out of range", (char *)NULL);
  1483.     return TCL_ERROR;
  1484.     }
  1485.     if ((column < 0) || (column > USHRT_MAX)) {
  1486.     Tcl_AppendResult(interp, "column index \"", columnStr,
  1487.         "\" is out of range", (char *)NULL);
  1488.     return TCL_ERROR;
  1489.     }
  1490.     *rowPtr = (int)row;
  1491.     *columnPtr = (int)column;
  1492.     return TCL_OK;
  1493. }
  1494.  
  1495. /*
  1496.  *----------------------------------------------------------------------
  1497.  *
  1498.  * MakeSlaveKey --
  1499.  *
  1500.  *    Creates a one word key out of the two 16 bit row and column
  1501.  *    indices.
  1502.  *
  1503.  *----------------------------------------------------------------------
  1504.  */
  1505. static unsigned int
  1506. MakeSlaveKey(row, column)
  1507.     int row;
  1508.     int column;
  1509. {
  1510. #ifndef cray
  1511.     SlaveKey key;
  1512.  
  1513.     key.position.row = (unsigned short)row;
  1514.     key.position.column = (unsigned short)column;
  1515.     return (key.index);
  1516. #else
  1517.     unsigned int index;
  1518.  
  1519.     index = (row & 0xffffffff);
  1520.     index |= ((column & 0xffffffff) << 32);
  1521.     return (index);
  1522. #endif /*cray*/
  1523. }
  1524.  
  1525. /*
  1526.  *----------------------------------------------------------------------
  1527.  *
  1528.  * LinkRowEntry --
  1529.  *
  1530.  *    Links new list entry into list of row-sorted cubicles.
  1531.  *    It's important to maintain this list, because the size of
  1532.  *    the row parititions is determined in order of this list.
  1533.  *
  1534.  * Results:
  1535.  *    None.
  1536.  *
  1537.  * Side Effects:
  1538.  *    Entry is linked into the list of row-sorted cubicles.
  1539.  *
  1540.  *----------------------------------------------------------------------
  1541.  */
  1542. static void
  1543. LinkRowEntry(newPtr)
  1544.     Cubicle *newPtr;
  1545. {
  1546.     register int delta;
  1547.     Cubicle *cubiPtr;
  1548.     Table *tablePtr;
  1549.     register Blt_ListEntry *entryPtr;
  1550.  
  1551.     tablePtr = newPtr->tablePtr;
  1552.     for (entryPtr = Blt_FirstListEntry(&(tablePtr->rowSorted));
  1553.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1554.     cubiPtr = (Cubicle *)Blt_GetListValue(entryPtr);
  1555.     delta = newPtr->rowSpan - cubiPtr->rowSpan;
  1556.     if (delta < 0) {
  1557.         break;
  1558.     } else if (delta == 0) {
  1559.         delta = newPtr->rowIndex - cubiPtr->rowIndex;
  1560.         if (delta > 0) {
  1561.         break;
  1562.         } else if (delta == 0) {
  1563.         delta = newPtr->colIndex - cubiPtr->colIndex;
  1564.         if (delta > 0) {
  1565.             break;
  1566.         }
  1567.         }
  1568.     }
  1569.     }
  1570.     if (entryPtr == NULL) {
  1571.     Blt_LinkListAfter(&(tablePtr->rowSorted), newPtr->rowEntryPtr,
  1572.         entryPtr);
  1573.     } else {
  1574.     Blt_LinkListBefore(&(tablePtr->rowSorted), newPtr->rowEntryPtr,
  1575.         entryPtr);
  1576.     }
  1577. }
  1578.  
  1579. /*
  1580.  *----------------------------------------------------------------------
  1581.  *
  1582.  * LinkColumnEntry --
  1583.  *
  1584.  *    Links new list entry into list of column-sorted cubicles.
  1585.  *    It's important to maintain this list, because the size of
  1586.  *    the column parititions is determined in order of this list.
  1587.  *
  1588.  * Results:
  1589.  *    None.
  1590.  *
  1591.  * Side Effects:
  1592.  *    Entry is linked into the list of column-sorted cubicles.
  1593.  *
  1594.  *----------------------------------------------------------------------
  1595.  */
  1596. static void
  1597. LinkColumnEntry(newPtr)
  1598.     Cubicle *newPtr;
  1599. {
  1600.     register int delta;
  1601.     Cubicle *cubiPtr;
  1602.     Table *tablePtr;
  1603.     register Blt_ListEntry *entryPtr;
  1604.  
  1605.     tablePtr = newPtr->tablePtr;
  1606.     for (entryPtr = Blt_FirstListEntry(&(tablePtr->colSorted));
  1607.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1608.     cubiPtr = (Cubicle *)Blt_GetListValue(entryPtr);
  1609.     delta = newPtr->colSpan - cubiPtr->colSpan;
  1610.     if (delta < 0) {
  1611.         break;
  1612.     } else if (delta == 0) {
  1613.         delta = newPtr->colIndex - cubiPtr->colIndex;
  1614.         if (delta > 0) {
  1615.         break;
  1616.         } else if (delta == 0) {
  1617.         delta = newPtr->rowIndex - cubiPtr->rowIndex;
  1618.         if (delta > 0) {
  1619.             break;
  1620.         }
  1621.         }
  1622.     }
  1623.     }
  1624.     if (entryPtr == NULL) {
  1625.     Blt_LinkListAfter(&(tablePtr->colSorted), newPtr->colEntryPtr,
  1626.         entryPtr);
  1627.     } else {
  1628.     Blt_LinkListBefore(&(tablePtr->colSorted), newPtr->colEntryPtr,
  1629.         entryPtr);
  1630.     }
  1631. }
  1632.  
  1633. /*
  1634.  *----------------------------------------------------------------------
  1635.  *
  1636.  * AddWindowToTable --
  1637.  *
  1638.  *    Adds the given window as a slave window into the table at
  1639.  *    a given row and column position.  The window may already
  1640.  *    exist as a slave window in the table. If tkwin is a slave
  1641.  *    of another table, it's an error.
  1642.  *
  1643.  *    The new window is inserted into both the slave window hash
  1644.  *    table (this is used to locate the information associated with
  1645.  *    the slave window without searching each table) and cubicle
  1646.  *    lists which are sorted in order of column and row spans.
  1647.  *
  1648.  * Results:
  1649.  *    Returns a standard Tcl result.  If an error occurred, TCL_ERROR
  1650.  *    is returned and an error message is left in interp->result.
  1651.  *
  1652.  * Side Effects:
  1653.  *    The table is re-computed and arranged at the next idle point.
  1654.  *
  1655.  *----------------------------------------------------------------------
  1656.  */
  1657. static int
  1658. AddWindowToTable(tablePtr, tkwin, row, column, argc, argv)
  1659.     Table *tablePtr;
  1660.     Tk_Window tkwin;
  1661.     int row, column;
  1662.     int argc;
  1663.     char **argv;
  1664. {
  1665.     register Cubicle *cubiPtr;
  1666.     Blt_ListEntry *entryPtr;
  1667.     int result = TCL_OK;
  1668.     unsigned int key;
  1669.  
  1670.     cubiPtr = FindCubicle(tablePtr->interp, tkwin, 0);
  1671.     if (cubiPtr != NULL) {
  1672.     /*
  1673.      * Make sure the window is currently being managed by this table.
  1674.      */
  1675.     if (cubiPtr->tablePtr != tablePtr) {
  1676.         Tcl_AppendResult(tablePtr->interp, "\"", Tk_PathName(tkwin),
  1677.         "\" is already managed by \"", Tk_PathName(cubiPtr->tkwin),
  1678.         "\"", (char *)NULL);
  1679.         return TCL_ERROR;
  1680.     }
  1681.     /*
  1682.      * Remove the cubicle from both row and column lists.  It will
  1683.      * be re-inserted into the table at the new position
  1684.      */
  1685.     Blt_DeleteListEntry(&(tablePtr->rowSorted), cubiPtr->rowEntryPtr);
  1686.     Blt_DeleteListEntry(&(tablePtr->colSorted), cubiPtr->colEntryPtr);
  1687.     } else {
  1688.     cubiPtr = CreateCubicle(tablePtr, tkwin);
  1689.     if (cubiPtr == NULL) {
  1690.         return TCL_ERROR;
  1691.     }
  1692.     }
  1693.     /*
  1694.      * If there's already a slave window at this position in the
  1695.      * table, unmap it and remove the cubicle.
  1696.      */
  1697.     key = MakeSlaveKey(row, column);
  1698.     entryPtr = Blt_FindListEntry(tablePtr->listPtr, (char *)key);
  1699.     if (entryPtr != NULL) {
  1700.     Cubicle *oldPtr;
  1701.  
  1702.     oldPtr = (Cubicle *)Blt_GetListValue(entryPtr);
  1703.     if (Tk_IsMapped(oldPtr->tkwin)) {
  1704.         Tk_UnmapWindow(oldPtr->tkwin);
  1705.     }
  1706.     DestroyCubicle(oldPtr);
  1707.     }
  1708.     cubiPtr->colIndex = column;
  1709.     cubiPtr->rowIndex = row;
  1710.     if (!AssertRow(tablePtr, cubiPtr->rowIndex) ||
  1711.     !AssertColumn(tablePtr, cubiPtr->colIndex)) {
  1712.     return TCL_ERROR;
  1713.     }
  1714.     if (argc > 0) {
  1715.     result = Tk_ConfigureWidget(tablePtr->interp, cubiPtr->tkwin,
  1716.         cubicleConfigSpecs, argc, argv, (char *)cubiPtr,
  1717.         TK_CONFIG_ARGV_ONLY);
  1718.     }
  1719.     if ((cubiPtr->colSpan < 1) || (cubiPtr->rowSpan < 1)) {
  1720.     Tcl_AppendResult(tablePtr->interp, "invalid spans specified for \"",
  1721.         Tk_PathName(tkwin), "\"", (char *)NULL);
  1722.     DestroyCubicle(cubiPtr);
  1723.     return TCL_ERROR;
  1724.     }
  1725.     /*
  1726.      * Insert the cubicle into both the row and column layout lists
  1727.      */
  1728.     cubiPtr->rowEntryPtr = Blt_CreateListEntry((char *)key);
  1729.     Blt_SetListValue(cubiPtr->rowEntryPtr, cubiPtr);
  1730.     LinkRowEntry(cubiPtr);
  1731.  
  1732.     cubiPtr->colEntryPtr = Blt_CreateListEntry((char *)key);
  1733.     Blt_SetListValue(cubiPtr->colEntryPtr, cubiPtr);
  1734.     LinkColumnEntry(cubiPtr);
  1735.  
  1736.     if (!AssertColumn(tablePtr, cubiPtr->colIndex + cubiPtr->colSpan - 1) ||
  1737.     !AssertRow(tablePtr, cubiPtr->rowIndex + cubiPtr->rowSpan - 1)) {
  1738.     return TCL_ERROR;
  1739.     }
  1740.     return (result);
  1741. }
  1742.  
  1743. /*
  1744.  *----------------------------------------------------------------------
  1745.  *
  1746.  * ManageWindows --
  1747.  *
  1748.  *    Processes an argv/argc list of table entries to add and
  1749.  *    configure new slave windows into the table.  A table entry
  1750.  *    consists of the window path name, table index, and optional
  1751.  *    configuration options.  The first argument in the argv list
  1752.  *    is the name of the table.  If no table exists for the given
  1753.  *    window, a new one is created.
  1754.  *
  1755.  * Results:
  1756.  *    Returns a standard Tcl result.  If an error occurred, TCL_ERROR
  1757.  *    is returned and an error message is left in interp->result.
  1758.  *
  1759.  * Side Effects:
  1760.  *    Memory is allocated, a new master table is possibly created, etc.
  1761.  *    The table is re-computed and arranged at the next idle point.
  1762.  *
  1763.  *----------------------------------------------------------------------
  1764.  */
  1765. static int
  1766. ManageWindows(tablePtr, interp, argc, argv)
  1767.     Table *tablePtr;        /* Table to manage new slave windows */
  1768.     Tcl_Interp *interp;        /* Interpreter to report errors back to */
  1769.     int argc;            /*  */
  1770.     char **argv;        /* List of slave windows, indices, and
  1771.                  * options */
  1772. {
  1773.     char *savePtr;
  1774.     int row, column;
  1775.     register int i, count;
  1776.     Tk_Window tkwin;
  1777.  
  1778.     for (i = 0; i < argc; /*empty*/ ) {
  1779.     tkwin = Tk_NameToWindow(interp, argv[i], tablePtr->tkwin);
  1780.     if (tkwin == NULL) {
  1781.         return TCL_ERROR;
  1782.     }
  1783.     if ((i + 1) == argc) {
  1784.         Tcl_AppendResult(interp, "missing index argument for \"", argv[i],
  1785.         "\"", (char *)NULL);
  1786.         return TCL_ERROR;
  1787.     }
  1788.     if (ParseIndex(interp, argv[i + 1], &row, &column) != TCL_OK) {
  1789.         return TCL_ERROR;    /* Invalid row,column index */
  1790.     }
  1791.     /*
  1792.      * Find the end this entry's option-value pairs, first
  1793.      * skipping over the slave window's pathname and table index
  1794.      * arguments.
  1795.      */
  1796.     i += 2;
  1797.     for (count = i; count < argc; count += 2) {
  1798.         if (argv[count][0] != '-') {
  1799.         break;
  1800.         }
  1801.     }
  1802.     savePtr = argv[count];
  1803.     argv[count] = NULL;
  1804.     if (AddWindowToTable(tablePtr, tkwin, row, column, count - i,
  1805.         argv + i) != TCL_OK) {
  1806.         return TCL_ERROR;
  1807.     }
  1808.     argv[count] = savePtr;
  1809.     i = count;
  1810.     }
  1811.     /* If all went well, arrange for the table layout to be performed. */
  1812.     tablePtr->flags |= REQUEST_LAYOUT;
  1813.     if (!(tablePtr->flags & ARRANGE_PENDING)) {
  1814.     tablePtr->flags |= ARRANGE_PENDING;
  1815.     Tk_DoWhenIdle(ArrangeTable, (ClientData)tablePtr);
  1816.     }
  1817.     interp->result = Tk_PathName(tablePtr->tkwin);
  1818.     return TCL_OK;
  1819. }
  1820.  
  1821. /*
  1822.  *----------------------------------------------------------------------
  1823.  *
  1824.  * SlaveNames --
  1825.  *
  1826.  *    Returns a list of all the pathnames of the slaves window
  1827.  *    managed by a table geometry manager.  The table is given
  1828.  *    by the path name of a master window associated with the table.
  1829.  *    pathnames of all slave windows managed by this table.
  1830.  *
  1831.  * Results:
  1832.  *    Returns a standard Tcl result.  If no error occurred, TCL_OK
  1833.  *    is returned and a list of slave window path names is left in
  1834.  *    interp->result.
  1835.  *
  1836.  *----------------------------------------------------------------------
  1837.  */
  1838. /*ARGSUSED*/
  1839. static int
  1840. SlaveNames(tablePtr, interp, argc, argv)
  1841.     Table *tablePtr;
  1842.     Tcl_Interp *interp;        /* Interpreter to return list of names to */
  1843.     int argc;            /* Number of arguments */
  1844.     char **argv;        /* Contains 1-2 arguments: pathname of master
  1845.                  * window associated with the table and search
  1846.                  * pattern */
  1847. {
  1848.     Blt_ListEntry *entryPtr;
  1849.     Cubicle *cubiPtr;
  1850.  
  1851.     for (entryPtr = Blt_FirstListEntry(tablePtr->listPtr);
  1852.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1853.     cubiPtr = (Cubicle *)Blt_GetListValue(entryPtr);
  1854.     if ((argc != 2) ||
  1855.         (Tcl_StringMatch(Tk_PathName(cubiPtr->tkwin), argv[1]))) {
  1856.         Tcl_AppendElement(interp, Tk_PathName(cubiPtr->tkwin));
  1857.     }
  1858.     }
  1859.     return TCL_OK;
  1860. }
  1861.  
  1862. /*
  1863.  *----------------------------------------------------------------------
  1864.  *
  1865.  * MasterNames --
  1866.  *
  1867.  *    Returns a list of all the pathnames of the master windows
  1868.  *    managed by a table geometry manager matching a given pattern.
  1869.  *    If no pattern is present (argc == 0), all pathnames are returned.
  1870.  *
  1871.  * Results:
  1872.  *    Returns a standard Tcl result.  If no error occurred, TCL_OK
  1873.  *    is returned and a list of slave window path names is left in
  1874.  *    interp->result.
  1875.  *
  1876.  *----------------------------------------------------------------------
  1877.  */
  1878. /*ARGSUSED*/
  1879. static int
  1880. MasterNames(clientData, interp, argc, argv)
  1881.     ClientData clientData;    /* Main window of the interpreter: Used to
  1882.                  * search for windows in the hierarchy */
  1883.     Tcl_Interp *interp;        /* Interpreter to return list of names to */
  1884.     int argc;
  1885.     char **argv;        /* Contains 0-1 arguments: search pattern */
  1886. {
  1887.     Tk_Window searchWin = (Tk_Window)clientData;
  1888.     Tcl_HashEntry *entryPtr;
  1889.     Tcl_HashSearch cursor;
  1890.     register Table *tablePtr;
  1891.  
  1892.     for (entryPtr = Tcl_FirstHashEntry(&masterWindows, &cursor);
  1893.     entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&cursor)) {
  1894.     tablePtr = (Table *)Tcl_GetHashValue(entryPtr);
  1895.     if ((tablePtr->searchWin == searchWin) && ((argc != 1) ||
  1896.         (Tcl_StringMatch(Tk_PathName(tablePtr->tkwin), argv[0])))) {
  1897.         Tcl_AppendElement(interp, Tk_PathName(tablePtr->tkwin));
  1898.     }
  1899.     }
  1900.     return TCL_OK;
  1901. }
  1902.  
  1903. /*
  1904.  *----------------------------------------------------------------------
  1905.  *
  1906.  * PartitionInfo --
  1907.  *
  1908.  *
  1909.  *    Returns a list of row and column partition information.
  1910.  *    The information consists of the minimum size, current size,
  1911.  *    maximum size, and resize flag option.  The format should
  1912.  *    be suitable to pass back to the "configure" option.
  1913.  *
  1914.  * Results:
  1915.  *    Returns a standard Tcl result.
  1916.  *
  1917.  *----------------------------------------------------------------------
  1918.  */
  1919. /* ARGSUSED */
  1920. static int
  1921. PartitionInfo(tablePtr, interp, type, argc, argv)
  1922.     Table *tablePtr;
  1923.     Tcl_Interp *interp;
  1924.     PartitionTypes type;
  1925.     int argc;
  1926.     char **argv;
  1927. {
  1928.     long index;
  1929.     Partition *partPtr;
  1930.     char **indexArr;
  1931.     char buf[BUFSIZ];
  1932.     char string[200];
  1933.     char *format;
  1934.     int numPartitions, maxIndex;
  1935.     int queryAll;
  1936.     int result = TCL_ERROR;
  1937.     register int i;
  1938.  
  1939.     if (Tcl_SplitList(interp, argv[0], &numPartitions, &indexArr) != TCL_OK) {
  1940.     return TCL_ERROR;    /* Can't split list */
  1941.     }
  1942.     maxIndex = 0;        /* Inhibit compiler warnings */
  1943.     partPtr = NULL;
  1944.     format = NULL;
  1945.     if ((numPartitions == 1) &&
  1946.     (indexArr[0][0] == 'a') && (strcmp(indexArr[0], "all") == 0)) {
  1947.     numPartitions = NUMENTRIES(tablePtr, type);
  1948.     queryAll = TRUE;
  1949.     } else {
  1950.     maxIndex = NUMENTRIES(tablePtr, type);
  1951.     queryAll = FALSE;
  1952.     }
  1953.     for (i = 0; i < numPartitions; i++) {
  1954.     if (queryAll) {
  1955.         index = i;
  1956.     } else {
  1957.         if (Tcl_ExprLong(interp, indexArr[i], &index) != TCL_OK) {
  1958.         goto error;
  1959.         }
  1960.         if ((index < 0) || (index >= maxIndex)) {
  1961.         Tcl_AppendResult(interp, "index \"", indexArr[i],
  1962.             "\" is out of range", (char *)NULL);
  1963.         goto error;
  1964.         }
  1965.     }
  1966.     if (type == ROW_PARTITION_TYPE) {
  1967.         partPtr = tablePtr->rowPtr + index;
  1968.         format = "%d -resize %s -height {%s} -pady %d";
  1969.     } else if (type == COLUMN_PARTITION_TYPE) {
  1970.         partPtr = tablePtr->colPtr + index;
  1971.         format = "%d -resize %s -width {%s} -padx %d";
  1972.     }
  1973.     LimitsToString(partPtr->reqSize.min, partPtr->reqSize.max,
  1974.         partPtr->reqSize.nom, string);
  1975.     sprintf(buf, format, index, resizeStrings[(int)partPtr->resize],
  1976.         string, partPtr->pad);
  1977.     Tcl_AppendElement(tablePtr->interp, buf);
  1978.     }
  1979.     result = TCL_OK;
  1980.   error:
  1981.     free((char *)indexArr);
  1982.     return result;
  1983. }
  1984.  
  1985. /*
  1986.  *----------------------------------------------------------------------
  1987.  *
  1988.  * PartitionSizes --
  1989.  *
  1990.  *
  1991.  *    Returns the sizes of the named partitions (rows or columns)
  1992.  *
  1993.  * Results:
  1994.  *    Returns a standard Tcl result.
  1995.  *
  1996.  *----------------------------------------------------------------------
  1997.  */
  1998. /* ARGSUSED */
  1999. static int
  2000. PartitionSizes(tablePtr, interp, type, indexStr)
  2001.     Table *tablePtr;
  2002.     Tcl_Interp *interp;
  2003.     PartitionTypes type;
  2004.     char *indexStr;
  2005. {
  2006.     long index;
  2007.     Partition *partPtr;
  2008.     char **indexArr;
  2009.     char string[200];
  2010.     int numPartitions, maxIndex;
  2011.     int queryAll;
  2012.     int result = TCL_ERROR;
  2013.     register int i;
  2014.  
  2015.     if (Tcl_SplitList(interp, indexStr, &numPartitions, &indexArr) != TCL_OK) {
  2016.     return TCL_ERROR;    /* Can't split list */
  2017.     }
  2018.     maxIndex = 0;        /* Suppress compiler warning */
  2019.     if ((numPartitions == 1) &&
  2020.     (indexArr[0][0] == 'a') && (strcmp(indexArr[0], "all") == 0)) {
  2021.     numPartitions = NUMENTRIES(tablePtr, type);
  2022.     queryAll = TRUE;
  2023.     } else {
  2024.     maxIndex = NUMENTRIES(tablePtr, type);
  2025.     queryAll = FALSE;
  2026.     }
  2027.     partPtr = ((type == ROW_PARTITION_TYPE)
  2028.     ? tablePtr->rowPtr : tablePtr->colPtr);
  2029.     for (i = 0; i < numPartitions; i++) {
  2030.     if (queryAll) {
  2031.         index = i;
  2032.     } else {
  2033.         if (Tcl_ExprLong(interp, indexArr[i], &index) != TCL_OK) {
  2034.         goto error;
  2035.         }
  2036.         if ((index < 0) || (index >= maxIndex)) {
  2037.         Tcl_AppendResult(interp, "index \"", indexArr[i],
  2038.             "\" is out of range", (char *)NULL);
  2039.         goto error;
  2040.         }
  2041.     }
  2042.     sprintf(string, "%d", partPtr[index].size);
  2043.     Tcl_AppendElement(tablePtr->interp, string);
  2044.     }
  2045.     result = TCL_OK;
  2046.   error:
  2047.     free((char *)indexArr);
  2048.     return result;
  2049. }
  2050.  
  2051. /*
  2052.  *----------------------------------------------------------------------
  2053.  *
  2054.  * LayoutTable --
  2055.  *
  2056.  *    Forces layout of the table geometry manager.  This is useful
  2057.  *    to get the geometry manager to calculate the normal width and
  2058.  *    height of each row and column.  Otherwise, one needs to
  2059.  *    withdraw the master window, run "update", and then query to
  2060.  *    geometry manager.
  2061.  *
  2062.  * Results:
  2063.  *    Returns a standard Tcl result.  If no error occurred, TCL_OK
  2064.  *    is returned. Otherwise, TCL_ERROR and a error message is left in
  2065.  *    interp->result.
  2066.  *
  2067.  *----------------------------------------------------------------------
  2068.  */
  2069. /*ARGSUSED*/
  2070. static int
  2071. LayoutTable(clientData, interp, name)
  2072.     ClientData clientData;    /* Main window of the interpreter: Used to
  2073.                  * search for windows in the hierarchy */
  2074.     Tcl_Interp *interp;        /* Interpreter to report errors to */
  2075.     char *name;            /* Path name of master window associated with
  2076.                  * the table */
  2077. {
  2078.     Tk_Window searchWin = (Tk_Window)clientData;
  2079.     Table *tablePtr;
  2080.  
  2081.     tablePtr = FindTable(interp, name, searchWin, TCL_LEAVE_ERR_MSG);
  2082.     if (tablePtr == NULL) {
  2083.     return TCL_ERROR;
  2084.     }
  2085.     tablePtr->flags |= REQUEST_LAYOUT;
  2086.     if (!(tablePtr->flags & ARRANGE_PENDING)) {
  2087.     tablePtr->flags |= ARRANGE_PENDING;
  2088.     ArrangeTable((ClientData)tablePtr);
  2089.     }
  2090.     return TCL_OK;
  2091. }
  2092.  
  2093. /*
  2094.  *----------------------------------------------------------------------
  2095.  *
  2096.  * SlaveInfo --
  2097.  *
  2098.  *    Returns the name, position and options of a slave in the table.
  2099.  *
  2100.  * Results:
  2101.  *    Returns a standard Tcl result.  A list of the slave window
  2102.  *    attributes is left in interp->result.
  2103.  *
  2104.  *----------------------------------------------------------------------
  2105.  */
  2106. /*ARGSUSED*/
  2107. static int
  2108. SlaveInfo(clientData, interp, name)
  2109.     ClientData clientData;
  2110.     Tcl_Interp *interp;
  2111.     char *name;
  2112. {
  2113.     Tk_Window searchWin = (Tk_Window)clientData;
  2114.     Tk_Window tkwin;
  2115.     Cubicle *cubiPtr;
  2116.     char string[200];
  2117.  
  2118.     tkwin = Tk_NameToWindow(interp, name, searchWin);
  2119.     if (tkwin == NULL) {
  2120.     return TCL_ERROR;
  2121.     }
  2122.     cubiPtr = FindCubicle(interp, tkwin, TCL_LEAVE_ERR_MSG);
  2123.     if (cubiPtr == NULL) {
  2124.     return TCL_ERROR;
  2125.     }
  2126.     sprintf(string, " %s %d,%d", Tk_PathName(cubiPtr->tkwin),
  2127.     cubiPtr->rowIndex, cubiPtr->colIndex);
  2128.     Tcl_AppendResult(interp, string, (char *)NULL);
  2129.     sprintf(string, " -ipadx %d -ipady %d -padx %d -pady %d", cubiPtr->ipadX,
  2130.     cubiPtr->ipadY, cubiPtr->padX, cubiPtr->padY);
  2131.     Tcl_AppendResult(interp, string, (char *)NULL);
  2132.     sprintf(string, " -rowspan %d -columnspan %d", cubiPtr->rowSpan,
  2133.     cubiPtr->colSpan);
  2134.     Tcl_AppendResult(interp, string, (char *)NULL);
  2135.     Tcl_AppendResult(interp, " -anchor ", Tk_NameOfAnchor(cubiPtr->anchor),
  2136.     " -fill ", fillStrings[(int)cubiPtr->fill], (char *)NULL);
  2137.     LimitsToString(cubiPtr->reqWidth.min, cubiPtr->reqWidth.max,
  2138.     cubiPtr->reqWidth.nom, string);
  2139.     Tcl_AppendResult(interp, " -reqwidth {", string, "}", (char *)NULL);
  2140.     LimitsToString(cubiPtr->reqHeight.min, cubiPtr->reqHeight.max,
  2141.     cubiPtr->reqHeight.nom, string);
  2142.     Tcl_AppendResult(interp, " -reqheight {", string, "}", (char *)NULL);
  2143.     return TCL_OK;
  2144. }
  2145.  
  2146. /*
  2147.  *----------------------------------------------------------------------
  2148.  *
  2149.  * ConfigurePartition --
  2150.  *
  2151.  *    This procedure is called to process an argv/argc list in order
  2152.  *    to configure a row or column in the table geometry manager.
  2153.  *
  2154.  * Results:
  2155.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  2156.  *    returned, then interp->result contains an error message.
  2157.  *
  2158.  * Side effects:
  2159.  *    Partition configuration options (bounds, resize flags, etc)
  2160.  *    get set.  New partitions may be created as necessary. The table
  2161.  *    is recalculated and arranged at the next idle point.
  2162.  *
  2163.  *----------------------------------------------------------------------
  2164.  */
  2165. static int
  2166. ConfigurePartition(tablePtr, interp, type, argc, argv)
  2167.     Table *tablePtr;
  2168.     Tcl_Interp *interp;
  2169.     PartitionTypes type;
  2170.     int argc;
  2171.     char **argv;
  2172. {
  2173.     int index;
  2174.     Partition *partPtr;
  2175.     Tk_ConfigSpec *configSpecsPtr;
  2176.     char **indexArr;
  2177.     int numPartitions;
  2178.     int configureAll;
  2179.     int result = TCL_ERROR;
  2180.     register int i;
  2181.  
  2182.     if (Tcl_SplitList(interp, argv[0], &numPartitions, &indexArr) != TCL_OK) {
  2183.     return TCL_ERROR;    /* Can't split list */
  2184.     }
  2185.     configSpecsPtr = partConfigSpecs[(int)type];
  2186.     configureAll = FALSE;
  2187.  
  2188.     partPtr = NULL;
  2189.     if ((numPartitions == 1) &&
  2190.     (indexArr[0][0] == 'a') && (strcmp(indexArr[0], "all") == 0)) {
  2191.     numPartitions = NUMENTRIES(tablePtr, type);
  2192.     configureAll = TRUE;
  2193.     }
  2194.     for (i = 0; i < numPartitions; i++) {
  2195.     if (configureAll) {
  2196.         index = i;
  2197.     } else {
  2198.         long int value;
  2199.  
  2200.         if (Tcl_ExprLong(interp, indexArr[i], &value) != TCL_OK) {
  2201.         goto error;
  2202.         }
  2203.         index = (int)value;
  2204.         if ((index < 0) || (index > USHRT_MAX)) {
  2205.         Tcl_AppendResult(interp, "index \"", indexArr[i],
  2206.             "\" is out of range", (char *)NULL);
  2207.         goto error;
  2208.         }
  2209.     }
  2210.     if (type == ROW_PARTITION_TYPE) {
  2211.         if (!AssertRow(tablePtr, index)) {
  2212.         goto error;
  2213.         }
  2214.         partPtr = tablePtr->rowPtr + index;
  2215.     } else if (type == COLUMN_PARTITION_TYPE) {
  2216.         if (!AssertColumn(tablePtr, index)) {
  2217.         goto error;
  2218.         }
  2219.         partPtr = tablePtr->colPtr + index;
  2220.     }
  2221.     if (argc == 1) {
  2222.         free((char *)indexArr);
  2223.         return (Tk_ConfigureInfo(interp, tablePtr->tkwin, configSpecsPtr,
  2224.             (char *)partPtr, (char *)NULL, 0));
  2225.     } else if (argc == 2) {
  2226.         free((char *)indexArr);
  2227.         return (Tk_ConfigureInfo(interp, tablePtr->tkwin, configSpecsPtr,
  2228.             (char *)partPtr, argv[1], 0));
  2229.     }
  2230.     if (Tk_ConfigureWidget(interp, tablePtr->tkwin, configSpecsPtr,
  2231.         argc - 1, argv + 1, (char *)partPtr,
  2232.         TK_CONFIG_ARGV_ONLY) != TCL_OK) {
  2233.         goto error;
  2234.     }
  2235.     }
  2236.     tablePtr->flags |= REQUEST_LAYOUT;
  2237.     if (!(tablePtr->flags & ARRANGE_PENDING)) {
  2238.     tablePtr->flags |= ARRANGE_PENDING;
  2239.     Tk_DoWhenIdle(ArrangeTable, (ClientData)tablePtr);
  2240.     }
  2241.     result = TCL_OK;
  2242.   error:
  2243.     free((char *)indexArr);
  2244.     return result;
  2245. }
  2246.  
  2247. /*
  2248.  *----------------------------------------------------------------------
  2249.  *
  2250.  * ForgetWindow --
  2251.  *
  2252.  *    Processes an argv/argc list of slave window names and  purges
  2253.  *    their entries from their respective tables.  The windows are
  2254.  *    unmapped and the tables are rearranged at the next idle point.
  2255.  *    Note that all the named slave windows do not need to exist in
  2256.  *    the same table.
  2257.  *
  2258.  * Results:
  2259.  *    Returns a standard Tcl result.  If an error occurred, TCL_ERROR
  2260.  *    is returned and an error message is left in interp->result.
  2261.  *
  2262.  * Side Effects:
  2263.  *    Memory is deallocated (the cubicle is destroyed), etc.
  2264.  *    The affected tables are is re-computed and arranged at the next
  2265.  *    idle point.
  2266.  *
  2267.  *----------------------------------------------------------------------
  2268.  */
  2269.  
  2270. static int
  2271. ForgetWindow(clientData, interp, argc, argv)
  2272.     ClientData clientData;
  2273.     Tcl_Interp *interp;
  2274.     int argc;
  2275.     char **argv;
  2276. {
  2277.     Cubicle *cubiPtr;
  2278.     register int i;
  2279.     Tk_Window tkwin;
  2280.  
  2281.     for (i = 0; i < argc; i++) {
  2282.     tkwin = Tk_NameToWindow(interp, argv[i], (Tk_Window)clientData);
  2283.     if (tkwin == NULL) {
  2284.         return TCL_ERROR;
  2285.     }
  2286.     cubiPtr = FindCubicle(interp, tkwin, TCL_LEAVE_ERR_MSG);
  2287.     if (cubiPtr == NULL) {
  2288.         return TCL_ERROR;
  2289.     }
  2290.     if (Tk_IsMapped(cubiPtr->tkwin)) {
  2291.         Tk_UnmapWindow(cubiPtr->tkwin);
  2292.     }
  2293.     /*
  2294.      * Arrange for the call back here because not all the named
  2295.      * slave windows may belong to the same table.
  2296.      */
  2297.     cubiPtr->tablePtr->flags |= REQUEST_LAYOUT;
  2298.     if (!(cubiPtr->tablePtr->flags & ARRANGE_PENDING)) {
  2299.         cubiPtr->tablePtr->flags |= ARRANGE_PENDING;
  2300.         Tk_DoWhenIdle(ArrangeTable, (ClientData)cubiPtr->tablePtr);
  2301.     }
  2302.     DestroyCubicle(cubiPtr);
  2303.     }
  2304.     return TCL_OK;
  2305. }
  2306.  
  2307. /*
  2308.  * -----------------------------------------------------------------
  2309.  *
  2310.  * TranslateAnchor --
  2311.  *
  2312.  *     Translate the coordinates of a given bounding box based
  2313.  *    upon the anchor specified.  The anchor indicates where
  2314.  *    the given xy position is in relation to the bounding box.
  2315.  *
  2316.  *          nw --- n --- ne
  2317.  *          |            |     x,y ---+
  2318.  *          w   center   e      |     |
  2319.  *          |            |      +-----+
  2320.  *          sw --- s --- se
  2321.  *
  2322.  * Results:
  2323.  *    The translated coordinates of the bounding box are returned.
  2324.  *
  2325.  * -----------------------------------------------------------------
  2326.  */
  2327. static XPoint
  2328. TranslateAnchor(deltaX, deltaY, anchor)
  2329.     int deltaX, deltaY;        /* Difference between outer and inner regions
  2330.                  */
  2331.     Tk_Anchor anchor;        /* Direction of the anchor */
  2332. {
  2333.     XPoint newPt;
  2334.  
  2335.     newPt.x = newPt.y = 0;
  2336.     switch (anchor) {
  2337.     case TK_ANCHOR_NW:        /* Upper left corner */
  2338.     break;
  2339.     case TK_ANCHOR_W:        /* Left center */
  2340.     newPt.y = (deltaY / 2);
  2341.     break;
  2342.     case TK_ANCHOR_SW:        /* Lower left corner */
  2343.     newPt.y = deltaY;
  2344.     break;
  2345.     case TK_ANCHOR_N:        /* Top center */
  2346.     newPt.x = (deltaX / 2);
  2347.     break;
  2348.     case TK_ANCHOR_CENTER:    /* Centered */
  2349.     newPt.x = (deltaX / 2);
  2350.     newPt.y = (deltaY / 2);
  2351.     break;
  2352.     case TK_ANCHOR_S:        /* Bottom center */
  2353.     newPt.x = (deltaX / 2);
  2354.     newPt.y = deltaY;
  2355.     break;
  2356.     case TK_ANCHOR_NE:        /* Upper right corner */
  2357.     newPt.x = deltaX;
  2358.     break;
  2359.     case TK_ANCHOR_E:        /* Right center */
  2360.     newPt.x = deltaX;
  2361.     newPt.y = (deltaY / 2);
  2362.     break;
  2363.     case TK_ANCHOR_SE:        /* Lower right corner */
  2364.     newPt.x = deltaX;
  2365.     newPt.y = deltaY;
  2366.     break;
  2367.     }
  2368.     return (newPt);
  2369. }
  2370.  
  2371. /*
  2372.  *----------------------------------------------------------------------
  2373.  *
  2374.  * GetReqWidth --
  2375.  *
  2376.  *    Returns the width requested by the slave window starting in
  2377.  *    the given cubicle.  The requested space also includes any
  2378.  *    internal padding which has been designated for this window.
  2379.  *
  2380.  *    The requested width of the window is always bounded by
  2381.  *    the limits set in cubiPtr->reqWidth.
  2382.  *
  2383.  * Results:
  2384.  *    Returns the requested width of the slave window.
  2385.  *
  2386.  *----------------------------------------------------------------------
  2387.  */
  2388. static int
  2389. GetReqWidth(cubiPtr)
  2390.     Cubicle *cubiPtr;
  2391. {
  2392.     register int width;
  2393.  
  2394.     if (cubiPtr->reqWidth.nom > 0) {
  2395.     width = cubiPtr->reqWidth.nom;
  2396.     } else {
  2397.     width = Tk_ReqWidth(cubiPtr->tkwin) + (2 * cubiPtr->ipadX);
  2398.     }
  2399.     if (width < cubiPtr->reqWidth.min) {
  2400.     width = cubiPtr->reqWidth.min;
  2401.     } else if (width > cubiPtr->reqWidth.max) {
  2402.     width = cubiPtr->reqWidth.max;
  2403.     }
  2404.     return (width);
  2405. }
  2406.  
  2407. /*
  2408.  *----------------------------------------------------------------------
  2409.  *
  2410.  * GetReqHeight --
  2411.  *
  2412.  *    Returns the height requested by the slave window starting in
  2413.  *    the given cubicle.  The requested space also includes any
  2414.  *    internal padding which has been designated for this window.
  2415.  *
  2416.  *    The requested height of the window is always bounded by
  2417.  *    the limits set in cubiPtr->reqHeight.
  2418.  *
  2419.  * Results:
  2420.  *    Returns the requested height of the slave window.
  2421.  *
  2422.  *----------------------------------------------------------------------
  2423.  */
  2424. static int
  2425. GetReqHeight(cubiPtr)
  2426.     Cubicle *cubiPtr;
  2427. {
  2428.     register int height;
  2429.  
  2430.     if (cubiPtr->reqHeight.nom > 0) {
  2431.     height = cubiPtr->reqHeight.nom;
  2432.     } else {
  2433.     height = Tk_ReqHeight(cubiPtr->tkwin) + (2 * cubiPtr->ipadY);
  2434.     }
  2435.     if (height < cubiPtr->reqHeight.min) {
  2436.     height = cubiPtr->reqHeight.min;
  2437.     } else if (height > cubiPtr->reqHeight.max) {
  2438.     height = cubiPtr->reqHeight.max;
  2439.     }
  2440.     return (height);
  2441. }
  2442.  
  2443. /*
  2444.  *----------------------------------------------------------------------
  2445.  *
  2446.  * GetSpan --
  2447.  *
  2448.  *    Calculates the distance of the given span of partitions.
  2449.  *
  2450.  * Results:
  2451.  *    Returns the space currently used in the span of partitions.
  2452.  *
  2453.  *----------------------------------------------------------------------
  2454.  */
  2455. static int
  2456. GetSpan(partArr, length, withPad)
  2457.     Partition *partArr;        /* Array of partitions */
  2458.     int length;            /* Number of partitions spanned */
  2459.     int withPad;        /* If non-zero, include the extra padding at
  2460.                  * the end partitions of the span in the space
  2461.                  * used */
  2462. {
  2463.     register Partition *partPtr;
  2464.     Partition *startPtr, *endPtr;
  2465.     register int spaceUsed;
  2466.  
  2467.     startPtr = partArr;
  2468.     endPtr = partArr + (length - 1);
  2469.  
  2470.     spaceUsed = 0;
  2471.     for (partPtr = startPtr; partPtr <= endPtr; partPtr++) {
  2472.     spaceUsed += partPtr->size;
  2473.     }
  2474.     if (!withPad) {
  2475.     spaceUsed -= (startPtr->pad + endPtr->pad);
  2476.     }
  2477.     return (spaceUsed);
  2478. }
  2479.  
  2480. /*
  2481.  *----------------------------------------------------------------------
  2482.  *
  2483.  * GrowSpan --
  2484.  *
  2485.  *    Expand the span by the amount of the extra space needed.
  2486.  *      This procedure is used in LayoutPartitions to grow the
  2487.  *    partitions to their minimum nominal size, starting from
  2488.  *    a zero width and height space.
  2489.  *
  2490.  *    This looks more complicated than it really is.  The idea is
  2491.  *    to make the size of the partitions correspond to the smallest
  2492.  *    cubicle spans.  For example, if window A is in column 1 and
  2493.  *    window B spans both columns 0 and 1, any extra space needed
  2494.  *    to fit window B should come from column 0.
  2495.  *
  2496.  *    On the first pass we try to add space to partitions which have
  2497.  *    not been touched yet (i.e. have no nominal size).  Since the
  2498.  *    row and column lists are sorted in ascending order of the number
  2499.  *    of rows or columns spanned, the space is distributed amongst the
  2500.  *    smallest spans first.
  2501.  *
  2502.  *    The second pass handles the case of windows which have the same
  2503.  *    span.  For example, if A and B, which span the same number of
  2504.  *    partitions are the only windows to span column 1, column 1 would
  2505.  *    grow to contain the bigger of the two slices of space.
  2506.  *
  2507.  *    If there is still extra space after the first two passes, this
  2508.  *    means that there were no partitions of with no window spans or
  2509.  *    the same order span that could be expanded. The third pass
  2510.  *    will try to remedy this by parcelling out the left over space
  2511.  *    evenly among the rest of the partitions.
  2512.  *
  2513.  *    On each pass, we have to keep iterating over the span, evenly
  2514.  *    doling out slices of extra space, because we may hit partition
  2515.  *    limits as space is donated.  In addition, if there are left
  2516.  *    over pixels because of round-off, this will distribute them as
  2517.  *    evenly as possible.  For the worst case, it will take *length*
  2518.  *    passes to expand the span.
  2519.  *
  2520.  * Results:
  2521.  *    None.
  2522.  *
  2523.  * Side Effects:
  2524.  *     The partitions in the span may be expanded.
  2525.  *
  2526.  *----------------------------------------------------------------------
  2527.  */
  2528. static void
  2529. GrowSpan(array, length, extraSpace)
  2530.     Partition *array;        /* Array of (column/row) partitions  */
  2531.     int length;            /* Number of partitions in the span */
  2532.     int extraSpace;        /* The amount of extra space needed to
  2533.                  * grow the span. */
  2534. {
  2535.     register Partition *partPtr;
  2536.     Partition *startPtr, *endPtr;
  2537.     int availSpace, adjustSize;
  2538.     int numAvail;        /* Number of partitions with space available */
  2539.  
  2540.     startPtr = array;
  2541.     endPtr = array + length;
  2542.  
  2543.     /*
  2544.      *-------------------------------------------------------------------
  2545.      *
  2546.      * Pass 1: Add space first to partitions which were previously empty
  2547.      *
  2548.      *-------------------------------------------------------------------
  2549.      */
  2550.  
  2551.     /* Find out how many partitions have no size yet */
  2552.     numAvail = 0;
  2553.     for (partPtr = startPtr; partPtr < endPtr; partPtr++) {
  2554.     if ((partPtr->nomSize == 0) && (partPtr->maxSize > partPtr->size)) {
  2555.         numAvail++;
  2556.     }
  2557.     }
  2558.     while ((numAvail > 0) && (extraSpace > 0)) {
  2559.     adjustSize = extraSpace / numAvail;
  2560.     if (adjustSize == 0) {
  2561.         adjustSize = 1;
  2562.     }
  2563.     for (partPtr = startPtr; (partPtr < endPtr) && (extraSpace > 0);
  2564.         partPtr++) {
  2565.         availSpace = partPtr->maxSize - partPtr->size;
  2566.         if ((partPtr->nomSize == 0) && (availSpace > 0)) {
  2567.         if (adjustSize < availSpace) {
  2568.             extraSpace -= adjustSize;
  2569.             partPtr->size += adjustSize;
  2570.         } else {
  2571.             extraSpace -= availSpace;
  2572.             partPtr->size += availSpace;
  2573.             numAvail--;
  2574.         }
  2575.         partPtr->span = length;
  2576.         }
  2577.     }
  2578.     }
  2579.  
  2580.     /*
  2581.      *-------------------------------------------------------------------
  2582.      *
  2583.      * Pass 2: Add space to partitions which have the same minimum span
  2584.      *
  2585.      *-------------------------------------------------------------------
  2586.      */
  2587.  
  2588.     numAvail = 0;
  2589.     for (partPtr = startPtr; partPtr < endPtr; partPtr++) {
  2590.     if ((partPtr->span == length) && (partPtr->maxSize > partPtr->size)) {
  2591.         numAvail++;
  2592.     }
  2593.     }
  2594.     while ((numAvail > 0) && (extraSpace > 0)) {
  2595.     adjustSize = extraSpace / numAvail;
  2596.     if (adjustSize == 0) {
  2597.         adjustSize = 1;
  2598.     }
  2599.     for (partPtr = startPtr; (partPtr < endPtr) && (extraSpace > 0);
  2600.         partPtr++) {
  2601.         availSpace = partPtr->maxSize - partPtr->size;
  2602.         if ((partPtr->span == length) && (availSpace > 0)) {
  2603.         if (adjustSize < availSpace) {
  2604.             extraSpace -= adjustSize;
  2605.             partPtr->size += adjustSize;
  2606.         } else {
  2607.             extraSpace -= availSpace;
  2608.             partPtr->size += availSpace;
  2609.             numAvail--;
  2610.         }
  2611.         }
  2612.     }
  2613.     }
  2614.  
  2615.     /*
  2616.      *-------------------------------------------------------------------
  2617.      *
  2618.      * Pass 3: Try to expand all the windows with space still available
  2619.      *
  2620.      *-------------------------------------------------------------------
  2621.      */
  2622.  
  2623.     /* Find out how many partitions still have space available */
  2624.     numAvail = 0;
  2625.     for (partPtr = startPtr; partPtr < endPtr; partPtr++) {
  2626.     if (partPtr->maxSize > partPtr->size) {
  2627.         numAvail++;
  2628.     }
  2629.     partPtr->nomSize = partPtr->size;
  2630.     }
  2631.     while ((numAvail > 0) && (extraSpace > 0)) {
  2632.     adjustSize = extraSpace / numAvail;
  2633.     if (adjustSize == 0) {
  2634.         adjustSize = 1;
  2635.     }
  2636.     for (partPtr = startPtr; (partPtr < endPtr) && (extraSpace > 0);
  2637.         partPtr++) {
  2638.         availSpace = partPtr->maxSize - partPtr->size;
  2639.         if (availSpace > 0) {
  2640.         if (adjustSize < availSpace) {
  2641.             extraSpace -= adjustSize;
  2642.             partPtr->nomSize = partPtr->size =
  2643.             (partPtr->size + adjustSize);
  2644.         } else {
  2645.             extraSpace -= availSpace;
  2646.             partPtr->nomSize = partPtr->size =
  2647.             (partPtr->size + availSpace);
  2648.             numAvail--;
  2649.         }
  2650.         }
  2651.     }
  2652.     }
  2653. }
  2654.  
  2655. /*
  2656.  *----------------------------------------------------------------------
  2657.  *
  2658.  * AdjustPartitions --
  2659.  *
  2660.  *    Adjust the span by the amount of the extra space needed.
  2661.  *    If the amount (extraSpace) is negative, shrink the span,
  2662.  *    otherwise expand it.  Size constraints on the partitions may
  2663.  *    prevent any or all of the spacing adjustments.
  2664.  *
  2665.  *    This is very much like the GrowSpan procedure, but in this
  2666.  *    case we are shrinking or expanding all the (row or column)
  2667.  *    partitions. It uses a two pass approach, first giving
  2668.  *    space to partitions which not are smaller/larger than their
  2669.  *    nominal sizes. This is because constraints on the partitions
  2670.  *    may cause resizing to be non-linear.
  2671.  *
  2672.  *    If there is still extra space, this means that all partitions
  2673.  *    are at least to their nominal sizes.  The second pass will
  2674.  *    try to add/remove the left over space evenly among all the
  2675.  *    partitions which still have space available.
  2676.  *
  2677.  * Results:
  2678.  *    None.
  2679.  *
  2680.  * Side Effects:
  2681.  *     The size of the partitions in the span may be increased
  2682.  *    or decreased.
  2683.  *
  2684.  *----------------------------------------------------------------------
  2685.  */
  2686. static void
  2687. AdjustPartitions(array, length, extraSpace)
  2688.     Partition *array;        /* Array of (column/row) partitions  */
  2689.     int length;            /* Number of partitions */
  2690.     int extraSpace;        /* The amount of extra space to grow or shrink
  2691.                  * the span. If negative, it represents the
  2692.                  * amount of space to remove */
  2693. {
  2694.     register Partition *partPtr;
  2695.     Partition *startPtr, *endPtr;
  2696.     int availSpace, adjustSize;
  2697.     int numAvail;
  2698.  
  2699.     startPtr = array;
  2700.     endPtr = array + length;
  2701.  
  2702.     /*
  2703.      *-------------------------------------------------------------------
  2704.      *
  2705.      * Pass 1: Adjust partition's with space beyond its nominal size.
  2706.      *
  2707.      *-------------------------------------------------------------------
  2708.      */
  2709.     numAvail = 0;
  2710.     for (partPtr = startPtr; partPtr < endPtr; partPtr++) {
  2711.     if (extraSpace < 0) {
  2712.         availSpace = partPtr->size - partPtr->nomSize;
  2713.     } else {
  2714.         availSpace = partPtr->nomSize - partPtr->size;
  2715.     }
  2716.     if (availSpace > 0) {
  2717.         numAvail++;
  2718.     }
  2719.     }
  2720.     while ((numAvail > 0) && (extraSpace != 0)) {
  2721.     adjustSize = extraSpace / numAvail;
  2722.     if (adjustSize == 0) {
  2723.         adjustSize = (extraSpace > 0) ? 1 : -1;
  2724.     }
  2725.     for (partPtr = startPtr; (partPtr < endPtr) && (extraSpace != 0);
  2726.         partPtr++) {
  2727.         availSpace = partPtr->nomSize - partPtr->size;
  2728.         if (((extraSpace > 0) && (availSpace > 0)) ||
  2729.         ((extraSpace < 0) && (availSpace < 0))) {
  2730.         if (ABS(adjustSize) < ABS(availSpace)) {
  2731.             extraSpace -= adjustSize;
  2732.             partPtr->size += adjustSize;
  2733.         } else {
  2734.             extraSpace -= availSpace;
  2735.             partPtr->size += availSpace;
  2736.             numAvail--;
  2737.         }
  2738.         }
  2739.     }
  2740.     }
  2741.  
  2742.     /*
  2743.      *-------------------------------------------------------------------
  2744.      *
  2745.      * Pass 2: Adjust the partitions with space still available
  2746.      *
  2747.      *-------------------------------------------------------------------
  2748.      */
  2749.  
  2750.     numAvail = 0;
  2751.     for (partPtr = startPtr; partPtr < endPtr; partPtr++) {
  2752.     if (extraSpace > 0) {
  2753.         availSpace = partPtr->maxSize - partPtr->size;
  2754.     } else {
  2755.         availSpace = partPtr->size - partPtr->minSize;
  2756.     }
  2757.     if (availSpace > 0) {
  2758.         numAvail++;
  2759.     }
  2760.     }
  2761.     while ((numAvail > 0) && (extraSpace != 0)) {
  2762.     adjustSize = extraSpace / numAvail;
  2763.     if (adjustSize == 0) {
  2764.         adjustSize = (extraSpace > 0) ? 1 : -1;
  2765.     }
  2766.     for (partPtr = startPtr; (partPtr < endPtr) && (extraSpace != 0);
  2767.         partPtr++) {
  2768.         if (extraSpace > 0) {
  2769.         availSpace = partPtr->maxSize - partPtr->size;
  2770.         } else {
  2771.         availSpace = partPtr->minSize - partPtr->size;
  2772.         }
  2773.         if (((extraSpace > 0) && (availSpace > 0)) ||
  2774.         ((extraSpace < 0) && (availSpace < 0))) {
  2775.         if (ABS(adjustSize) < ABS(availSpace)) {
  2776.             extraSpace -= adjustSize;
  2777.             partPtr->size += adjustSize;
  2778.         } else {
  2779.             extraSpace -= availSpace;
  2780.             partPtr->size += availSpace;
  2781.             numAvail--;
  2782.         }
  2783.         }
  2784.     }
  2785.     }
  2786. }
  2787.  
  2788. /*
  2789.  *----------------------------------------------------------------------
  2790.  *
  2791.  * ResetPartitions --
  2792.  *
  2793.  *    Sets/resets the size of each row and column partition to the
  2794.  *    minimum limit of the partition (this is usually zero). This
  2795.  *    routine gets called when new slave windows are added, deleted,
  2796.  *    or resized.
  2797.  *
  2798.  * Results:
  2799.  *    None.
  2800.  *
  2801.  * Side Effects:
  2802.  *     The size of each partition is re-initialized to its minimum size.
  2803.  *
  2804.  *----------------------------------------------------------------------
  2805.  */
  2806. static void
  2807. ResetPartitions(partPtr, length)
  2808.     register Partition *partPtr;
  2809.     int length;
  2810. {
  2811.     register int i;
  2812.     int size;
  2813.  
  2814.     for (i = 0; i < length; i++) {
  2815.     if (partPtr->reqSize.nom > 0) {
  2816.         /*
  2817.          * This could be done more cleanly.  Want to ensure that
  2818.          * the requested nominal size is not overridden when
  2819.          * determining the normal sizes.  So temporarily fix min
  2820.          * and max to the nominal size and reset them back later.
  2821.          */
  2822.         partPtr->minSize = partPtr->maxSize = partPtr->size =
  2823.         partPtr->nomSize = partPtr->reqSize.nom;
  2824.  
  2825.     } else {
  2826.         partPtr->minSize = partPtr->reqSize.min;
  2827.         partPtr->maxSize = partPtr->reqSize.max;
  2828.         size = 2 * partPtr->pad;
  2829.         if (size < partPtr->minSize) {
  2830.         size = partPtr->minSize;
  2831.         } else if (size > partPtr->maxSize) {
  2832.         size = partPtr->maxSize;
  2833.         }
  2834.         partPtr->nomSize = 0;
  2835.         partPtr->size = size;
  2836.     }
  2837.     partPtr->span = 0;
  2838.     partPtr++;
  2839.     }
  2840. }
  2841.  
  2842. /*
  2843.  *----------------------------------------------------------------------
  2844.  *
  2845.  * SetNominalSizes
  2846.  *
  2847.  *    Sets the normal sizes for each partition in the array.
  2848.  *    The partition size is the requested window size plus an
  2849.  *    amount of padding.  In addition, adjust the min/max bounds
  2850.  *    of the partition depending upon the resize flags (whether
  2851.  *    the partition can be expanded or shrunk from its normal
  2852.  *    size).
  2853.  *
  2854.  * Results:
  2855.  *    Returns the total space needed for the all the partitions.
  2856.  *
  2857.  * Side Effects:
  2858.  *    The nominal size of each partition in the array is set.
  2859.  *    This is used later to determine how to shrink or grow the
  2860.  *    table if the master window cannot be resized to accommodate
  2861.  *    exactly the size requirements of all the partitions.
  2862.  *
  2863.  *----------------------------------------------------------------------
  2864.  */
  2865. static int
  2866. SetNominalSizes(partPtr, numEntries)
  2867.     register Partition *partPtr;/* Row or column partition array */
  2868.     int numEntries;        /* Number of partitions in the array */
  2869. {
  2870.     register int i, size;
  2871.     int total = 0;
  2872.  
  2873.     for (i = 0; i < numEntries; i++) {
  2874.     /*
  2875.      * Restore the real bounds after setting nominal size.
  2876.      */
  2877.     partPtr->minSize = partPtr->reqSize.min;
  2878.     partPtr->maxSize = partPtr->reqSize.max;
  2879.  
  2880.     size = partPtr->size;
  2881.     if (size > partPtr->maxSize) {
  2882.         size = partPtr->maxSize;
  2883.     }
  2884.     partPtr->nomSize = partPtr->size = size;
  2885.     total += partPtr->nomSize;
  2886.  
  2887.     /*
  2888.      * If a partition can't be resized (to either expand or
  2889.      * shrink), hold its respective limit at its normal size.
  2890.      */
  2891.  
  2892.     if (!(partPtr->resize & RESIZE_EXPAND)) {
  2893.         partPtr->maxSize = partPtr->nomSize;
  2894.     }
  2895.     if (!(partPtr->resize & RESIZE_SHRINK)) {
  2896.         partPtr->minSize = partPtr->nomSize;
  2897.     }
  2898.     partPtr++;
  2899.     }
  2900.     return (total);
  2901. }
  2902.  
  2903. /*
  2904.  *----------------------------------------------------------------------
  2905.  *
  2906.  * LayoutPartitions --
  2907.  *
  2908.  *    Calculates the normal space requirements for both the row
  2909.  *    and column partitions.  Each slave window is added in order of
  2910.  *    the number of rows or columns spanned, which defines the space
  2911.  *    needed among in the partitions spanned.
  2912.  *
  2913.  * Results:
  2914.  *    None.
  2915.  *
  2916.  * Side Effects:
  2917.  *     The sum of normal sizes set here will be used as the normal
  2918.  *    size for the master window.
  2919.  *
  2920.  *----------------------------------------------------------------------
  2921.  */
  2922. static void
  2923. LayoutPartitions(tablePtr)
  2924.     Table *tablePtr;
  2925. {
  2926.     register Blt_ListEntry *entryPtr;
  2927.     register Cubicle *cubiPtr;
  2928.     Partition *rowPtr, *colPtr;
  2929.     int neededSpace, usedSpace, totalSpace;
  2930.     int twiceBW;
  2931.  
  2932.     twiceBW = (2 * Tk_InternalBorderWidth(tablePtr->tkwin));
  2933.     ResetPartitions(tablePtr->colPtr, tablePtr->numCols);
  2934.     for (entryPtr = Blt_FirstListEntry(&(tablePtr->colSorted));
  2935.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  2936.     cubiPtr = (Cubicle *)Blt_GetListValue(entryPtr);
  2937.     neededSpace = GetReqWidth(cubiPtr) +
  2938.         2 * (cubiPtr->padX + cubiPtr->extBW);
  2939.     if (neededSpace > 0) {
  2940.         colPtr = tablePtr->colPtr + cubiPtr->colIndex;
  2941.         usedSpace = GetSpan(colPtr, cubiPtr->colSpan, WITHOUT_PAD);
  2942.         if (neededSpace > usedSpace) {
  2943.         GrowSpan(colPtr, cubiPtr->colSpan, neededSpace - usedSpace);
  2944.         }
  2945.     }
  2946.     }
  2947.     totalSpace = SetNominalSizes(tablePtr->colPtr, tablePtr->numCols);
  2948.     tablePtr->reqWidth = totalSpace + twiceBW;
  2949.  
  2950.     ResetPartitions(tablePtr->rowPtr, tablePtr->numRows);
  2951.     for (entryPtr = Blt_FirstListEntry(&(tablePtr->rowSorted));
  2952.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  2953.     cubiPtr = (Cubicle *)Blt_GetListValue(entryPtr);
  2954.     neededSpace = GetReqHeight(cubiPtr) +
  2955.         2 * (cubiPtr->padY + cubiPtr->extBW);
  2956.     if (neededSpace > 0) {
  2957.         rowPtr = tablePtr->rowPtr + cubiPtr->rowIndex;
  2958.         usedSpace = GetSpan(rowPtr, cubiPtr->rowSpan, WITHOUT_PAD);
  2959.         if (neededSpace > usedSpace) {
  2960.         GrowSpan(rowPtr, cubiPtr->rowSpan, neededSpace - usedSpace);
  2961.         }
  2962.     }
  2963.     }
  2964.     totalSpace = SetNominalSizes(tablePtr->rowPtr, tablePtr->numRows);
  2965.     tablePtr->reqHeight = totalSpace + twiceBW;
  2966. }
  2967.  
  2968. /*
  2969.  *----------------------------------------------------------------------
  2970.  *
  2971.  * ArrangeCubicles
  2972.  *
  2973.  *    Places each slave window at its proper location.  First
  2974.  *    determines the size and position of the each window.
  2975.  *    It then considers the following:
  2976.  *
  2977.  *      1. translation of slave window position its parent window.
  2978.  *      2. fill style
  2979.  *      3. anchor
  2980.  *      4. external and internal padding
  2981.  *      5. window size must be greater than zero
  2982.  *
  2983.  * Results:
  2984.  *    None.
  2985.  *
  2986.  * Side Effects:
  2987.  *     The size of each partition is re-initialized its minimum size.
  2988.  *
  2989.  *----------------------------------------------------------------------
  2990.  */
  2991. static void
  2992. ArrangeCubicles(tablePtr)
  2993.     Table *tablePtr;        /* Table widget structure */
  2994. {
  2995.     register Blt_ListEntry *entryPtr;
  2996.     register Cubicle *cubiPtr;
  2997.     register int width, height;
  2998.     Partition *colPtr, *rowPtr;
  2999.     register int x, y;
  3000.     int winWidth, winHeight;
  3001.     int deltaX, deltaY;
  3002.     Tk_Window parent, ancestor;
  3003.     int maxX, maxY;
  3004.  
  3005.     maxX = tablePtr->width - Tk_InternalBorderWidth(tablePtr->tkwin);
  3006.     maxY = tablePtr->height - Tk_InternalBorderWidth(tablePtr->tkwin);
  3007.     for (entryPtr = tablePtr->listPtr->tailPtr; entryPtr != NULL;
  3008.     entryPtr = entryPtr->prevPtr) {
  3009.     cubiPtr = (Cubicle *)Blt_GetListValue(entryPtr);
  3010.  
  3011.     colPtr = tablePtr->colPtr + cubiPtr->colIndex;
  3012.     rowPtr = tablePtr->rowPtr + cubiPtr->rowIndex;
  3013.  
  3014.     x = colPtr->offset + colPtr->pad + cubiPtr->padX + cubiPtr->extBW;
  3015.     y = rowPtr->offset + rowPtr->pad + cubiPtr->padY + cubiPtr->extBW;
  3016.  
  3017.     /*
  3018.      * Unmap any slave windows which start outside of the master
  3019.      * window
  3020.      */
  3021.     if ((x >= maxX) || (y >= maxY)) {
  3022.         if (Tk_IsMapped(cubiPtr->tkwin)) {
  3023.         Tk_UnmapWindow(cubiPtr->tkwin);
  3024.         }
  3025.         continue;
  3026.     }
  3027.     width = GetSpan(colPtr, cubiPtr->colSpan, WITHOUT_PAD) -
  3028.         (2 * (cubiPtr->padX + cubiPtr->extBW));
  3029.     height = GetSpan(rowPtr, cubiPtr->rowSpan, WITHOUT_PAD) -
  3030.         (2 * (cubiPtr->padY + cubiPtr->extBW));
  3031.  
  3032.     winWidth = GetReqWidth(cubiPtr);
  3033.     winHeight = GetReqHeight(cubiPtr);
  3034.  
  3035.     /*
  3036.      *
  3037.      * Compare the window's requested size to the size of the
  3038.      * span.
  3039.      *
  3040.      * 1) If it's bigger or if the fill flag is set, make them
  3041.      *    the same size. Check that the new size is within the
  3042.      *    bounds set for the window.
  3043.      *
  3044.      * 2) Otherwise, position the window in the space according
  3045.      *    to its anchor.
  3046.      *
  3047.      */
  3048.  
  3049.     if ((width <= winWidth) || (cubiPtr->fill & FILL_X)) {
  3050.         winWidth = width;
  3051.         if (winWidth > cubiPtr->reqWidth.max) {
  3052.         winWidth = cubiPtr->reqWidth.max;
  3053.         }
  3054.     }
  3055.     if ((height <= winHeight) || (cubiPtr->fill & FILL_Y)) {
  3056.         winHeight = height;
  3057.         if (winHeight > cubiPtr->reqHeight.max) {
  3058.         winHeight = cubiPtr->reqHeight.max;
  3059.         }
  3060.     }
  3061.     /*
  3062.      * If the window is too small to be interesting (i.e. it has
  3063.      * only an external border) then unmap it.
  3064.      */
  3065.     if ((winWidth < 1) || (winHeight < 1)) {
  3066.         if (Tk_IsMapped(cubiPtr->tkwin)) {
  3067.         Tk_UnmapWindow(cubiPtr->tkwin);
  3068.         }
  3069.         continue;
  3070.     }
  3071.     deltaX = deltaY = 0;
  3072.     if (width > winWidth) {
  3073.         deltaX = (width - winWidth);
  3074.     }
  3075.     if (height > winHeight) {
  3076.         deltaY = (height - winHeight);
  3077.     }
  3078.     if ((deltaX > 0) || (deltaY > 0)) {
  3079.         XPoint newPt;
  3080.  
  3081.         newPt = TranslateAnchor(deltaX, deltaY, cubiPtr->anchor);
  3082.         x += newPt.x, y += newPt.y;
  3083.     }
  3084.     /*
  3085.      * Clip the slave window at the bottom and/or right edge of
  3086.      * the master
  3087.      */
  3088.     if (winWidth > (maxX - x)) {
  3089.         winWidth = (maxX - x);
  3090.     }
  3091.     if (winHeight > (maxY - y)) {
  3092.         winHeight = (maxY - y);
  3093.     }
  3094.     /*
  3095.      * If the slave's parent window is not the master window,
  3096.      * translate the master window's x and y coordinates to the
  3097.      * coordinate system of the slave's parent.
  3098.      */
  3099.  
  3100.     parent = Tk_Parent(cubiPtr->tkwin);
  3101.     for (ancestor = tablePtr->tkwin; ancestor != parent;
  3102.         ancestor = Tk_Parent(ancestor)) {
  3103.         x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width;
  3104.         y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width;
  3105.     }
  3106.  
  3107.     /*
  3108.      * Resize or move the window if necessary. Save the window's x
  3109.      * and y coordinates for reference next time.
  3110.      */
  3111.  
  3112.     if ((x != cubiPtr->x) || (y != cubiPtr->y) ||
  3113.         (winWidth != Tk_Width(cubiPtr->tkwin)) ||
  3114.         (winHeight != Tk_Height(cubiPtr->tkwin))) {
  3115. #ifdef notdef
  3116.         fprintf(stderr, "%s at %d,%d is %dx%d\n",
  3117.         Tk_PathName(cubiPtr->tkwin), x, y, winWidth, winHeight);
  3118. #endif
  3119.         Tk_MoveResizeWindow(cubiPtr->tkwin, x, y, (unsigned int)winWidth,
  3120.         (unsigned int)winHeight);
  3121.         cubiPtr->x = x, cubiPtr->y = y;
  3122.     }
  3123.     if (!Tk_IsMapped(cubiPtr->tkwin)) {
  3124.         Tk_MapWindow(cubiPtr->tkwin);
  3125.     }
  3126.     }
  3127. }
  3128.  
  3129. /*
  3130.  *----------------------------------------------------------------------
  3131.  *
  3132.  * ArrangeTable --
  3133.  *
  3134.  *
  3135.  * Results:
  3136.  *    None.
  3137.  *
  3138.  * Side Effects:
  3139.  *     The slave windows in the table are possibly resized and redrawn.
  3140.  *
  3141.  *----------------------------------------------------------------------
  3142.  */
  3143. static void
  3144. ArrangeTable(clientData)
  3145.     ClientData clientData;
  3146. {
  3147.     Table *tablePtr = (Table *)clientData;
  3148.     int width, height;
  3149.     register int i;
  3150.     unsigned int offset;
  3151.     int twiceBW;
  3152.  
  3153. #ifdef notdef
  3154.     fprintf(stderr, "ArrangeTable(%s)\n", Tk_PathName(tablePtr->tkwin));
  3155. #endif
  3156.     Tk_Preserve((ClientData)tablePtr);
  3157.     tablePtr->flags &= ~ARRANGE_PENDING;
  3158.  
  3159.     /*
  3160.      * If the table has no children anymore, then don't do anything at
  3161.      * all: just leave the master window's size as-is.
  3162.      */
  3163.  
  3164.     if ((Blt_GetListLength(tablePtr->listPtr) == 0) ||
  3165.     (tablePtr->tkwin == NULL)) {
  3166.     Tk_Release((ClientData)tablePtr);
  3167.     return;
  3168.     }
  3169.     if (tablePtr->flags & REQUEST_LAYOUT) {
  3170.     tablePtr->flags &= ~REQUEST_LAYOUT;
  3171.     LayoutPartitions(tablePtr);
  3172.     }
  3173.     /*
  3174.      * Initially, try to fit the partitions exactly into the master
  3175.      * window by resizing the window.  If the window's requested size
  3176.      * is different, send a request to the master window's geometry
  3177.      * manager to resize.
  3178.      */
  3179.  
  3180.     if ((tablePtr->reqWidth != Tk_ReqWidth(tablePtr->tkwin)) ||
  3181.     (tablePtr->reqHeight != Tk_ReqHeight(tablePtr->tkwin))) {
  3182.     Tk_GeometryRequest(tablePtr->tkwin, tablePtr->reqWidth,
  3183.         tablePtr->reqHeight);
  3184.     tablePtr->flags |= ARRANGE_PENDING;
  3185.     Tk_DoWhenIdle(ArrangeTable, (ClientData)tablePtr);
  3186.     Tk_Release((ClientData)tablePtr);
  3187.     return;
  3188.     }
  3189.     /*
  3190.      * If the parent isn't mapped then don't do anything more: wait
  3191.      * until it gets mapped again.  Need to get at least to here to
  3192.      * reflect size needs up the window hierarchy, but there's no
  3193.      * point in actually mapping the children.
  3194.      */
  3195.  
  3196.     if (!Tk_IsMapped(tablePtr->tkwin)) {
  3197.     Tk_Release((ClientData)tablePtr);
  3198.     return;
  3199.     }
  3200.     /*
  3201.      * Save the width and height of the master window so we know when
  3202.      * it has changed during ConfigureNotify events.
  3203.      */
  3204.  
  3205.     tablePtr->width = Tk_Width(tablePtr->tkwin);
  3206.     tablePtr->height = Tk_Height(tablePtr->tkwin);
  3207.     twiceBW = (2 * Tk_InternalBorderWidth(tablePtr->tkwin));
  3208.  
  3209.     width = GetSpan(tablePtr->colPtr, tablePtr->numCols, WITH_PAD) + twiceBW;
  3210.     height = GetSpan(tablePtr->rowPtr, tablePtr->numRows, WITH_PAD) + twiceBW;
  3211.  
  3212.     /*
  3213.      * If the previous geometry request was not fulfilled (i.e. the
  3214.      * size of the master window is different from partitions' space
  3215.      * requirements), try to adjust size of the partitions to fit the
  3216.      * window.
  3217.      */
  3218.  
  3219.     if (tablePtr->width != width) {
  3220.     AdjustPartitions(tablePtr->colPtr, tablePtr->numCols,
  3221.         tablePtr->width - width);
  3222.     width = GetSpan(tablePtr->colPtr, tablePtr->numCols, WITH_PAD) +
  3223.         twiceBW;
  3224.     }
  3225.     if (tablePtr->height != height) {
  3226.     AdjustPartitions(tablePtr->rowPtr, tablePtr->numRows,
  3227.         tablePtr->height - height);
  3228.     height = GetSpan(tablePtr->rowPtr, tablePtr->numRows, WITH_PAD) +
  3229.         twiceBW;
  3230.     }
  3231.     /*
  3232.      * If after adjusting the size of the partitions the space
  3233.      * required does not equal the size of the window, do one of the
  3234.      * following:
  3235.      *
  3236.      * 1) If is smaller, center the table in the window.
  3237.      * 2) If it's still bigger, clip the partitions that extend beyond
  3238.      *    the edge of the master window.
  3239.      *
  3240.      * Set the row and column offsets (including the master's internal
  3241.      * border width). To be used later when positioning the slave
  3242.      * windows.
  3243.      */
  3244.  
  3245.     offset = Tk_InternalBorderWidth(tablePtr->tkwin);
  3246.     if (width < tablePtr->width) {
  3247.     offset += (tablePtr->width - width) / 2;
  3248.     }
  3249.     for (i = 0; i < tablePtr->numCols; i++) {
  3250.     tablePtr->colPtr[i].offset = offset;
  3251.     offset += tablePtr->colPtr[i].size;
  3252.     }
  3253.  
  3254.     offset = Tk_InternalBorderWidth(tablePtr->tkwin);
  3255.     if (height < tablePtr->height) {
  3256.     offset += (tablePtr->height - height) / 2;
  3257.     }
  3258.     for (i = 0; i < tablePtr->numRows; i++) {
  3259.     tablePtr->rowPtr[i].offset = offset;
  3260.     offset += tablePtr->rowPtr[i].size;
  3261.     }
  3262.     ArrangeCubicles(tablePtr);
  3263.     Tk_Release((ClientData)tablePtr);
  3264. }
  3265.  
  3266. /*
  3267.  *--------------------------------------------------------------
  3268.  *
  3269.  * PartitionCmd --
  3270.  *
  3271.  *    This procedure is invoked to process the Tcl command
  3272.  *    that corresponds to the table geometry manager. It handles
  3273.  *    only those commands related to the table's rows or columns.
  3274.  *    See the user documentation for details on what it does.
  3275.  *
  3276.  * Results:
  3277.  *    A standard Tcl result.
  3278.  *
  3279.  * Side effects:
  3280.  *    See the user documentation.
  3281.  *
  3282.  *--------------------------------------------------------------
  3283.  */
  3284. static int
  3285. PartitionCmd(tablePtr, interp, type, argc, argv)
  3286.     Table *tablePtr;
  3287.     Tcl_Interp *interp;
  3288.     PartitionTypes type;
  3289.     int argc;
  3290.     char **argv;
  3291. {
  3292.     char c;
  3293.     int length;
  3294.     int result = TCL_ERROR;
  3295.     char *partClass;
  3296.  
  3297.     partClass = partitionNames[(int)type];
  3298.     if (argc < 4) {
  3299.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3300.         " master ", partClass, " option number ?args?", (char *)NULL);
  3301.     return TCL_ERROR;
  3302.     }
  3303.     c = argv[3][0];
  3304.     length = strlen(argv[3]);
  3305.     if ((c == 'c') && (strncmp(argv[3], "configure", length) == 0)) {
  3306.     if (argc < 5) {
  3307.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3308.         " master ", partClass, " configure index", (char *)NULL);
  3309.         return TCL_ERROR;
  3310.     }
  3311.     result = ConfigurePartition(tablePtr, interp, type, argc - 4, argv + 4);
  3312.     } else if ((c == 'd') && (strncmp(argv[3], "dimension", length) == 0)) {
  3313.     if (argc != 4) {
  3314.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3315.         " master ", partClass, " dimension", (char *)NULL);
  3316.         return TCL_ERROR;
  3317.     }
  3318.     sprintf(interp->result, "%d", NUMENTRIES(tablePtr, type));
  3319.     result = TCL_OK;
  3320.     } else if ((c == 'i') && (strncmp(argv[3], "info", length) == 0)) {
  3321.     if (argc < 5) {
  3322.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3323.         " master ", partClass, " info index", (char *)NULL);
  3324.         return TCL_ERROR;
  3325.     }
  3326.     result = PartitionInfo(tablePtr, interp, type, argc - 4, argv + 4);
  3327.     } else if ((c == 's') && (strncmp(argv[3], "sizes", length) == 0)) {
  3328.     if (argc != 5) {
  3329.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3330.         " master ", partClass, " sizes index", (char *)NULL);
  3331.         return TCL_ERROR;
  3332.     }
  3333.     result = PartitionSizes(tablePtr, interp, type, argv[4]);
  3334.     } else {
  3335.     Tcl_AppendResult(interp, "unknown ", partClass, " option \"", argv[3],
  3336.         "\": should be configure, dimension, info, or sizes",
  3337.         (char *)NULL);
  3338.     }
  3339.     return result;
  3340. }
  3341.  
  3342. /*
  3343.  *--------------------------------------------------------------
  3344.  *
  3345.  * TableCmd --
  3346.  *
  3347.  *    This procedure is invoked to process the Tcl command
  3348.  *    that corresponds to the table geometry manager.
  3349.  *    See the user documentation for details on what it does.
  3350.  *
  3351.  * Results:
  3352.  *    A standard Tcl result.
  3353.  *
  3354.  * Side effects:
  3355.  *    See the user documentation.
  3356.  *
  3357.  *--------------------------------------------------------------
  3358.  */
  3359. static int
  3360. TableCmd(clientData, interp, argc, argv)
  3361.     ClientData clientData;
  3362.     Tcl_Interp *interp;
  3363.     int argc;
  3364.     char **argv;
  3365. {
  3366.     char c;
  3367.     int length;
  3368.     int result = TCL_ERROR;
  3369.     Tk_Window mainWindow = (Tk_Window)clientData;
  3370.     Table *tablePtr;
  3371.  
  3372.     if (argc < 2) {
  3373.     Tcl_AppendResult(interp,
  3374.         "wrong # args: should be \"", argv[0], " options\"", (char *)NULL);
  3375.     return TCL_ERROR;
  3376.     }
  3377.     /* Initialize structures managing all table widgets once. */
  3378.  
  3379.     if (!initialized) {
  3380.     Tcl_InitHashTable(&masterWindows, TCL_ONE_WORD_KEYS);
  3381.     Tcl_InitHashTable(&slaveWindows, TCL_ONE_WORD_KEYS);
  3382.     initialized = TRUE;
  3383.     }
  3384.     tablePtr = NULL;
  3385.     c = argv[1][0];
  3386.     length = strlen(argv[1]);
  3387.     if (c == '.') {
  3388.     tablePtr = FindTable(interp, argv[1], mainWindow, 0);
  3389.  
  3390.     /* If the table doesn't exist, create one. */
  3391.     if (tablePtr == NULL) {
  3392.         tablePtr = CreateTable(interp, argv[1], mainWindow);
  3393.         if (tablePtr == NULL) {
  3394.         return TCL_ERROR;
  3395.         }
  3396.     }
  3397.     return (ManageWindows(tablePtr, interp, argc - 2, argv + 2));
  3398.     } else if ((c == 'c') && (length > 2) &&
  3399.     (strncmp(argv[1], "configure", length) == 0)) {
  3400.     if (argc < 3) {
  3401.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3402.         " configure slave ?option-values ...?\"", (char *)NULL);
  3403.         return TCL_ERROR;
  3404.     }
  3405.     return (ConfigureCubicle(clientData, interp, argc - 2, argv + 2));
  3406.     } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
  3407.     if (argc < 3) {
  3408.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3409.         " forget slave ?slave ...?\"", (char *)NULL);
  3410.         return TCL_ERROR;
  3411.     }
  3412.     return (ForgetWindow(clientData, interp, argc - 2, argv + 2));
  3413.     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
  3414.     if (argc != 3) {
  3415.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3416.         " info slave\"", (char *)NULL);
  3417.         return TCL_ERROR;
  3418.     }
  3419.     return (SlaveInfo(clientData, interp, argv[2]));
  3420.     }
  3421.     /*
  3422.      * The rest of the options all take the pathname of the master
  3423.      * window as their second argument.  If the second argument starts
  3424.      * with a ".", try to find the table associated with it.
  3425.      */
  3426.     if ((argc > 2) && (argv[2][0] == '.')) {
  3427.     tablePtr = FindTable(interp, argv[2], mainWindow, TCL_LEAVE_ERR_MSG);
  3428.     if (tablePtr == NULL) {
  3429.         return TCL_ERROR;
  3430.     }
  3431.     }
  3432.     if ((c == 'c') && (length > 2) &&
  3433.     (strncmp(argv[1], "column", length) == 0)) {
  3434.     result = PartitionCmd(tablePtr, interp, COLUMN_PARTITION_TYPE, argc,
  3435.         argv);
  3436.     } else if ((c == 'r') && (strncmp(argv[1], "row", length) == 0)) {
  3437.     result = PartitionCmd(tablePtr, interp, ROW_PARTITION_TYPE, argc,
  3438.         argv);
  3439.     } else if ((c == 'm') && (strncmp(argv[1], "masters", length) == 0)) {
  3440.     if ((argc != 2) && (argc != 3)) {
  3441.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3442.         " masters ?pattern?", (char *)NULL);
  3443.         return TCL_ERROR;
  3444.     }
  3445.     result = MasterNames(clientData, interp, argc - 2, argv + 2);
  3446.     } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
  3447.     if ((argc != 3) && (argc != 4)) {
  3448.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3449.         " slaves master ?pattern?", (char *)NULL);
  3450.         return TCL_ERROR;
  3451.     }
  3452.     result = SlaveNames(tablePtr, interp, argc - 2, argv + 2);
  3453.     } else if ((c == 'a') && (strncmp(argv[1], "arrange", length) == 0)) {
  3454.     if (argc != 3) {
  3455.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3456.         " arrange master", (char *)NULL);
  3457.         return TCL_ERROR;
  3458.     }
  3459.     result = LayoutTable(tablePtr, interp, argv[2]);
  3460.     } else {
  3461.     Tcl_AppendResult(interp, "unknown option \"", argv[1], "\": should be\
  3462.  configure, column, forget, info, masters, row, or slaves", (char *)NULL);
  3463.     }
  3464.     return result;
  3465. }
  3466.  
  3467. /*
  3468.  *--------------------------------------------------------------
  3469.  *
  3470.  * Blt_TableInit --
  3471.  *
  3472.  *    This procedure is invoked to initialized the Tcl command
  3473.  *    that corresponds to the table geometry manager.
  3474.  *
  3475.  * Results:
  3476.  *    None.
  3477.  *
  3478.  * Side effects:
  3479.  *    Creates the new command and adds an entry into a global
  3480.  *    Tcl associative array.
  3481.  *
  3482.  *--------------------------------------------------------------
  3483.  */
  3484. int
  3485. Blt_TableInit(interp)
  3486.     Tcl_Interp *interp;
  3487. {
  3488.     Tk_Window tkwin;
  3489.  
  3490.     if (Blt_FindCmd(interp, "blt_table", (ClientData *)NULL) == TCL_OK) {
  3491.     Tcl_AppendResult(interp, "\"blt_table\" command already exists",
  3492.         (char *)NULL);
  3493.     return TCL_ERROR;
  3494.     }
  3495.     tkwin = Tk_MainWindow(interp);
  3496.     if (tkwin == NULL) {
  3497.     Tcl_AppendResult(interp, "\"blt_table\" requires Tk", (char *)NULL);
  3498.     return TCL_ERROR;
  3499.     }
  3500.     Tcl_SetVar2(interp, "blt_versions", "blt_table", TABLE_VERSION,
  3501.     TCL_GLOBAL_ONLY);
  3502.     Tcl_CreateCommand(interp, "blt_table", TableCmd, (ClientData)tkwin,
  3503.     (Tcl_CmdDeleteProc *)NULL);
  3504.     return TCL_OK;
  3505. }
  3506.