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