home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / tk3.3b1 / tkPlace.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-16  |  27.7 KB  |  957 lines

  1. /* 
  2.  * tkPlace.c --
  3.  *
  4.  *    This file contains code to implement a simple geometry manager
  5.  *    for Tk based on absolute placement or "rubber-sheet" placement.
  6.  *
  7.  * Copyright (c) 1992-1993 The Regents of the University of California.
  8.  * All rights reserved.
  9.  *
  10.  * Permission is hereby granted, without written agreement and without
  11.  * license or royalty fees, to use, copy, modify, and distribute this
  12.  * software and its documentation for any purpose, provided that the
  13.  * above copyright notice and the following two paragraphs appear in
  14.  * all copies of this software.
  15.  * 
  16.  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
  17.  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
  18.  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
  19.  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  20.  *
  21.  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  22.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  23.  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  24.  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
  25.  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  26.  */
  27.  
  28. #ifndef lint
  29. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkPlace.c,v 1.10 93/06/16 17:16:24 ouster Exp $ SPRITE (Berkeley)";
  30. #endif /* not lint */
  31.  
  32. #include "tkConfig.h"
  33. #include "tkInt.h"
  34.  
  35. /*
  36.  * Border modes for relative placement:
  37.  *
  38.  * BM_INSIDE:        relative distances computed using area inside
  39.  *            all borders of master window.
  40.  * BM_OUTSIDE:        relative distances computed using outside area
  41.  *            that includes all borders of master.
  42.  * BM_IGNORE:        border issues are ignored:  place relative to
  43.  *            master's actual window size.
  44.  */
  45.  
  46. typedef enum {BM_INSIDE, BM_OUTSIDE, BM_IGNORE} BorderMode;
  47.  
  48. /*
  49.  * For each window whose geometry is managed by the placer there is
  50.  * a structure of the following type:
  51.  */
  52.  
  53. typedef struct Slave {
  54.     Tk_Window tkwin;        /* Tk's token for window. */
  55.     struct Master *masterPtr;    /* Pointer to information for window
  56.                  * relative to which tkwin is placed.
  57.                  * This isn't necessarily the logical
  58.                  * parent of tkwin.  NULL means the
  59.                  * master was deleted or never assigned. */
  60.     struct Slave *nextPtr;    /* Next in list of windows placed relative
  61.                  * to same master (NULL for end of list). */
  62.  
  63.     /*
  64.      * Geometry information for window;  where there are both relative
  65.      * and absolute values for the same attribute (e.g. x and relX) only
  66.      * one of them is actually used, depending on flags.
  67.      */
  68.  
  69.     int x, y;            /* X and Y pixel coordinates for tkwin. */
  70.     float relX, relY;        /* X and Y coordinates relative to size of
  71.                  * master. */
  72.     int width, height;        /* Absolute dimensions for tkwin. */
  73.     float relWidth, relHeight;    /* Dimensions for tkwin relative to size of
  74.                  * master. */
  75.     Tk_Anchor anchor;        /* Which point on tkwin is placed at the
  76.                  * given position. */
  77.     BorderMode borderMode;    /* How to treat borders of master window. */
  78.     int flags;            /* Various flags;  see below for bit
  79.                  * definitions. */
  80. } Slave;
  81.  
  82. /*
  83.  * Flag definitions for Slave structures:
  84.  *
  85.  * CHILD_REL_X -        1 means use relX field;  0 means use x.
  86.  * CHILD_REL_Y -        1 means use relY field;  0 means use y;
  87.  * CHILD_WIDTH -        1 means use width field;
  88.  * CHILD_REL_WIDTH -        1 means use relWidth;  if neither this nor
  89.  *                CHILD_WIDTH is 1, use window's requested
  90.  *                width.
  91.  * CHILD_HEIGHT -        1 means use height field;
  92.  * CHILD_REL_HEIGHT -        1 means use relHeight;  if neither this nor
  93.  *                CHILD_HEIGHT is 1, use window's requested
  94.  *                height.
  95.  */
  96.  
  97. #define CHILD_REL_X        1
  98. #define CHILD_REL_Y        2
  99. #define CHILD_WIDTH        4
  100. #define CHILD_REL_WIDTH        8
  101. #define CHILD_HEIGHT        0x10
  102. #define CHILD_REL_HEIGHT    0x20
  103.  
  104. /*
  105.  * For each master window that has a slave managed by the placer there
  106.  * is a structure of the following form:
  107.  */
  108.  
  109. typedef struct Master {
  110.     Tk_Window tkwin;        /* Tk's token for master window. */
  111.     struct Slave *slavePtr;    /* First in linked list of slaves
  112.                  * placed relative to this master. */
  113.     int flags;            /* See below for bit definitions. */
  114. } Master;
  115.  
  116. /*
  117.  * Flag definitions for masters:
  118.  *
  119.  * PARENT_RECONFIG_PENDING -    1 means that a call to RecomputePlacement
  120.  *                is already pending via a Do_When_Idle handler.
  121.  */
  122.  
  123. #define PARENT_RECONFIG_PENDING    1
  124.  
  125. /*
  126.  * The hash tables below both use Tk_Window tokens as keys.  They map
  127.  * from Tk_Windows to Slave and Master structures for windows, if they
  128.  * exist.
  129.  */
  130.  
  131. static int initialized = 0;
  132. static Tcl_HashTable masterTable;
  133. static Tcl_HashTable slaveTable;
  134.  
  135. /*
  136.  * Forward declarations for procedures defined later in this file:
  137.  */
  138.  
  139. static void        SlaveStructureProc _ANSI_ARGS_((ClientData clientData,
  140.                 XEvent *eventPtr));
  141. static int        ConfigureSlave _ANSI_ARGS_((Tcl_Interp *interp,
  142.                 Slave *slavePtr, int argc, char **argv));
  143. static Slave *        FindSlave _ANSI_ARGS_((Tk_Window tkwin));
  144. static Master *        FindMaster _ANSI_ARGS_((Tk_Window tkwin));
  145. static void        MasterStructureProc _ANSI_ARGS_((ClientData clientData,
  146.                 XEvent *eventPtr));
  147. static void        PlaceRequestProc _ANSI_ARGS_((ClientData clientData,
  148.                 Tk_Window tkwin));
  149. static void        RecomputePlacement _ANSI_ARGS_((ClientData clientData));
  150. static void        UnlinkSlave _ANSI_ARGS_((Slave *slavePtr));
  151.  
  152. /*
  153.  *--------------------------------------------------------------
  154.  *
  155.  * Tk_PlaceCmd --
  156.  *
  157.  *    This procedure is invoked to process the "place" Tcl
  158.  *    commands.  See the user documentation for details on
  159.  *    what it does.
  160.  *
  161.  * Results:
  162.  *    A standard Tcl result.
  163.  *
  164.  * Side effects:
  165.  *    See the user documentation.
  166.  *
  167.  *--------------------------------------------------------------
  168.  */
  169.  
  170. int
  171. Tk_PlaceCmd(clientData, interp, argc, argv)
  172.     ClientData clientData;    /* Main window associated with interpreter. */
  173.     Tcl_Interp *interp;        /* Current interpreter. */
  174.     int argc;            /* Number of arguments. */
  175.     char **argv;        /* Argument strings. */
  176. {
  177.     Tk_Window tkwin;
  178.     Slave *slavePtr;
  179.     Tcl_HashEntry *hPtr;
  180.     int length;
  181.     char c;
  182.  
  183.     /*
  184.      * Initialize, if that hasn't been done yet.
  185.      */
  186.  
  187.     if (!initialized) {
  188.     Tcl_InitHashTable(&masterTable, TCL_ONE_WORD_KEYS);
  189.     Tcl_InitHashTable(&slaveTable, TCL_ONE_WORD_KEYS);
  190.     initialized = 1;
  191.     }
  192.  
  193.     if (argc < 3) {
  194.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  195.         argv[0], " option|pathName args", (char *) NULL);
  196.     return TCL_ERROR;
  197.     }
  198.     c = argv[1][0];
  199.     length = strlen(argv[1]);
  200.  
  201.     /*
  202.      * Handle special shortcut where window name is first argument.
  203.      */
  204.  
  205.     if (c == '.') {
  206.     tkwin = Tk_NameToWindow(interp, argv[1], (Tk_Window) clientData);
  207.     if (tkwin == NULL) {
  208.         return TCL_ERROR;
  209.     }
  210.     slavePtr = FindSlave(tkwin);
  211.     return ConfigureSlave(interp, slavePtr, argc-2, argv+2);
  212.     }
  213.  
  214.     /*
  215.      * Handle more general case of option followed by window name followed
  216.      * by possible additional arguments.
  217.      */
  218.  
  219.     tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
  220.     if (tkwin == NULL) {
  221.     return TCL_ERROR;
  222.     }
  223.     if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  224.     if (argc < 5) {
  225.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  226.             argv[0],
  227.             " configure pathName option value ?option value ...?\"",
  228.             (char *) NULL);
  229.         return TCL_ERROR;
  230.     }
  231.     slavePtr = FindSlave(tkwin);
  232.     return ConfigureSlave(interp, slavePtr, argc-3, argv+3);
  233.     } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
  234.     if (argc != 3) {
  235.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  236.             argv[0], " forget pathName\"", (char *) NULL);
  237.         return TCL_ERROR;
  238.     }
  239.     hPtr = Tcl_FindHashEntry(&slaveTable, (char *) tkwin);
  240.     if (hPtr == NULL) {
  241.         return TCL_OK;
  242.     }
  243.     slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
  244.     UnlinkSlave(slavePtr);
  245.     Tcl_DeleteHashEntry(hPtr);
  246.     Tk_DeleteEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc,
  247.         (ClientData) slavePtr);
  248.     Tk_ManageGeometry(tkwin, (Tk_GeometryProc *) NULL, (ClientData) NULL);
  249.     Tk_UnmapWindow(tkwin);
  250.     ckfree((char *) slavePtr);
  251.     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
  252.     char buffer[50];
  253.  
  254.     if (argc != 3) {
  255.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  256.             argv[0], " info pathName\"", (char *) NULL);
  257.         return TCL_ERROR;
  258.     }
  259.     hPtr = Tcl_FindHashEntry(&slaveTable, (char *) tkwin);
  260.     if (hPtr == NULL) {
  261.         return TCL_OK;
  262.     }
  263.     slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
  264.     if (slavePtr->flags & CHILD_REL_X) {
  265.         sprintf(buffer, "-relx %.4g", slavePtr->relX);
  266.     } else {
  267.         sprintf(buffer, "-x %d", slavePtr->x);
  268.     }
  269.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  270.     if (slavePtr->flags & CHILD_REL_Y) {
  271.         sprintf(buffer, " -rely %.4g", slavePtr->relY);
  272.     } else {
  273.         sprintf(buffer, " -y %d", slavePtr->y);
  274.     }
  275.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  276.     if (slavePtr->flags & CHILD_REL_WIDTH) {
  277.         sprintf(buffer, " -relwidth %.4g", slavePtr->relWidth);
  278.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  279.     } else if (slavePtr->flags & CHILD_WIDTH) {
  280.         sprintf(buffer, " -width %d", slavePtr->width);
  281.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  282.     }
  283.     if (slavePtr->flags & CHILD_REL_HEIGHT) {
  284.         sprintf(buffer, " -relheight %.4g", slavePtr->relHeight);
  285.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  286.     } else if (slavePtr->flags & CHILD_HEIGHT) {
  287.         sprintf(buffer, " -height %d", slavePtr->height);
  288.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  289.     }
  290.     Tcl_AppendResult(interp, " -anchor ", Tk_NameOfAnchor(slavePtr->anchor),
  291.         (char *) NULL);
  292.     if (slavePtr->borderMode == BM_OUTSIDE) {
  293.         Tcl_AppendResult(interp, " -bordermode outside", (char *) NULL);
  294.     } else if (slavePtr->borderMode == BM_IGNORE) {
  295.         Tcl_AppendResult(interp, " -bordermode ignore", (char *) NULL);
  296.     }
  297.     if ((slavePtr->masterPtr != NULL)
  298.         && (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin))) {
  299.         Tcl_AppendResult(interp, " -in ",
  300.             Tk_PathName(slavePtr->masterPtr->tkwin), (char *) NULL);
  301.     }
  302.     } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
  303.     if (argc != 3) {
  304.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  305.             argv[0], " slaves pathName\"", (char *) NULL);
  306.         return TCL_ERROR;
  307.     }
  308.     hPtr = Tcl_FindHashEntry(&masterTable, (char *) tkwin);
  309.     if (hPtr != NULL) {
  310.         Master *masterPtr;
  311.         masterPtr = (Master *) Tcl_GetHashValue(hPtr);
  312.         for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  313.             slavePtr = slavePtr->nextPtr) {
  314.         Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin));
  315.         }
  316.     }
  317.     } else {
  318.     Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
  319.         "\": must be configure, forget, info, or slaves",
  320.         (char *) NULL);
  321.     return TCL_ERROR;
  322.     }
  323.     return TCL_OK;
  324. }
  325.  
  326. /*
  327.  *----------------------------------------------------------------------
  328.  *
  329.  * FindSlave --
  330.  *
  331.  *    Given a Tk_Window token, find the Slave structure corresponding
  332.  *    to that token (making a new one if necessary).
  333.  *
  334.  * Results:
  335.  *    None.
  336.  *
  337.  * Side effects:
  338.  *    A new Slave structure may be created.
  339.  *
  340.  *----------------------------------------------------------------------
  341.  */
  342.  
  343. static Slave *
  344. FindSlave(tkwin)
  345.     Tk_Window tkwin;        /* Token for desired slave. */
  346. {
  347.     Tcl_HashEntry *hPtr;
  348.     register Slave *slavePtr;
  349.     int new;
  350.  
  351.     hPtr = Tcl_CreateHashEntry(&slaveTable, (char *) tkwin, &new);
  352.     if (new) {
  353.     slavePtr = (Slave *) ckalloc(sizeof(Slave));
  354.     slavePtr->tkwin = tkwin;
  355.     slavePtr->masterPtr = NULL;
  356.     slavePtr->nextPtr = NULL;
  357.     slavePtr->x = slavePtr->y = 0;
  358.     slavePtr->relX = slavePtr->relY = 0.0;
  359.     slavePtr->width = slavePtr->height = 0;
  360.     slavePtr->relWidth = slavePtr->relHeight = 0.0;
  361.     slavePtr->anchor = TK_ANCHOR_NW;
  362.     slavePtr->borderMode = BM_INSIDE;
  363.     slavePtr->flags = 0;
  364.     Tcl_SetHashValue(hPtr, slavePtr);
  365.     Tk_CreateEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc,
  366.         (ClientData) slavePtr);
  367.     Tk_ManageGeometry(tkwin, PlaceRequestProc, (ClientData) slavePtr);
  368.     } else {
  369.     slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
  370.     }
  371.     return slavePtr;
  372. }
  373.  
  374. /*
  375.  *----------------------------------------------------------------------
  376.  *
  377.  * UnlinkSlave --
  378.  *
  379.  *    This procedure removes a slave window from the chain of slaves
  380.  *    in its master.
  381.  *
  382.  * Results:
  383.  *    None.
  384.  *
  385.  * Side effects:
  386.  *    The slave list of slavePtr's master changes.
  387.  *
  388.  *----------------------------------------------------------------------
  389.  */
  390.  
  391. static void
  392. UnlinkSlave(slavePtr)
  393.     Slave *slavePtr;        /* Slave structure to be unlinked. */
  394. {
  395.     register Master *masterPtr;
  396.     register Slave *prevPtr;
  397.  
  398.     masterPtr = slavePtr->masterPtr;
  399.     if (masterPtr == NULL) {
  400.     return;
  401.     }
  402.     if (masterPtr->slavePtr == slavePtr) {
  403.     masterPtr->slavePtr = slavePtr->nextPtr;
  404.     } else {
  405.     for (prevPtr = masterPtr->slavePtr; ;
  406.         prevPtr = prevPtr->nextPtr) {
  407.         if (prevPtr == NULL) {
  408.         panic("UnlinkSlave couldn't find slave to unlink");
  409.         }
  410.         if (prevPtr->nextPtr == slavePtr) {
  411.         prevPtr->nextPtr = slavePtr->nextPtr;
  412.         break;
  413.         }
  414.     }
  415.     }
  416.     slavePtr->masterPtr = NULL;
  417. }
  418.  
  419. /*
  420.  *----------------------------------------------------------------------
  421.  *
  422.  * FindMaster --
  423.  *
  424.  *    Given a Tk_Window token, find the Master structure corresponding
  425.  *    to that token (making a new one if necessary).
  426.  *
  427.  * Results:
  428.  *    None.
  429.  *
  430.  * Side effects:
  431.  *    A new Master structure may be created.
  432.  *
  433.  *----------------------------------------------------------------------
  434.  */
  435.  
  436. static Master *
  437. FindMaster(tkwin)
  438.     Tk_Window tkwin;        /* Token for desired master. */
  439. {
  440.     Tcl_HashEntry *hPtr;
  441.     register Master *masterPtr;
  442.     int new;
  443.  
  444.     hPtr = Tcl_CreateHashEntry(&masterTable, (char *) tkwin, &new);
  445.     if (new) {
  446.     masterPtr = (Master *) ckalloc(sizeof(Master));
  447.     masterPtr->tkwin = tkwin;
  448.     masterPtr->slavePtr = NULL;
  449.     masterPtr->flags = 0;
  450.     Tcl_SetHashValue(hPtr, masterPtr);
  451.     Tk_CreateEventHandler(masterPtr->tkwin, StructureNotifyMask,
  452.         MasterStructureProc, (ClientData) masterPtr);
  453.     } else {
  454.     masterPtr = (Master *) Tcl_GetHashValue(hPtr);
  455.     }
  456.     return masterPtr;
  457. }
  458.  
  459. /*
  460.  *----------------------------------------------------------------------
  461.  *
  462.  * ConfigureSlave --
  463.  *
  464.  *    This procedure is called to process an argv/argc list to
  465.  *    reconfigure the placement of a window.
  466.  *
  467.  * Results:
  468.  *    A standard Tcl result.  If an error occurs then a message is
  469.  *    left in interp->result.
  470.  *
  471.  * Side effects:
  472.  *    Information in slavePtr may change, and slavePtr's master is
  473.  *    scheduled for reconfiguration.
  474.  *
  475.  *----------------------------------------------------------------------
  476.  */
  477.  
  478. static int
  479. ConfigureSlave(interp, slavePtr, argc, argv)
  480.     Tcl_Interp *interp;        /* Used for error reporting. */
  481.     Slave *slavePtr;        /* Pointer to current information
  482.                  * about slave. */
  483.     int argc;            /* Number of config arguments. */
  484.     char **argv;        /* String values for arguments. */
  485. {
  486.     register Master *masterPtr;
  487.     int c, length, result;
  488.     double d;
  489.  
  490.     result = TCL_OK;
  491.     if (Tk_IsTopLevel(slavePtr->tkwin)) {
  492.     Tcl_AppendResult(interp, "can't use placer on top-level window \"",
  493.         Tk_PathName(slavePtr->tkwin), "\"; use wm command instead",
  494.         (char *) NULL);
  495.     return TCL_ERROR;
  496.     }
  497.     for ( ; argc > 0; argc -= 2, argv += 2) {
  498.     if (argc < 2) {
  499.         Tcl_AppendResult(interp, "extra option \"", argv[0],
  500.             "\" (option with no value?)", (char *) NULL);
  501.         result = TCL_ERROR;
  502.         goto done;
  503.     }
  504.     length = strlen(argv[0]);
  505.     c = argv[0][1];
  506.     if ((c == 'a') && (strncmp(argv[0], "-anchor", length) == 0)) {
  507.         if (Tk_GetAnchor(interp, argv[1], &slavePtr->anchor) != TCL_OK) {
  508.         result = TCL_ERROR;
  509.         goto done;
  510.         }
  511.     } else if ((c == 'b')
  512.         && (strncmp(argv[0], "-bordermode", length) == 0)) {
  513.         c = argv[1][0];
  514.         length = strlen(argv[1]);
  515.         if ((c == 'i') && (strncmp(argv[1], "ignore", length) == 0)
  516.             && (length >= 2)) {
  517.         slavePtr->borderMode = BM_IGNORE;
  518.         } else if ((c == 'i') && (strncmp(argv[1], "inside", length) == 0)
  519.             && (length >= 2)) {
  520.         slavePtr->borderMode = BM_INSIDE;
  521.         } else if ((c == 'o')
  522.             && (strncmp(argv[1], "outside", length) == 0)) {
  523.         slavePtr->borderMode = BM_OUTSIDE;
  524.         } else {
  525.         Tcl_AppendResult(interp, "bad border mode \"", argv[1],
  526.             "\": must be ignore, inside, or outside",
  527.             (char *) NULL);
  528.         result = TCL_ERROR;
  529.         goto done;
  530.         }
  531.     } else if ((c == 'h') && (strncmp(argv[0], "-height", length) == 0)) {
  532.         if (argv[1][0] == 0) {
  533.         slavePtr->flags &= ~(CHILD_REL_HEIGHT|CHILD_HEIGHT);
  534.         } else {
  535.         if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
  536.             &slavePtr->height) != TCL_OK) {
  537.             result = TCL_ERROR;
  538.             goto done;
  539.         }
  540.         slavePtr->flags &= ~CHILD_REL_HEIGHT;
  541.         slavePtr->flags |= CHILD_HEIGHT;
  542.         }
  543.     } else if ((c == 'i') && (strncmp(argv[0], "-in", length) == 0)) {
  544.         Tk_Window tkwin;
  545.         Tk_Window ancestor;
  546.  
  547.         tkwin = Tk_NameToWindow(interp, argv[1], slavePtr->tkwin);
  548.         if (tkwin == NULL) {
  549.         result = TCL_ERROR;
  550.         goto done;
  551.         }
  552.  
  553.         /*
  554.          * Make sure that the new master is either the logical parent
  555.          * of the slave or a descendant of that window.
  556.          */
  557.  
  558.         for (ancestor = tkwin; ; ancestor = Tk_Parent(ancestor)) {
  559.         if (ancestor == Tk_Parent(slavePtr->tkwin)) {
  560.             break;
  561.         }
  562.         if (Tk_IsTopLevel(ancestor)) {
  563.             Tcl_AppendResult(interp, "can't place ",
  564.                 Tk_PathName(slavePtr->tkwin), " relative to ",
  565.                 Tk_PathName(tkwin), (char *) NULL);
  566.             result = TCL_ERROR;
  567.             goto done;
  568.         }
  569.         }
  570.         UnlinkSlave(slavePtr);
  571.         slavePtr->masterPtr = FindMaster(tkwin);
  572.         slavePtr->nextPtr = slavePtr->masterPtr->slavePtr;
  573.         slavePtr->masterPtr->slavePtr = slavePtr;
  574.     } else if ((c == 'r') && (strncmp(argv[0], "-relheight", length) == 0)
  575.         && (length >= 5)) {
  576.         if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
  577.         result = TCL_ERROR;
  578.         goto done;
  579.         }
  580.         slavePtr->relHeight = d;
  581.         slavePtr->flags |= CHILD_REL_HEIGHT;
  582.         slavePtr->flags &= ~CHILD_HEIGHT;
  583.     } else if ((c == 'r') && (strncmp(argv[0], "-relwidth", length) == 0)
  584.         && (length >= 5)) {
  585.         if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
  586.         result = TCL_ERROR;
  587.         goto done;
  588.         }
  589.         slavePtr->relWidth = d;
  590.         slavePtr->flags |= CHILD_REL_WIDTH;
  591.         slavePtr->flags &= ~CHILD_WIDTH;
  592.     } else if ((c == 'r') && (strncmp(argv[0], "-relx", length) == 0)
  593.         && (length >= 5)) {
  594.         if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
  595.         result = TCL_ERROR;
  596.         goto done;
  597.         }
  598.         slavePtr->relX = d;
  599.         slavePtr->flags |= CHILD_REL_X;
  600.     } else if ((c == 'r') && (strncmp(argv[0], "-rely", length) == 0)
  601.         && (length >= 5)) {
  602.         if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
  603.         result = TCL_ERROR;
  604.         goto done;
  605.         }
  606.         slavePtr->relY = d;
  607.         slavePtr->flags |= CHILD_REL_Y;
  608.     } else if ((c == 'w') && (strncmp(argv[0], "-width", length) == 0)) {
  609.         if (argv[1][0] == 0) {
  610.         slavePtr->flags &= ~(CHILD_REL_WIDTH|CHILD_WIDTH);
  611.         } else {
  612.         if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
  613.             &slavePtr->width) != TCL_OK) {
  614.             result = TCL_ERROR;
  615.             goto done;
  616.         }
  617.         slavePtr->flags &= ~CHILD_REL_WIDTH;
  618.         slavePtr->flags |= CHILD_WIDTH;
  619.         }
  620.     } else if ((c == 'x') && (strncmp(argv[0], "-x", length) == 0)) {
  621.         if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
  622.             &slavePtr->x) != TCL_OK) {
  623.         result = TCL_ERROR;
  624.         goto done;
  625.         }
  626.         slavePtr->flags &= ~CHILD_REL_X;
  627.     } else if ((c == 'y') && (strncmp(argv[0], "-y", length) == 0)) {
  628.         if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
  629.             &slavePtr->y) != TCL_OK) {
  630.         result = TCL_ERROR;
  631.         goto done;
  632.         }
  633.         slavePtr->flags &= ~CHILD_REL_Y;
  634.     } else {
  635.         Tcl_AppendResult(interp, "unknown or ambiguous option \"",
  636.             argv[0], "\": must be -anchor, -bordermode, -height, ",
  637.             "-in, -relheight, -relwidth, -relx, -rely, -width, ",
  638.             "-x, or -y", (char *) NULL);
  639.         result = TCL_ERROR;
  640.         goto done;
  641.     }
  642.     }
  643.  
  644.     /*
  645.      * If there's no master specified for this slave, use its Tk_Parent.
  646.      * Then arrange for a placement recalculation in the master.
  647.      */
  648.  
  649.     done:
  650.     masterPtr = slavePtr->masterPtr;
  651.     if (masterPtr == NULL) {
  652.     masterPtr = FindMaster(Tk_Parent(slavePtr->tkwin));
  653.     slavePtr->masterPtr = masterPtr;
  654.     slavePtr->nextPtr = masterPtr->slavePtr;
  655.     masterPtr->slavePtr = slavePtr;
  656.     }
  657.     if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
  658.     masterPtr->flags |= PARENT_RECONFIG_PENDING;
  659.     Tk_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
  660.     }
  661.     return result;
  662. }
  663.  
  664. /*
  665.  *----------------------------------------------------------------------
  666.  *
  667.  * RecomputePlacement --
  668.  *
  669.  *    This procedure is called as a when-idle handler.  It recomputes
  670.  *    the geometries of all the slaves of a given master.
  671.  *
  672.  * Results:
  673.  *    None.
  674.  *
  675.  * Side effects:
  676.  *    Windows may change size or shape.
  677.  *
  678.  *----------------------------------------------------------------------
  679.  */
  680.  
  681. static void
  682. RecomputePlacement(clientData)
  683.     ClientData clientData;    /* Pointer to Master record. */
  684. {
  685.     register Master *masterPtr = (Master *) clientData;
  686.     register Slave *slavePtr;
  687.     Tk_Window ancestor, realMaster;
  688.     int x, y, width, height;
  689.     int masterWidth, masterHeight, masterBW;
  690.  
  691.     masterPtr->flags &= ~PARENT_RECONFIG_PENDING;
  692.  
  693.     /*
  694.      * Iterate over all the slaves for the master.  Each slave's
  695.      * geometry can be computed independently of the other slaves.
  696.      */
  697.  
  698.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  699.         slavePtr = slavePtr->nextPtr) {
  700.     /*
  701.      * Step 1: compute size and borderwidth of master, taking into
  702.      * account desired border mode.
  703.      */
  704.  
  705.     masterBW = 0;
  706.     masterWidth = Tk_Width(masterPtr->tkwin);
  707.     masterHeight = Tk_Height(masterPtr->tkwin);
  708.     if (slavePtr->borderMode == BM_INSIDE) {
  709.         masterBW = Tk_InternalBorderWidth(masterPtr->tkwin);
  710.     } else if (slavePtr->borderMode == BM_OUTSIDE) {
  711.         masterBW = -Tk_Changes(masterPtr->tkwin)->border_width;
  712.     }
  713.     masterWidth -= 2*masterBW;
  714.     masterHeight -= 2*masterBW;
  715.  
  716.     /*
  717.      * Step 2:  compute size of slave (outside dimensions including
  718.      * border) and location of anchor point within master.
  719.      */
  720.  
  721.     x = slavePtr->x;
  722.     if (slavePtr->flags & CHILD_REL_X) {
  723.         x = (slavePtr->relX*masterWidth) +
  724.         ((slavePtr->relX > 0) ? 0.5 : -0.5);
  725.     }
  726.     x += masterBW;
  727.     y = slavePtr->y;
  728.     if (slavePtr->flags & CHILD_REL_Y) {
  729.         y = (slavePtr->relY*masterHeight) +
  730.         ((slavePtr->relY > 0) ? 0.5 : -0.5);
  731.     }
  732.     y += masterBW;
  733.     if (slavePtr->flags & CHILD_REL_WIDTH) {
  734.         width = (slavePtr->relWidth*masterWidth) + 0.5;
  735.     } else if (slavePtr->flags & CHILD_WIDTH) {
  736.         width = slavePtr->width;
  737.     } else {
  738.         width = Tk_ReqWidth(slavePtr->tkwin)
  739.             + 2*Tk_Changes(slavePtr->tkwin)->border_width;
  740.     }
  741.     if (slavePtr->flags & CHILD_REL_HEIGHT) {
  742.         height = (slavePtr->relHeight*masterHeight) + 0.5;
  743.     } else if (slavePtr->flags & CHILD_HEIGHT) {
  744.         height = slavePtr->height;
  745.     } else {
  746.         height = Tk_ReqHeight(slavePtr->tkwin)
  747.             + 2*Tk_Changes(slavePtr->tkwin)->border_width;
  748.     }
  749.  
  750.     /*
  751.      * Step 3: adjust the x and y positions so that the desired
  752.      * anchor point on the slave appears at that position.  Also
  753.      * adjust for the border mode and master's border.
  754.      */
  755.  
  756.     switch (slavePtr->anchor) {
  757.         case TK_ANCHOR_N:
  758.         x -= width/2;
  759.         break;
  760.         case TK_ANCHOR_NE:
  761.         x -= width;
  762.         break;
  763.         case TK_ANCHOR_E:
  764.         x -= width;
  765.         y -= height/2;
  766.         break;
  767.         case TK_ANCHOR_SE:
  768.         x -= width;
  769.         y -= height;
  770.         break;
  771.         case TK_ANCHOR_S:
  772.         x -= width/2;
  773.         y -= height;
  774.         break;
  775.         case TK_ANCHOR_SW:
  776.         y -= height;
  777.         break;
  778.         case TK_ANCHOR_W:
  779.         y -= height/2;
  780.         break;
  781.         case TK_ANCHOR_NW:
  782.         break;
  783.         case TK_ANCHOR_CENTER:
  784.         x -= width/2;
  785.         y -= height/2;
  786.         break;
  787.     }
  788.  
  789.     /*
  790.      * Step 4: if masterPtr isn't actually the X master of slavePtr,
  791.      * then translate the x and y coordinates back into the coordinate
  792.      * system of masterPtr.
  793.      */
  794.  
  795.     for (ancestor = masterPtr->tkwin,
  796.         realMaster = Tk_Parent(slavePtr->tkwin);
  797.         ancestor != realMaster; ancestor = Tk_Parent(ancestor)) {
  798.         x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width;
  799.         y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width;
  800.     }
  801.  
  802.     /*
  803.      * Step 5: adjust width and height again to reflect inside dimensions
  804.      * of window rather than outside.  Also make sure that the width and
  805.      * height aren't zero.
  806.      */
  807.  
  808.     width -= 2*Tk_Changes(slavePtr->tkwin)->border_width;
  809.     height -= 2*Tk_Changes(slavePtr->tkwin)->border_width;
  810.     if (width <= 0) {
  811.         width = 1;
  812.     }
  813.     if (height <= 0) {
  814.         height = 1;
  815.     }
  816.  
  817.     /*
  818.      * Step 6: see if the window's size or location has changed;  if
  819.      * so then tell X to reconfigure it.
  820.      */
  821.  
  822.     if ((x != Tk_X(slavePtr->tkwin))
  823.         || (y != Tk_Y(slavePtr->tkwin))
  824.         || (width != Tk_Width(slavePtr->tkwin))
  825.         || (height != Tk_Height(slavePtr->tkwin))) {
  826.         Tk_MoveResizeWindow(slavePtr->tkwin, x, y,
  827.             (unsigned int) width, (unsigned int) height);
  828.     }
  829.     Tk_MapWindow(slavePtr->tkwin);
  830.     }
  831. }
  832.  
  833. /*
  834.  *----------------------------------------------------------------------
  835.  *
  836.  * MasterStructureProc --
  837.  *
  838.  *    This procedure is invoked by the Tk event handler when
  839.  *    StructureNotify events occur for a master window.
  840.  *
  841.  * Results:
  842.  *    None.
  843.  *
  844.  * Side effects:
  845.  *    Structures get cleaned up if the window was deleted.  If the
  846.  *    window was resized then slave geometries get recomputed.
  847.  *
  848.  *----------------------------------------------------------------------
  849.  */
  850.  
  851. static void
  852. MasterStructureProc(clientData, eventPtr)
  853.     ClientData clientData;    /* Pointer to Master structure for window
  854.                  * referred to by eventPtr. */
  855.     XEvent *eventPtr;        /* Describes what just happened. */
  856. {
  857.     register Master *masterPtr = (Master *) clientData;
  858.     register Slave *slavePtr, *nextPtr;
  859.  
  860.     if (eventPtr->type == ConfigureNotify) {
  861.     if ((masterPtr->slavePtr != NULL)
  862.         && !(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
  863.         masterPtr->flags |= PARENT_RECONFIG_PENDING;
  864.         Tk_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
  865.     }
  866.     } else if (eventPtr->type == DestroyNotify) {
  867.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  868.         slavePtr = nextPtr) {
  869.         slavePtr->masterPtr = NULL;
  870.         nextPtr = slavePtr->nextPtr;
  871.         slavePtr->nextPtr = NULL;
  872.     }
  873.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&masterTable,
  874.         (char *) masterPtr->tkwin));
  875.     if (masterPtr->flags & PARENT_RECONFIG_PENDING) {
  876.         Tk_CancelIdleCall(RecomputePlacement, (ClientData) masterPtr);
  877.     }
  878.     masterPtr->tkwin = NULL;
  879.     ckfree((char *) masterPtr);
  880.     }
  881. }
  882.  
  883. /*
  884.  *----------------------------------------------------------------------
  885.  *
  886.  * SlaveStructureProc --
  887.  *
  888.  *    This procedure is invoked by the Tk event handler when
  889.  *    StructureNotify events occur for a slave window.
  890.  *
  891.  * Results:
  892.  *    None.
  893.  *
  894.  * Side effects:
  895.  *    Structures get cleaned up if the window was deleted.
  896.  *
  897.  *----------------------------------------------------------------------
  898.  */
  899.  
  900. static void
  901. SlaveStructureProc(clientData, eventPtr)
  902.     ClientData clientData;    /* Pointer to Slave structure for window
  903.                  * referred to by eventPtr. */
  904.     XEvent *eventPtr;        /* Describes what just happened. */
  905. {
  906.     register Slave *slavePtr = (Slave *) clientData;
  907.  
  908.     if (eventPtr->type == DestroyNotify) {
  909.     UnlinkSlave(slavePtr);
  910.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&slaveTable,
  911.         (char *) slavePtr->tkwin));
  912.     ckfree((char *) slavePtr);
  913.     }
  914. }
  915.  
  916. /*
  917.  *----------------------------------------------------------------------
  918.  *
  919.  * PlaceRequestProc --
  920.  *
  921.  *    This procedure is invoked by Tk whenever a slave managed by us
  922.  *    changes its requested geometry.
  923.  *
  924.  * Results:
  925.  *    None.
  926.  *
  927.  * Side effects:
  928.  *    The window will get relayed out, if its requested size has
  929.  *    anything to do with its actual size.
  930.  *
  931.  *----------------------------------------------------------------------
  932.  */
  933.  
  934.     /* ARGSUSED */
  935. static void
  936. PlaceRequestProc(clientData, tkwin)
  937.     ClientData clientData;        /* Pointer to our record for slave. */
  938.     Tk_Window tkwin;            /* Window that changed its desired
  939.                      * size. */
  940. {
  941.     Slave *slavePtr = (Slave *) clientData;
  942.     Master *masterPtr;
  943.  
  944.     if (((slavePtr->flags & (CHILD_WIDTH|CHILD_REL_WIDTH)) != 0)
  945.         && ((slavePtr->flags & (CHILD_HEIGHT|CHILD_REL_HEIGHT)) != 0)) {
  946.     return;
  947.     }
  948.     masterPtr = slavePtr->masterPtr;
  949.     if (masterPtr == NULL) {
  950.     return;
  951.     }
  952.     if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
  953.     masterPtr->flags |= PARENT_RECONFIG_PENDING;
  954.     Tk_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
  955.     }
  956. }
  957.