home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / tk3.3b1 / tkPack.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-08  |  49.6 KB  |  1,708 lines

  1. /* 
  2.  * tkPack.c --
  3.  *
  4.  *    This file contains code to implement the "packer"
  5.  *    geometry manager for Tk.
  6.  *
  7.  * Copyright (c) 1990-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/tkPack.c,v 1.40 93/07/08 15:24:52 ouster Exp $ SPRITE (Berkeley)";
  30. #endif
  31.  
  32. #include "tkConfig.h"
  33. #include "tkInt.h"
  34.  
  35. typedef enum {TOP, BOTTOM, LEFT, RIGHT} Side;
  36.  
  37. /* For each window that the packer cares about (either because
  38.  * the window is managed by the packer or because the window
  39.  * has slaves that are managed by the packer), there is a
  40.  * structure of the following type:
  41.  */
  42.  
  43. typedef struct Packer {
  44.     Tk_Window tkwin;        /* Tk token for window.  NULL means that
  45.                  * the window has been deleted, but the
  46.                  * packet hasn't had a chance to clean up
  47.                  * yet because the structure is still in
  48.                  * use. */
  49.     struct Packer *masterPtr;    /* Master window within which this window
  50.                  * is packed (NULL means this window
  51.                  * isn't managed by the packer). */
  52.     struct Packer *nextPtr;    /* Next window packed within same
  53.                  * parent.  List is priority-ordered:
  54.                  * first on list gets packed first. */
  55.     struct Packer *slavePtr;    /* First in list of slaves packed
  56.                  * inside this window (NULL means
  57.                  * no packed slaves). */
  58.     Side side;            /* Side of parent against which
  59.                  * this window is packed. */
  60.     Tk_Anchor anchor;        /* If frame allocated for window is larger
  61.                  * than window needs, this indicates how
  62.                  * where to position window in frame. */
  63.     int padX, padY;        /* Total additional pixels to leave around the
  64.                  * window (half of this space is left on each
  65.                  * side).  This is space *outside* the window:
  66.                  * we'll allocate extra space in frame but
  67.                  * won't enlarge window). */
  68.     int iPadX, iPadY;        /* Total extra pixels to allocate inside the
  69.                  * window (half this amount will appear on
  70.                  * each side). */
  71.     int doubleBw;        /* Twice the window's last known border
  72.                  * width.  If this changes, the window
  73.                  * must be repacked within its parent. */
  74.     int *abortPtr;        /* If non-NULL, it means that there is a nested
  75.                  * call to ArrangePacking already working on
  76.                  * this window.  *abortPtr may be set to 1 to
  77.                  * abort that nested call.  This happens, for
  78.                  * example, if tkwin or any of its slaves
  79.                  * is deleted. */
  80.     int flags;            /* Miscellaneous flags;  see below
  81.                  * for definitions. */
  82. } Packer;
  83.  
  84. /*
  85.  * Flag values for Packer structures:
  86.  *
  87.  * REQUESTED_REPACK:        1 means a Tk_DoWhenIdle request
  88.  *                has already been made to repack
  89.  *                all the slaves of this window.
  90.  * FILLX:            1 means if frame allocated for window
  91.  *                is wider than window needs, expand window
  92.  *                to fill frame.  0 means don't make window
  93.  *                any larger than needed.
  94.  * FILLY:            Same as FILLX, except for height.
  95.  * EXPAND:            1 means this window's frame will absorb any
  96.  *                extra space in the parent window.
  97.  * OLD_STYLE:            1 means this window is being managed with
  98.  *                the old-style packer algorithms (before
  99.  *                Tk version 3.3).  The main difference is
  100.  *                that padding and filling are done differently.
  101.  * DONT_PROPAGATE:        1 means don't set this window's requested
  102.  *                size.  0 means if this window is a master
  103.  *                then Tk will set its requested size to fit
  104.  *                the needs of its slaves.
  105.  */
  106.  
  107. #define REQUESTED_REPACK    1
  108. #define FILLX            2
  109. #define FILLY            4
  110. #define EXPAND            8
  111. #define OLD_STYLE        16
  112. #define DONT_PROPAGATE        32
  113.  
  114. /*
  115.  * Hash table used to map from Tk_Window tokens to corresponding
  116.  * Packer structures:
  117.  */
  118.  
  119. static Tcl_HashTable packerHashTable;
  120.  
  121. /*
  122.  * Have statics in this module been initialized?
  123.  */
  124.  
  125. static initialized = 0;
  126.  
  127. /*
  128.  * Forward declarations for procedures defined later in this file:
  129.  */
  130.  
  131. static void        ArrangePacking _ANSI_ARGS_((ClientData clientData));
  132. static int        ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
  133.                 Tk_Window tkwin, int argc, char *argv[]));
  134. static Packer *        GetPacker _ANSI_ARGS_((Tk_Window tkwin));
  135. static int        PackAfter _ANSI_ARGS_((Tcl_Interp *interp,
  136.                 Packer *prevPtr, Packer *masterPtr, int argc,
  137.                 char **argv));
  138. static void        PackReqProc _ANSI_ARGS_((ClientData clientData,
  139.                 Tk_Window tkwin));
  140. static void        PackStructureProc _ANSI_ARGS_((ClientData clientData,
  141.                 XEvent *eventPtr));
  142. static void        Unlink _ANSI_ARGS_((Packer *packPtr));
  143. static int        XExpansion _ANSI_ARGS_((Packer *slavePtr,
  144.                 int cavityWidth));
  145. static int        YExpansion _ANSI_ARGS_((Packer *slavePtr,
  146.                 int cavityHeight));
  147.  
  148. /*
  149.  *--------------------------------------------------------------
  150.  *
  151.  * Tk_PackCmd --
  152.  *
  153.  *    This procedure is invoked to process the "pack" Tcl command.
  154.  *    See the user documentation for details on what it does.
  155.  *
  156.  * Results:
  157.  *    A standard Tcl result.
  158.  *
  159.  * Side effects:
  160.  *    See the user documentation.
  161.  *
  162.  *--------------------------------------------------------------
  163.  */
  164.  
  165. int
  166. Tk_PackCmd(clientData, interp, argc, argv)
  167.     ClientData clientData;    /* Main window associated with
  168.                  * interpreter. */
  169.     Tcl_Interp *interp;        /* Current interpreter. */
  170.     int argc;            /* Number of arguments. */
  171.     char **argv;        /* Argument strings. */
  172. {
  173.     Tk_Window tkwin = (Tk_Window) clientData;
  174.     int length;
  175.     char c;
  176.  
  177.     if ((argc >= 2) && (argv[1][0] == '.')) {
  178.     return ConfigureSlaves(interp, tkwin, argc-1, argv+1);
  179.     }
  180.     if (argc < 3) {
  181.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  182.         argv[0], " option arg ?arg ...?\"", (char *) NULL);
  183.     return TCL_ERROR;
  184.     }
  185.     c = argv[1][0];
  186.     length = strlen(argv[1]);
  187.     if ((c == 'a') && (length >= 2)
  188.         && (strncmp(argv[1], "after", length) == 0)) {
  189.     Packer *prevPtr;
  190.     Tk_Window tkwin2;
  191.  
  192.     tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
  193.     if (tkwin2 == NULL) {
  194.         return TCL_ERROR;
  195.     }
  196.     prevPtr = GetPacker(tkwin2);
  197.     if (prevPtr->masterPtr == NULL) {
  198.         Tcl_AppendResult(interp, "window \"", argv[2],
  199.             "\" isn't packed", (char *) NULL);
  200.         return TCL_ERROR;
  201.     }
  202.     return PackAfter(interp, prevPtr, prevPtr->masterPtr, argc-3, argv+3);
  203.     } else if ((c == 'a') && (length >= 2)
  204.         && (strncmp(argv[1], "append", length) == 0)) {
  205.     Packer *masterPtr;
  206.     register Packer *prevPtr;
  207.     Tk_Window tkwin2;
  208.  
  209.     tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
  210.     if (tkwin2 == NULL) {
  211.         return TCL_ERROR;
  212.     }
  213.     masterPtr = GetPacker(tkwin2);
  214.     prevPtr = masterPtr->slavePtr;
  215.     if (prevPtr != NULL) {
  216.         while (prevPtr->nextPtr != NULL) {
  217.         prevPtr = prevPtr->nextPtr;
  218.         }
  219.     }
  220.     return PackAfter(interp, prevPtr, masterPtr, argc-3, argv+3);
  221.     } else if ((c == 'b') && (strncmp(argv[1], "before", length) == 0)) {
  222.     Packer *packPtr, *masterPtr;
  223.     register Packer *prevPtr;
  224.     Tk_Window tkwin2;
  225.  
  226.     tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
  227.     if (tkwin2 == NULL) {
  228.         return TCL_ERROR;
  229.     }
  230.     packPtr = GetPacker(tkwin2);
  231.     if (packPtr->masterPtr == NULL) {
  232.         Tcl_AppendResult(interp, "window \"", argv[2],
  233.             "\" isn't packed", (char *) NULL);
  234.         return TCL_ERROR;
  235.     }
  236.     masterPtr = packPtr->masterPtr;
  237.     prevPtr = masterPtr->slavePtr;
  238.     if (prevPtr == packPtr) {
  239.         prevPtr = NULL;
  240.     } else {
  241.         for ( ; ; prevPtr = prevPtr->nextPtr) {
  242.         if (prevPtr == NULL) {
  243.             panic("\"pack before\" couldn't find predecessor");
  244.         }
  245.         if (prevPtr->nextPtr == packPtr) {
  246.             break;
  247.         }
  248.         }
  249.     }
  250.     return PackAfter(interp, prevPtr, masterPtr, argc-3, argv+3);
  251.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  252.     if (argv[2][0] != '.') {
  253.         Tcl_AppendResult(interp, "bad argument \"", argv[2],
  254.             "\": must be name of window", (char *) NULL);
  255.         return TCL_ERROR;
  256.     }
  257.     return ConfigureSlaves(interp, tkwin, argc-2, argv+2);
  258.     } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
  259.     Tk_Window slave;
  260.     Packer *slavePtr;
  261.     int i;
  262.  
  263.     for (i = 2; i < argc; i++) {
  264.         slave = Tk_NameToWindow(interp, argv[i], tkwin);
  265.         if (slave == NULL) {
  266.         continue;
  267.         }
  268.         slavePtr = GetPacker(slave);
  269.         if ((slavePtr != NULL) && (slavePtr->masterPtr != NULL)) {
  270.         Tk_ManageGeometry(slave, (Tk_GeometryProc *) NULL,
  271.             (ClientData) NULL);
  272.         Unlink(slavePtr);
  273.         Tk_UnmapWindow(slavePtr->tkwin);
  274.         }
  275.     }
  276.     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
  277.     char *prefix;
  278.     register Packer *slavePtr;
  279.     Tk_Window tkwin2;
  280.     char tmp[20];
  281.     static char *sideNames[] = {"top", "bottom", "left", "right"};
  282.     int pad;
  283.  
  284.     if (argc != 3) {
  285.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  286.             argv[0], " info window\"", (char *) NULL);
  287.         return TCL_ERROR;
  288.     }
  289.     tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
  290.     if (tkwin2 == NULL) {
  291.         return TCL_ERROR;
  292.     }
  293.     slavePtr = GetPacker(tkwin2);
  294.     prefix = "";
  295.     for (slavePtr = slavePtr->slavePtr; slavePtr != NULL;
  296.         slavePtr = slavePtr->nextPtr) {
  297.         Tcl_AppendResult(interp, prefix, Tk_PathName(slavePtr->tkwin),
  298.             " {", sideNames[(int) slavePtr->side],
  299.             " frame ", Tk_NameOfAnchor(slavePtr->anchor),
  300.             (char *) NULL);
  301.         pad = slavePtr->padX + slavePtr->iPadX;
  302.         if (pad != 0) {
  303.         sprintf(tmp, "%d", pad);
  304.         Tcl_AppendResult(interp, " padx ", tmp, (char *) NULL);
  305.         }
  306.         pad = slavePtr->padY + slavePtr->iPadY;
  307.         if (pad != 0) {
  308.         sprintf(tmp, "%d", pad);
  309.         Tcl_AppendResult(interp, " pady ", tmp, (char *) NULL);
  310.         }
  311.         if (slavePtr->flags & EXPAND) {
  312.         Tcl_AppendResult(interp, " expand", (char *) NULL);
  313.         }
  314.         if ((slavePtr->flags & (FILLX|FILLY)) == (FILLX|FILLY)) {
  315.         Tcl_AppendResult(interp, " fill", (char *) NULL);
  316.         } else if (slavePtr->flags & FILLX) {
  317.         Tcl_AppendResult(interp, " fillx", (char *) NULL);
  318.         } else if (slavePtr->flags & FILLY) {
  319.         Tcl_AppendResult(interp, " filly", (char *) NULL);
  320.         }
  321.         Tcl_AppendResult(interp, "}", (char *) NULL);
  322.         prefix = " ";
  323.     }
  324.     return TCL_OK;
  325.     } else if ((c == 'n') && (strncmp(argv[1], "newinfo", length) == 0)) {
  326.     register Packer *slavePtr;
  327.     Tk_Window slave;
  328.     char buffer[300];
  329.     static char *sideNames[] = {"top", "bottom", "left", "right"};
  330.  
  331.     if (argc != 3) {
  332.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  333.             argv[0], " info window\"", (char *) NULL);
  334.         return TCL_ERROR;
  335.     }
  336.     slave = Tk_NameToWindow(interp, argv[2], tkwin);
  337.     if (slave == NULL) {
  338.         return TCL_ERROR;
  339.     }
  340.     slavePtr = GetPacker(slave);
  341.     if (slavePtr->masterPtr == NULL) {
  342.         Tcl_AppendResult(interp, "window \"", argv[2],
  343.             "\" isn't packed", (char *) NULL);
  344.         return TCL_ERROR;
  345.     }
  346.     Tcl_AppendElement(interp, "-in");
  347.     Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin));
  348.     Tcl_AppendElement(interp, "-anchor");
  349.     Tcl_AppendElement(interp, Tk_NameOfAnchor(slavePtr->anchor));
  350.     Tcl_AppendResult(interp, " -expand ",
  351.         (slavePtr->flags & EXPAND) ? "1" : "0", " -fill ",
  352.         (char *) NULL);
  353.     switch (slavePtr->flags & (FILLX|FILLY)) {
  354.         case 0:
  355.         Tcl_AppendResult(interp, "none", (char *) NULL);
  356.         break;
  357.         case FILLX:
  358.         Tcl_AppendResult(interp, "x", (char *) NULL);
  359.         break;
  360.         case FILLY:
  361.         Tcl_AppendResult(interp, "y", (char *) NULL);
  362.         break;
  363.         case FILLX|FILLY:
  364.         Tcl_AppendResult(interp, "both", (char *) NULL);
  365.         break;
  366.     }
  367.     sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
  368.         slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
  369.         slavePtr->padY/2);
  370.     Tcl_AppendResult(interp, buffer, " -side ", sideNames[slavePtr->side],
  371.         (char *) NULL);
  372.     } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
  373.     Tk_Window master;
  374.     Packer *masterPtr;
  375.     int propagate;
  376.  
  377.     if (argc > 4) {
  378.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  379.             argv[0], " propagate window ?boolean?\"", (char *) NULL);
  380.         return TCL_ERROR;
  381.     }
  382.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  383.     if (master == NULL) {
  384.         return TCL_ERROR;
  385.     }
  386.     masterPtr = GetPacker(master);
  387.     if (argc == 3) {
  388.         if (masterPtr->flags & DONT_PROPAGATE) {
  389.         interp->result = "0";
  390.         } else {
  391.         interp->result = "1";
  392.         }
  393.         return TCL_OK;
  394.     }
  395.     if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
  396.         return TCL_ERROR;
  397.     }
  398.     if (propagate) {
  399.         masterPtr->flags &= ~DONT_PROPAGATE;
  400.  
  401.         /*
  402.          * Repack the master to allow new geometry information to
  403.          * propagate upwards to the master's master.
  404.          */
  405.  
  406.         if (masterPtr->abortPtr != NULL) {
  407.         *masterPtr->abortPtr = 1;
  408.         }
  409.         if (!(masterPtr->flags & REQUESTED_REPACK)) {
  410.         masterPtr->flags |= REQUESTED_REPACK;
  411.         Tk_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
  412.         }
  413.     } else {
  414.         masterPtr->flags |= DONT_PROPAGATE;
  415.     }
  416.     } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
  417.     Tk_Window master;
  418.     Packer *masterPtr, *slavePtr;
  419.  
  420.     if (argc != 3) {
  421.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  422.             argv[0], " slaves window\"", (char *) NULL);
  423.         return TCL_ERROR;
  424.     }
  425.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  426.     if (master == NULL) {
  427.         return TCL_ERROR;
  428.     }
  429.     masterPtr = GetPacker(master);
  430.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  431.         slavePtr = slavePtr->nextPtr) {
  432.         Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin));
  433.     }
  434.     } else if ((c == 'u') && (strncmp(argv[1], "unpack", length) == 0)) {
  435.     Tk_Window tkwin2;
  436.     Packer *packPtr;
  437.  
  438.     if (argc != 3) {
  439.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  440.             argv[0], " unpack window\"", (char *) NULL);
  441.         return TCL_ERROR;
  442.     }
  443.     tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
  444.     if (tkwin2 == NULL) {
  445.         return TCL_ERROR;
  446.     }
  447.     packPtr = GetPacker(tkwin2);
  448.     if ((packPtr != NULL) && (packPtr->masterPtr != NULL)) {
  449.         Tk_ManageGeometry(tkwin2, (Tk_GeometryProc *) NULL,
  450.             (ClientData) NULL);
  451.         Unlink(packPtr);
  452.         Tk_UnmapWindow(packPtr->tkwin);
  453.     }
  454.     } else {
  455.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  456.         "\":  must be configure, forget, info, newinfo, ",
  457.         "propagate, or slaves", (char *) NULL);
  458.     return TCL_ERROR;
  459.     }
  460.     return TCL_OK;
  461. }
  462.  
  463. /*
  464.  *--------------------------------------------------------------
  465.  *
  466.  * PackReqProc --
  467.  *
  468.  *    This procedure is invoked by Tk_GeometryRequest for
  469.  *    windows managed by the packer.
  470.  *
  471.  * Results:
  472.  *    None.
  473.  *
  474.  * Side effects:
  475.  *    Arranges for tkwin, and all its managed siblings, to
  476.  *    be re-packed at the next idle point.
  477.  *
  478.  *--------------------------------------------------------------
  479.  */
  480.  
  481.     /* ARGSUSED */
  482. static void
  483. PackReqProc(clientData, tkwin)
  484.     ClientData clientData;    /* Packer's information about
  485.                  * window that got new preferred
  486.                  * geometry.  */
  487.     Tk_Window tkwin;        /* Other Tk-related information
  488.                  * about the window. */
  489. {
  490.     register Packer *packPtr = (Packer *) clientData;
  491.  
  492.     packPtr = packPtr->masterPtr;
  493.     if (!(packPtr->flags & REQUESTED_REPACK)) {
  494.     packPtr->flags |= REQUESTED_REPACK;
  495.     Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
  496.     }
  497. }
  498.  
  499. /*
  500.  *--------------------------------------------------------------
  501.  *
  502.  * ArrangePacking --
  503.  *
  504.  *    This procedure is invoked (using the Tk_DoWhenIdle
  505.  *    mechanism) to re-layout a set of windows managed by
  506.  *    the packer.  It is invoked at idle time so that a
  507.  *    series of packer requests can be merged into a single
  508.  *    layout operation.
  509.  *
  510.  * Results:
  511.  *    None.
  512.  *
  513.  * Side effects:
  514.  *    The packed slaves of masterPtr may get resized or
  515.  *    moved.
  516.  *
  517.  *--------------------------------------------------------------
  518.  */
  519.  
  520. static void
  521. ArrangePacking(clientData)
  522.     ClientData clientData;    /* Structure describing parent whose slaves
  523.                  * are to be re-layed out. */
  524. {
  525.     register Packer *masterPtr = (Packer *) clientData;
  526.     register Packer *slavePtr;    
  527.     int cavityX, cavityY, cavityWidth, cavityHeight;
  528.                 /* These variables keep track of the
  529.                  * as-yet-unallocated space remaining in
  530.                  * the middle of the parent window. */
  531.     int frameX, frameY, frameWidth, frameHeight;
  532.                 /* These variables keep track of the frame
  533.                  * allocated to the current window. */
  534.     int x, y, width, height;    /* These variables are used to hold the
  535.                  * actual geometry of the current window. */
  536.     int intBWidth;        /* Width of internal border in parent window,
  537.                  * if any. */
  538.     int abort;            /* May get set to non-zero to abort this
  539.                  * repacking operation. */
  540.     int borderX, borderY;
  541.     int maxWidth, maxHeight, tmp;
  542.     Tk_Window parent, ancestor;
  543.  
  544.     masterPtr->flags &= ~REQUESTED_REPACK;
  545.  
  546.     /*
  547.      * If the parent has no slaves anymore, then don't do anything
  548.      * at all:  just leave the parent's size as-is.
  549.      */
  550.  
  551.     if (masterPtr->slavePtr == NULL) {
  552.     return;
  553.     }
  554.  
  555.     /*
  556.      * Abort any nested call to ArrangePacking for this window, since
  557.      * we'll do everything necessary here, and set up so this call
  558.      * can be aborted if necessary.  
  559.      */
  560.  
  561.     if (masterPtr->abortPtr != NULL) {
  562.     *masterPtr->abortPtr = 1;
  563.     }
  564.     masterPtr->abortPtr = &abort;
  565.     abort = 0;
  566.     Tk_Preserve((ClientData) masterPtr);
  567.  
  568.     /*
  569.      * Pass #1: scan all the slaves to figure out the total amount
  570.      * of space needed.  Two separate width and height values are
  571.      * computed:
  572.      *
  573.      * width -        Holds the sum of the widths (plus padding) of
  574.      *            all the slaves seen so far that were packed LEFT
  575.      *            or RIGHT.
  576.      * height -        Holds the sum of the heights (plus padding) of
  577.      *            all the slaves seen so far that were packed TOP
  578.      *            or BOTTOM.
  579.      *
  580.      * maxWidth -    Gradually builds up the width needed by the master
  581.      *            to just barely satisfy all the slave's needs.  For
  582.      *            each slave, the code computes the width needed for
  583.      *            all the slaves so far and updates maxWidth if the
  584.      *            new value is greater.
  585.      * maxHeight -    Same as maxWidth, except keeps height info.
  586.      */
  587.  
  588.     intBWidth = Tk_InternalBorderWidth(masterPtr->tkwin);
  589.     width = height = maxWidth = maxHeight = 2*intBWidth;
  590.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  591.         slavePtr = slavePtr->nextPtr) {
  592.     if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
  593.         tmp = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
  594.             + slavePtr->padX + slavePtr->iPadX + width;
  595.         if (tmp > maxWidth) {
  596.         maxWidth = tmp;
  597.         }
  598.         height += Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
  599.             + slavePtr->padY + slavePtr->iPadY;
  600.     } else {
  601.         tmp = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
  602.             + slavePtr->padY + slavePtr->iPadY + height;
  603.         if (tmp > maxHeight) {
  604.         maxHeight = tmp;
  605.         }
  606.         width += Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
  607.             + slavePtr->padX + slavePtr->iPadX;
  608.     }
  609.     }
  610.     if (width > maxWidth) {
  611.     maxWidth = width;
  612.     }
  613.     if (height > maxHeight) {
  614.     maxHeight = height;
  615.     }
  616.  
  617.     /*
  618.      * If the total amount of space needed in the parent window has
  619.      * changed, and if we're propagating geometry information, then
  620.      * notify the next geometry manager up and requeue ourselves to
  621.      * start again after the parent has had a chance to
  622.      * resize us.
  623.      */
  624.  
  625.     if (((maxWidth != Tk_ReqWidth(masterPtr->tkwin))
  626.         || (maxHeight != Tk_ReqHeight(masterPtr->tkwin)))
  627.         && !(masterPtr->flags & DONT_PROPAGATE)) {
  628.     Tk_GeometryRequest(masterPtr->tkwin, maxWidth, maxHeight);
  629.     masterPtr->flags |= REQUESTED_REPACK;
  630.     Tk_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
  631.     goto done;
  632.     }
  633.  
  634.     /*
  635.      * If the parent isn't mapped then don't do anything more:  wait
  636.      * until it gets mapped again.  Need to get at least to here to
  637.      * reflect size needs up the window hierarchy, but there's no
  638.      * point in actually mapping the slaves.
  639.      */
  640.  
  641.     if (!Tk_IsMapped(masterPtr->tkwin)) {
  642.     goto done;
  643.     }
  644.  
  645.     /*
  646.      * Pass #2: scan the slaves a second time assigning
  647.      * new sizes.  The "cavity" variables keep track of the
  648.      * unclaimed space in the cavity of the window;  this
  649.      * shrinks inward as we allocate windows around the
  650.      * edges.  The "frame" variables keep track of the space
  651.      * allocated to the current window and its frame.  The
  652.      * current window is then placed somewhere inside the
  653.      * frame, depending on anchor.
  654.      */
  655.  
  656.     cavityX = cavityY = x = y = intBWidth;
  657.     cavityWidth = Tk_Width(masterPtr->tkwin) - 2*intBWidth;
  658.     cavityHeight = Tk_Height(masterPtr->tkwin) - 2*intBWidth;
  659.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  660.         slavePtr = slavePtr->nextPtr) {
  661.     if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
  662.         frameWidth = cavityWidth;
  663.         frameHeight = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
  664.             + slavePtr->padY + slavePtr->iPadY;
  665.         if (slavePtr->flags & EXPAND) {
  666.         frameHeight += YExpansion(slavePtr, cavityHeight);
  667.         }
  668.         cavityHeight -= frameHeight;
  669.         if (cavityHeight < 0) {
  670.         frameHeight += cavityHeight;
  671.         cavityHeight = 0;
  672.         }
  673.         frameX = cavityX;
  674.         if (slavePtr->side == TOP) {
  675.         frameY = cavityY;
  676.         cavityY += frameHeight;
  677.         } else {
  678.         frameY = cavityY + cavityHeight;
  679.         }
  680.     } else {
  681.         frameHeight = cavityHeight;
  682.         frameWidth = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
  683.             + slavePtr->padX + slavePtr->iPadX;
  684.         if (slavePtr->flags & EXPAND) {
  685.         frameWidth += XExpansion(slavePtr, cavityWidth);
  686.         }
  687.         cavityWidth -= frameWidth;
  688.         if (cavityWidth < 0) {
  689.         frameWidth += cavityWidth;
  690.         cavityWidth = 0;
  691.         }
  692.         frameY = cavityY;
  693.         if (slavePtr->side == LEFT) {
  694.         frameX = cavityX;
  695.         cavityX += frameWidth;
  696.         } else {
  697.         frameX = cavityX + cavityWidth;
  698.         }
  699.     }
  700.  
  701.     /*
  702.      * Now that we've got the size of the frame for the window,
  703.      * compute the window's actual size and location using the
  704.      * fill, padding, and frame factors.  The variables "borderX"
  705.      * and "borderY" are used to handle the differences between
  706.      * old-style packing and the new style (in old-style, iPadX
  707.      * and iPadY are always zero and padding is completely ignored
  708.      * except when computing frame size).
  709.      */
  710.  
  711.     if (slavePtr->flags & OLD_STYLE) {
  712.         borderX = borderY = 0;
  713.     } else {
  714.         borderX = slavePtr->padX;
  715.         borderY = slavePtr->padY;
  716.     }
  717.     width = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
  718.         + slavePtr->iPadX;
  719.     if ((slavePtr->flags & FILLX)
  720.         || (width > (frameWidth - borderX))) {
  721.         width = frameWidth - borderX;
  722.     }
  723.     height = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
  724.         + slavePtr->iPadY;
  725.     if ((slavePtr->flags & FILLY)
  726.         || (height > (frameHeight - borderY))) {
  727.         height = frameHeight - borderY;
  728.     }
  729.     borderX /= 2;
  730.     borderY /= 2;
  731.     switch (slavePtr->anchor) {
  732.         case TK_ANCHOR_N:
  733.         x = frameX + (frameWidth - width)/2;
  734.         y = frameY + borderY;
  735.         break;
  736.         case TK_ANCHOR_NE:
  737.         x = frameX + frameWidth - width - borderX;
  738.         y = frameY + borderY;
  739.         break;
  740.         case TK_ANCHOR_E:
  741.         x = frameX + frameWidth - width - borderX;
  742.         y = frameY + (frameHeight - height)/2;
  743.         break;
  744.         case TK_ANCHOR_SE:
  745.         x = frameX + frameWidth - width - borderX;
  746.         y = frameY + frameHeight - height - borderY;
  747.         break;
  748.         case TK_ANCHOR_S:
  749.         x = frameX + (frameWidth - width)/2;
  750.         y = frameY + frameHeight - height - borderY;
  751.         break;
  752.         case TK_ANCHOR_SW:
  753.         x = frameX + borderX;
  754.         y = frameY + frameHeight - height - borderY;
  755.         break;
  756.         case TK_ANCHOR_W:
  757.         x = frameX + borderX;
  758.         y = frameY + (frameHeight - height)/2;
  759.         break;
  760.         case TK_ANCHOR_NW:
  761.         x = frameX + borderX;
  762.         y = frameY + borderY;
  763.         break;
  764.         case TK_ANCHOR_CENTER:
  765.         x = frameX + (frameWidth - width)/2;
  766.         y = frameY + (frameHeight - height)/2;
  767.         break;
  768.         default:
  769.         panic("bad frame factor in ArrangePacking");
  770.     }
  771.     width -= slavePtr->doubleBw;
  772.     height -= slavePtr->doubleBw;
  773.  
  774.     /*
  775.      * If the window in which slavePtr is packed is not its
  776.      * parent in the window hierarchy, translate the coordinates
  777.      * to the coordinate system of the real X parent.
  778.      */
  779.  
  780.     parent= Tk_Parent(slavePtr->tkwin);
  781.     for (ancestor = masterPtr->tkwin; ancestor != parent;
  782.         ancestor = Tk_Parent(ancestor)) {
  783.         x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width;
  784.         y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width;
  785.     }
  786.  
  787.     /*
  788.      * If the window is too small to be interesting then
  789.      * unmap it.  Otherwise configure it and then make sure
  790.      * it's mapped.
  791.      */
  792.  
  793.     if ((width <= 0) || (height <= 0)) {
  794.         Tk_UnmapWindow(slavePtr->tkwin);
  795.     } else {
  796.         if ((x != Tk_X(slavePtr->tkwin))
  797.             || (y != Tk_Y(slavePtr->tkwin))
  798.             || (width != Tk_Width(slavePtr->tkwin))
  799.             || (height != Tk_Height(slavePtr->tkwin))) {
  800.         Tk_MoveResizeWindow(slavePtr->tkwin, x, y,
  801.             (unsigned int) width, (unsigned int) height);
  802.         }
  803.         if (abort) {
  804.         goto done;
  805.         }
  806.         Tk_MapWindow(slavePtr->tkwin);
  807.     }
  808.  
  809.     /*
  810.      * Changes to the window's structure could cause almost anything
  811.      * to happen, including deleting the parent or child.  If this
  812.      * happens, we'll be told to abort.
  813.      */
  814.  
  815.     if (abort) {
  816.         goto done;
  817.     }
  818.     }
  819.  
  820.     done:
  821.     masterPtr->abortPtr = NULL;
  822.     Tk_Release((ClientData) masterPtr);
  823. }
  824.  
  825. /*
  826.  *----------------------------------------------------------------------
  827.  *
  828.  * XExpansion --
  829.  *
  830.  *    Given a list of packed slaves, the first of which is packed
  831.  *    on the left or right and is expandable, compute how much to
  832.  *    expand the child.
  833.  *
  834.  * Results:
  835.  *    The return value is the number of additional pixels to give to
  836.  *    the child.
  837.  *
  838.  * Side effects:
  839.  *    None.
  840.  *
  841.  *----------------------------------------------------------------------
  842.  */
  843.  
  844. static int
  845. XExpansion(slavePtr, cavityWidth)
  846.     register Packer *slavePtr;        /* First in list of remaining
  847.                      * slaves. */
  848.     int cavityWidth;            /* Horizontal space left for all
  849.                      * remaining slaves. */
  850. {
  851.     int numExpand, minExpand, curExpand;
  852.     int childWidth;
  853.  
  854.     /*
  855.      * This procedure is tricky because windows packed top or bottom can
  856.      * be interspersed among expandable windows packed left or right.
  857.      * Scan through the list, keeping a running sum of the widths of
  858.      * all left and right windows (actually, count the cavity space not
  859.      * allocated) and a running count of all expandable left and right
  860.      * windows.  At each top or bottom window, and at the end of the
  861.      * list, compute the expansion factor that seems reasonable at that
  862.      * point.  Return the smallest factor seen at any of these points.
  863.      */
  864.  
  865.     minExpand = cavityWidth;
  866.     numExpand = 0;
  867.     for ( ; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
  868.     childWidth = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
  869.         + slavePtr->padX + slavePtr->iPadX;
  870.     if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
  871.         curExpand = (cavityWidth - childWidth)/numExpand;
  872.         if (curExpand < minExpand) {
  873.         minExpand = curExpand;
  874.         }
  875.     } else {
  876.         cavityWidth -= childWidth;
  877.         if (slavePtr->flags & EXPAND) {
  878.         numExpand++;
  879.         }
  880.     }
  881.     }
  882.     curExpand = cavityWidth/numExpand;
  883.     if (curExpand < minExpand) {
  884.     minExpand = curExpand;
  885.     }
  886.     return (minExpand < 0) ? 0 : minExpand;
  887. }
  888.  
  889. /*
  890.  *----------------------------------------------------------------------
  891.  *
  892.  * YExpansion --
  893.  *
  894.  *    Given a list of packed slaves, the first of which is packed
  895.  *    on the top or bottom and is expandable, compute how much to
  896.  *    expand the child.
  897.  *
  898.  * Results:
  899.  *    The return value is the number of additional pixels to give to
  900.  *    the child.
  901.  *
  902.  * Side effects:
  903.  *    None.
  904.  *
  905.  *----------------------------------------------------------------------
  906.  */
  907.  
  908. static int
  909. YExpansion(slavePtr, cavityHeight)
  910.     register Packer *slavePtr;        /* First in list of remaining
  911.                      * slaves. */
  912.     int cavityHeight;            /* Vertical space left for all
  913.                      * remaining slaves. */
  914. {
  915.     int numExpand, minExpand, curExpand;
  916.     int childHeight;
  917.  
  918.     /*
  919.      * See comments for XExpansion.
  920.      */
  921.  
  922.     minExpand = cavityHeight;
  923.     numExpand = 0;
  924.     for ( ; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
  925.     childHeight = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
  926.         + slavePtr->padY + slavePtr->iPadY;
  927.     if ((slavePtr->side == LEFT) || (slavePtr->side == RIGHT)) {
  928.         curExpand = (cavityHeight - childHeight)/numExpand;
  929.         if (curExpand < minExpand) {
  930.         minExpand = curExpand;
  931.         }
  932.     } else {
  933.         cavityHeight -= childHeight;
  934.         if (slavePtr->flags & EXPAND) {
  935.         numExpand++;
  936.         }
  937.     }
  938.     }
  939.     curExpand = cavityHeight/numExpand;
  940.     if (curExpand < minExpand) {
  941.     minExpand = curExpand;
  942.     }
  943.     return (minExpand < 0) ? 0 : minExpand;
  944. }
  945.  
  946. /*
  947.  *--------------------------------------------------------------
  948.  *
  949.  * GetPacker --
  950.  *
  951.  *    This internal procedure is used to locate a Packer
  952.  *    structure for a given window, creating one if one
  953.  *    doesn't exist already.
  954.  *
  955.  * Results:
  956.  *    The return value is a pointer to the Packer structure
  957.  *    corresponding to tkwin.
  958.  *
  959.  * Side effects:
  960.  *    A new packer structure may be created.  If so, then
  961.  *    a callback is set up to clean things up when the
  962.  *    window is deleted.
  963.  *
  964.  *--------------------------------------------------------------
  965.  */
  966.  
  967. static Packer *
  968. GetPacker(tkwin)
  969.     Tk_Window tkwin;        /* Token for window for which
  970.                  * packer structure is desired. */
  971. {
  972.     register Packer *packPtr;
  973.     Tcl_HashEntry *hPtr;
  974.     int new;
  975.  
  976.     if (!initialized) {
  977.     initialized = 1;
  978.     Tcl_InitHashTable(&packerHashTable, TCL_ONE_WORD_KEYS);
  979.     }
  980.  
  981.     /*
  982.      * See if there's already packer for this window.  If not,
  983.      * then create a new one.
  984.      */
  985.  
  986.     hPtr = Tcl_CreateHashEntry(&packerHashTable, (char *) tkwin, &new);
  987.     if (!new) {
  988.     return (Packer *) Tcl_GetHashValue(hPtr);
  989.     }
  990.     packPtr = (Packer *) ckalloc(sizeof(Packer));
  991.     packPtr->tkwin = tkwin;
  992.     packPtr->masterPtr = NULL;
  993.     packPtr->nextPtr = NULL;
  994.     packPtr->slavePtr = NULL;
  995.     packPtr->side = TOP;
  996.     packPtr->anchor = TK_ANCHOR_CENTER;
  997.     packPtr->padX = packPtr->padY = 0;
  998.     packPtr->iPadX = packPtr->iPadY = 0;
  999.     packPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
  1000.     packPtr->abortPtr = NULL;
  1001.     packPtr->flags = 0;
  1002.     Tcl_SetHashValue(hPtr, packPtr);
  1003.     Tk_CreateEventHandler(tkwin, StructureNotifyMask,
  1004.         PackStructureProc, (ClientData) packPtr);
  1005.     return packPtr;
  1006. }
  1007.  
  1008. /*
  1009.  *--------------------------------------------------------------
  1010.  *
  1011.  * PackAfter --
  1012.  *
  1013.  *    This procedure does most of the real work of adding
  1014.  *    one or more windows into the packing order for its parent.
  1015.  *
  1016.  * Results:
  1017.  *    A standard Tcl return value.
  1018.  *
  1019.  * Side effects:
  1020.  *    The geometry of the specified windows may change, both now and
  1021.  *    again in the future.
  1022.  *
  1023.  *--------------------------------------------------------------
  1024.  */
  1025.  
  1026. static int
  1027. PackAfter(interp, prevPtr, masterPtr, argc, argv)
  1028.     Tcl_Interp *interp;        /* Interpreter for error reporting. */
  1029.     Packer *prevPtr;        /* Pack windows in argv just after this
  1030.                  * window;  NULL means pack as first
  1031.                  * child of masterPtr. */
  1032.     Packer *masterPtr;        /* Master in which to pack windows. */
  1033.     int argc;            /* Number of elements in argv. */
  1034.     char **argv;        /* Array of lists, each containing 2
  1035.                  * elements:  window name and side
  1036.                  * against which to pack. */
  1037. {
  1038.     register Packer *packPtr;
  1039.     Tk_Window tkwin, ancestor, parent;
  1040.     int length, optionCount;
  1041.     char **options;
  1042.     int index, tmp;
  1043.     char c;
  1044.  
  1045.     /*
  1046.      * Iterate over all of the window specifiers, each consisting of
  1047.      * two arguments.  The first argument contains the window name and
  1048.      * the additional arguments contain options such as "top" or
  1049.      * "padx 20".
  1050.      */
  1051.  
  1052.     for ( ; argc > 0; argc -= 2, argv += 2, prevPtr = packPtr) {
  1053.     if (argc < 2) {
  1054.         Tcl_AppendResult(interp, "wrong # args: window \"",
  1055.             argv[0], "\" should be followed by options",
  1056.             (char *) NULL);
  1057.         return TCL_ERROR;
  1058.     }
  1059.  
  1060.     /*
  1061.      * Find the packer for the window to be packed, and make sure
  1062.      * that the window in which it will be packed is either its
  1063.      * or a descendant of its parent.
  1064.      */
  1065.  
  1066.     tkwin = Tk_NameToWindow(interp, argv[0], masterPtr->tkwin);
  1067.     if (tkwin == NULL) {
  1068.         return TCL_ERROR;
  1069.     }
  1070.  
  1071.     parent = Tk_Parent(tkwin);
  1072.     for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
  1073.         if (ancestor == parent) {
  1074.         break;
  1075.         }
  1076.         if (((Tk_FakeWin *) (ancestor))->flags & TK_TOP_LEVEL) {
  1077.         badWindow:
  1078.         Tcl_AppendResult(interp, "can't pack ", argv[0],
  1079.             " inside ", Tk_PathName(masterPtr->tkwin),
  1080.             (char *) NULL);
  1081.         return TCL_ERROR;
  1082.         }
  1083.     }
  1084.     if (((Tk_FakeWin *) (tkwin))->flags & TK_TOP_LEVEL) {
  1085.         goto badWindow;
  1086.     }
  1087.     if (tkwin == masterPtr->tkwin) {
  1088.         goto badWindow;
  1089.     }
  1090.     packPtr = GetPacker(tkwin);
  1091.  
  1092.     /*
  1093.      * Process options for this window.
  1094.      */
  1095.  
  1096.     if (Tcl_SplitList(interp, argv[1], &optionCount, &options) != TCL_OK) {
  1097.         return TCL_ERROR;
  1098.     }
  1099.     packPtr->side = TOP;
  1100.     packPtr->anchor = TK_ANCHOR_CENTER;
  1101.     packPtr->padX = packPtr->padY = 0;
  1102.     packPtr->iPadX = packPtr->iPadY = 0;
  1103.     packPtr->flags &= ~(FILLX|FILLY|EXPAND);
  1104.     packPtr->flags |= OLD_STYLE;
  1105.     for (index = 0 ; index < optionCount; index++) {
  1106.         char *curOpt = options[index];
  1107.  
  1108.         c = curOpt[0];
  1109.         length = strlen(curOpt);
  1110.  
  1111.         if ((c == 't')
  1112.             && (strncmp(curOpt, "top", length)) == 0) {
  1113.         packPtr->side = TOP;
  1114.         } else if ((c == 'b')
  1115.             && (strncmp(curOpt, "bottom", length)) == 0) {
  1116.         packPtr->side = BOTTOM;
  1117.         } else if ((c == 'l')
  1118.             && (strncmp(curOpt, "left", length)) == 0) {
  1119.         packPtr->side = LEFT;
  1120.         } else if ((c == 'r')
  1121.             && (strncmp(curOpt, "right", length)) == 0) {
  1122.         packPtr->side = RIGHT;
  1123.         } else if ((c == 'e')
  1124.             && (strncmp(curOpt, "expand", length)) == 0) {
  1125.         packPtr->flags |= EXPAND;
  1126.         } else if ((c == 'f')
  1127.             && (strcmp(curOpt, "fill")) == 0) {
  1128.         packPtr->flags |= FILLX|FILLY;
  1129.         } else if ((length == 5) && (strcmp(curOpt, "fillx")) == 0) {
  1130.         packPtr->flags |= FILLX;
  1131.         } else if ((length == 5) && (strcmp(curOpt, "filly")) == 0) {
  1132.         packPtr->flags |= FILLY;
  1133.         } else if ((c == 'p') && (strcmp(curOpt, "padx")) == 0) {
  1134.         if (optionCount < (index+2)) {
  1135.             missingPad:
  1136.             Tcl_AppendResult(interp, "wrong # args: \"", curOpt,
  1137.                 "\" option must be followed by screen distance",
  1138.                 (char *) NULL);
  1139.             goto error;
  1140.         }
  1141.         if ((Tk_GetPixels(interp, tkwin, options[index+1], &tmp)
  1142.             != TCL_OK) || (tmp < 0)) {
  1143.             badPad:
  1144.             Tcl_AppendResult(interp, "bad pad value \"",
  1145.                 options[index+1],
  1146.                 "\":  must be positive screen distance",
  1147.                 (char *) NULL);
  1148.             goto error;
  1149.         }
  1150.         packPtr->padX = tmp;
  1151.         packPtr->iPadX = 0;
  1152.         index++;
  1153.         } else if ((c == 'p') && (strcmp(curOpt, "pady")) == 0) {
  1154.         if (optionCount < (index+2)) {
  1155.             goto missingPad;
  1156.         }
  1157.         if ((Tk_GetPixels(interp, tkwin, options[index+1], &tmp)
  1158.             != TCL_OK) || (tmp < 0)) {
  1159.             goto badPad;
  1160.         }
  1161.         packPtr->padY = tmp;
  1162.         packPtr->iPadY = 0;
  1163.         index++;
  1164.         } else if ((c == 'f') && (length > 1)
  1165.             && (strncmp(curOpt, "frame", length) == 0)) {
  1166.         if (optionCount < (index+2)) {
  1167.             Tcl_AppendResult(interp, "wrong # args: \"frame\" ",
  1168.                 "option must be followed by anchor point",
  1169.                 (char *) NULL);
  1170.             goto error;
  1171.         }
  1172.         if (Tk_GetAnchor(interp, options[index+1],
  1173.             &packPtr->anchor) != TCL_OK) {
  1174.             goto error;
  1175.         }
  1176.         index++;
  1177.         } else {
  1178.         Tcl_AppendResult(interp, "bad option \"", curOpt,
  1179.             "\":  should be top, bottom, left, right, ",
  1180.             "expand, fill, fillx, filly, padx, pady, or frame",
  1181.             (char *) NULL);
  1182.         goto error;
  1183.         }
  1184.     }
  1185.  
  1186.     if (packPtr != prevPtr) {
  1187.  
  1188.         /*
  1189.          * Unpack this window if it's currently packed.
  1190.          */
  1191.     
  1192.         if (packPtr->masterPtr != NULL) {
  1193.         Unlink(packPtr);
  1194.         }
  1195.     
  1196.         /*
  1197.          * Add the window in the correct place in its parent's
  1198.          * packing order, then make sure that the window is
  1199.          * managed by us.
  1200.          */
  1201.  
  1202.         packPtr->masterPtr = masterPtr;
  1203.         if (prevPtr == NULL) {
  1204.         packPtr->nextPtr = masterPtr->slavePtr;
  1205.         masterPtr->slavePtr = packPtr;
  1206.         } else {
  1207.         packPtr->nextPtr = prevPtr->nextPtr;
  1208.         prevPtr->nextPtr = packPtr;
  1209.         }
  1210.         Tk_ManageGeometry(tkwin, PackReqProc, (ClientData) packPtr);
  1211.     }
  1212.     ckfree((char *) options);
  1213.     }
  1214.  
  1215.     /*
  1216.      * Arrange for the parent to be re-packed at the first
  1217.      * idle moment.
  1218.      */
  1219.  
  1220.     if (masterPtr->abortPtr != NULL) {
  1221.     *masterPtr->abortPtr = 1;
  1222.     }
  1223.     if (!(masterPtr->flags & REQUESTED_REPACK)) {
  1224.     masterPtr->flags |= REQUESTED_REPACK;
  1225.     Tk_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
  1226.     }
  1227.     return TCL_OK;
  1228.  
  1229.     error:
  1230.     ckfree((char *) options);
  1231.     return TCL_ERROR;
  1232. }
  1233.  
  1234. /*
  1235.  *----------------------------------------------------------------------
  1236.  *
  1237.  * Unlink --
  1238.  *
  1239.  *    Remove a packer from its parent's list of slaves.
  1240.  *
  1241.  * Results:
  1242.  *    None.
  1243.  *
  1244.  * Side effects:
  1245.  *    The parent will be scheduled for repacking.
  1246.  *
  1247.  *----------------------------------------------------------------------
  1248.  */
  1249.  
  1250. static void
  1251. Unlink(packPtr)
  1252.     register Packer *packPtr;        /* Window to unlink. */
  1253. {
  1254.     register Packer *masterPtr, *packPtr2;
  1255.  
  1256.     masterPtr = packPtr->masterPtr;
  1257.     if (masterPtr == NULL) {
  1258.     return;
  1259.     }
  1260.     if (masterPtr->slavePtr == packPtr) {
  1261.     masterPtr->slavePtr = packPtr->nextPtr;
  1262.     } else {
  1263.     for (packPtr2 = masterPtr->slavePtr; ; packPtr2 = packPtr2->nextPtr) {
  1264.         if (packPtr2 == NULL) {
  1265.         panic("Unlink couldn't find previous window");
  1266.         }
  1267.         if (packPtr2->nextPtr == packPtr) {
  1268.         packPtr2->nextPtr = packPtr->nextPtr;
  1269.         break;
  1270.         }
  1271.     }
  1272.     }
  1273.     if (!(masterPtr->flags & REQUESTED_REPACK)) {
  1274.     masterPtr->flags |= REQUESTED_REPACK;
  1275.     Tk_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
  1276.     }
  1277.     if (masterPtr->abortPtr != NULL) {
  1278.     *masterPtr->abortPtr = 1;
  1279.     }
  1280.  
  1281.     packPtr->masterPtr = NULL;
  1282. }
  1283.  
  1284. /*
  1285.  *----------------------------------------------------------------------
  1286.  *
  1287.  * DestroyPacker --
  1288.  *
  1289.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  1290.  *    to clean up the internal structure of a packer at a safe time
  1291.  *    (when no-one is using it anymore).
  1292.  *
  1293.  * Results:
  1294.  *    None.
  1295.  *
  1296.  * Side effects:
  1297.  *    Everything associated with the packer is freed up.
  1298.  *
  1299.  *----------------------------------------------------------------------
  1300.  */
  1301.  
  1302. static void
  1303. DestroyPacker(clientData)
  1304.     ClientData clientData;        /* Info about packed window that
  1305.                      * is now dead. */
  1306. {
  1307.     register Packer *packPtr = (Packer *) clientData;
  1308.     ckfree((char *) packPtr);
  1309. }
  1310.  
  1311. /*
  1312.  *----------------------------------------------------------------------
  1313.  *
  1314.  * PackStructureProc --
  1315.  *
  1316.  *    This procedure is invoked by the Tk event dispatcher in response
  1317.  *    to StructureNotify events.
  1318.  *
  1319.  * Results:
  1320.  *    None.
  1321.  *
  1322.  * Side effects:
  1323.  *    If a window was just deleted, clean up all its packer-related
  1324.  *    information.  If it was just resized, repack its slaves, if
  1325.  *    any.
  1326.  *
  1327.  *----------------------------------------------------------------------
  1328.  */
  1329.  
  1330. static void
  1331. PackStructureProc(clientData, eventPtr)
  1332.     ClientData clientData;        /* Our information about window
  1333.                      * referred to by eventPtr. */
  1334.     XEvent *eventPtr;            /* Describes what just happened. */
  1335. {
  1336.     register Packer *packPtr = (Packer *) clientData;
  1337.     if (eventPtr->type == ConfigureNotify) {
  1338.     if ((packPtr->slavePtr != NULL)
  1339.         && !(packPtr->flags & REQUESTED_REPACK)) {
  1340.         packPtr->flags |= REQUESTED_REPACK;
  1341.         Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
  1342.     }
  1343.     if (packPtr->doubleBw != 2*Tk_Changes(packPtr->tkwin)->border_width) {
  1344.         if ((packPtr->masterPtr != NULL)
  1345.             && !(packPtr->masterPtr->flags & REQUESTED_REPACK)) {
  1346.         packPtr->doubleBw = 2*Tk_Changes(packPtr->tkwin)->border_width;
  1347.         packPtr->masterPtr->flags |= REQUESTED_REPACK;
  1348.         Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr->masterPtr);
  1349.         }
  1350.     }
  1351.     } else if (eventPtr->type == DestroyNotify) {
  1352.     register Packer *packPtr2, *nextPtr;
  1353.  
  1354.     if (packPtr->masterPtr != NULL) {
  1355.         Unlink(packPtr);
  1356.     }
  1357.     for (packPtr2 = packPtr->slavePtr; packPtr2 != NULL;
  1358.         packPtr2 = nextPtr) {
  1359.         packPtr2->masterPtr = NULL;
  1360.         nextPtr = packPtr2->nextPtr;
  1361.         packPtr2->nextPtr = NULL;
  1362.     }
  1363.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&packerHashTable,
  1364.         (char *) packPtr->tkwin));
  1365.     if (packPtr->flags & REQUESTED_REPACK) {
  1366.         Tk_CancelIdleCall(ArrangePacking, (ClientData) packPtr);
  1367.     }
  1368.     packPtr->tkwin = NULL;
  1369.     Tk_EventuallyFree((ClientData) packPtr, DestroyPacker);
  1370.     } else if (eventPtr->type == MapNotify) {
  1371.     if ((packPtr->slavePtr != NULL)
  1372.         && !(packPtr->flags & REQUESTED_REPACK)) {
  1373.         packPtr->flags |= REQUESTED_REPACK;
  1374.         Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
  1375.     }
  1376.     } else if (eventPtr->type == UnmapNotify) {
  1377.     register Packer *packPtr2;
  1378.  
  1379.     for (packPtr2 = packPtr->slavePtr; packPtr2 != NULL;
  1380.         packPtr2 = packPtr2->nextPtr) {
  1381.         Tk_UnmapWindow(packPtr2->tkwin);
  1382.     }
  1383.     }
  1384. }
  1385.  
  1386. /*
  1387.  *----------------------------------------------------------------------
  1388.  *
  1389.  * ConfigureSlaves --
  1390.  *
  1391.  *    This implements the guts of the "pack configure" command.  Given
  1392.  *    a list of slaves and configuration options, it arranges for the
  1393.  *    packer to manage the slaves and sets the specified options.
  1394.  *
  1395.  * Results:
  1396.  *    TCL_OK is returned if all went well.  Otherwise, TCL_ERROR is
  1397.  *    returned and interp->result is set to contain an error message.
  1398.  *
  1399.  * Side effects:
  1400.  *    Slave windows get taken over by the packer.
  1401.  *
  1402.  *----------------------------------------------------------------------
  1403.  */
  1404.  
  1405. static int
  1406. ConfigureSlaves(interp, tkwin, argc, argv)
  1407.     Tcl_Interp *interp;        /* Interpreter for error reporting. */
  1408.     Tk_Window tkwin;        /* Any window in application containing
  1409.                  * slaves.  Used to look up slave names. */
  1410.     int argc;            /* Number of elements in argv. */
  1411.     char *argv[];        /* Argument strings:  contains one or more
  1412.                  * window names followed by any number
  1413.                  * of "option value" pairs.  Caller must
  1414.                  * make sure that there is at least one
  1415.                  * window name. */
  1416. {
  1417.     Packer *masterPtr, *slavePtr, *prevPtr, *otherPtr;
  1418.     Tk_Window other, slave, parent, ancestor;
  1419.     int i, j, numWindows, c, length, tmp, positionGiven;
  1420.  
  1421.     /*
  1422.      * Find out how many windows are specified.
  1423.      */
  1424.  
  1425.     for (numWindows = 0; numWindows < argc; numWindows++) {
  1426.     if (argv[numWindows][0] != '.') {
  1427.         break;
  1428.     }
  1429.     }
  1430.  
  1431.     /*
  1432.      * Iterate over all of the slave windows, parsing the configuration
  1433.      * options for each slave.  It's a bit wasteful to re-parse the
  1434.      * options for each slave, but things get too messy if we try to
  1435.      * parse the arguments just once at the beginning.  For example,
  1436.      * if a slave already is packed we want to just change a few
  1437.      * existing values without resetting everything.  If there are
  1438.      * multiple windows, the -after, -before, and -in options only
  1439.      * get processed for the first window.
  1440.      */
  1441.  
  1442.     masterPtr = NULL;
  1443.     prevPtr = NULL;
  1444.     positionGiven = 0;
  1445.     for (j = 0; j < numWindows; j++) {
  1446.     slave = Tk_NameToWindow(interp, argv[j], tkwin);
  1447.     if (slave == NULL) {
  1448.         return TCL_ERROR;
  1449.     }
  1450.     if (Tk_IsTopLevel(slave)) {
  1451.         Tcl_AppendResult(interp, "can't pack \"", argv[j],
  1452.             "\": it's a top-level window", (char *) NULL);
  1453.         return TCL_ERROR;
  1454.     }
  1455.     slavePtr = GetPacker(slave);
  1456.     slavePtr->flags &= ~OLD_STYLE;
  1457.  
  1458.     /*
  1459.      * If the slave isn't currently packed, reset all of its
  1460.      * configuration information to default values (there could
  1461.      * be old values left from a previous packing).
  1462.      */
  1463.  
  1464.     if (slavePtr->masterPtr == NULL) {
  1465.         slavePtr->side = TOP;
  1466.         slavePtr->anchor = TK_ANCHOR_CENTER;
  1467.         slavePtr->padX = slavePtr->padY = 0;
  1468.         slavePtr->iPadX = slavePtr->iPadY = 0;
  1469.         slavePtr->flags &= ~(FILLX|FILLY|EXPAND);
  1470.     }
  1471.  
  1472.     for (i = numWindows; i < argc; i+=2) {
  1473.         if ((i+2) > argc) {
  1474.         Tcl_AppendResult(interp, "extra option \"", argv[i],
  1475.             "\" (option with no value?)", (char *) NULL);
  1476.         return TCL_ERROR;
  1477.         }
  1478.         length = strlen(argv[i]);
  1479.         if (length < 2) {
  1480.         goto badOption;
  1481.         }
  1482.         c = argv[i][1];
  1483.         if ((c == 'a') && (strncmp(argv[i], "-after", length) == 0)
  1484.             && (length >= 2)) {
  1485.         if (j == 0) {
  1486.             other = Tk_NameToWindow(interp, argv[i+1], tkwin);
  1487.             if (other == NULL) {
  1488.             return TCL_ERROR;
  1489.             }
  1490.             prevPtr = GetPacker(other);
  1491.             if (prevPtr->masterPtr == NULL) {
  1492.             notPacked:
  1493.             Tcl_AppendResult(interp, "window \"", argv[i+1],
  1494.                 "\" isn't packed", (char *) NULL);
  1495.             return TCL_ERROR;
  1496.             }
  1497.             masterPtr = prevPtr->masterPtr;
  1498.             positionGiven = 1;
  1499.         }
  1500.         } else if ((c == 'a') && (strncmp(argv[i], "-anchor", length) == 0)
  1501.             && (length >= 2)) {
  1502.         if (Tk_GetAnchor(interp, argv[i+1], &slavePtr->anchor)
  1503.             != TCL_OK) {
  1504.             return TCL_ERROR;
  1505.         }
  1506.         } else if ((c == 'b')
  1507.             && (strncmp(argv[i], "-before", length) == 0)) {
  1508.         if (j == 0) {
  1509.             other = Tk_NameToWindow(interp, argv[i+1], tkwin);
  1510.             if (other == NULL) {
  1511.             return TCL_ERROR;
  1512.             }
  1513.             otherPtr = GetPacker(other);
  1514.             if (otherPtr->masterPtr == NULL) {
  1515.             goto notPacked;
  1516.             }
  1517.             masterPtr = otherPtr->masterPtr;
  1518.             prevPtr = masterPtr->slavePtr;
  1519.             if (prevPtr == otherPtr) {
  1520.             prevPtr = NULL;
  1521.             } else {
  1522.             while (prevPtr->nextPtr != otherPtr) {
  1523.                 prevPtr = prevPtr->nextPtr;
  1524.             }
  1525.             }
  1526.             positionGiven = 1;
  1527.         }
  1528.         } else if ((c == 'e')
  1529.             && (strncmp(argv[i], "-expand", length) == 0)) {
  1530.         if (Tcl_GetBoolean(interp, argv[i+1], &tmp) != TCL_OK) {
  1531.             return TCL_ERROR;
  1532.         }
  1533.         slavePtr->flags &= ~EXPAND;
  1534.         if (tmp) {
  1535.             slavePtr->flags |= EXPAND;
  1536.         }
  1537.         } else if ((c == 'f') && (strncmp(argv[i], "-fill", length) == 0)) {
  1538.         if (strcmp(argv[i+1], "none") == 0) {
  1539.             slavePtr->flags &= ~(FILLX|FILLY);
  1540.         } else if (strcmp(argv[i+1], "x") == 0) {
  1541.             slavePtr->flags = (slavePtr->flags & ~FILLY) | FILLX;
  1542.         } else if (strcmp(argv[i+1], "y") == 0) {
  1543.             slavePtr->flags = (slavePtr->flags & ~FILLX) | FILLY;
  1544.         } else if (strcmp(argv[i+1], "both") == 0) {
  1545.             slavePtr->flags |= FILLX|FILLY;
  1546.         } else {
  1547.             Tcl_AppendResult(interp, "bad fill style \"", argv[i+1],
  1548.                 "\": must be none, x, y, or both", (char *) NULL);
  1549.             return TCL_ERROR;
  1550.         }
  1551.         } else if ((c == 'i') && (strcmp(argv[i], "-in") == 0)) {
  1552.         if (j == 0) {
  1553.             other = Tk_NameToWindow(interp, argv[i+1], tkwin);
  1554.             if (other == NULL) {
  1555.             return TCL_ERROR;
  1556.             }
  1557.             masterPtr = GetPacker(other);
  1558.             prevPtr = masterPtr->slavePtr;
  1559.             if (prevPtr != NULL) {
  1560.             while (prevPtr->nextPtr != NULL) {
  1561.                 prevPtr = prevPtr->nextPtr;
  1562.             }
  1563.             }
  1564.             positionGiven = 1;
  1565.         }
  1566.         } else if ((c == 'i') && (strcmp(argv[i], "-ipadx") == 0)) {
  1567.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  1568.             || (tmp < 0)) {
  1569.             badPad:
  1570.             Tcl_ResetResult(interp);
  1571.             Tcl_AppendResult(interp, "bad pad value \"", argv[i+1],
  1572.                 "\": must be positive screen distance",
  1573.                 (char *) NULL);
  1574.             return TCL_ERROR;
  1575.         }
  1576.         slavePtr->iPadX = tmp*2;
  1577.         } else if ((c == 'i') && (strcmp(argv[i], "-ipady") == 0)) {
  1578.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  1579.             || (tmp< 0)) {
  1580.             goto badPad;
  1581.         }
  1582.         slavePtr->iPadY = tmp*2;
  1583.         } else if ((c == 'p') && (strcmp(argv[i], "-padx") == 0)) {
  1584.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  1585.             || (tmp< 0)) {
  1586.             goto badPad;
  1587.         }
  1588.         slavePtr->padX = tmp*2;
  1589.         } else if ((c == 'p') && (strcmp(argv[i], "-pady") == 0)) {
  1590.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  1591.             || (tmp< 0)) {
  1592.             goto badPad;
  1593.         }
  1594.         slavePtr->padY = tmp*2;
  1595.         } else if ((c == 's') && (strncmp(argv[i], "-side", length) == 0)) {
  1596.         c = argv[i+1][0];
  1597.         if ((c == 't') && (strcmp(argv[i+1], "top") == 0)) {
  1598.             slavePtr->side = TOP;
  1599.         } else if ((c == 'b') && (strcmp(argv[i+1], "bottom") == 0)) {
  1600.             slavePtr->side = BOTTOM;
  1601.         } else if ((c == 'l') && (strcmp(argv[i+1], "left") == 0)) {
  1602.             slavePtr->side = LEFT;
  1603.         } else if ((c == 'r') && (strcmp(argv[i+1], "right") == 0)) {
  1604.             slavePtr->side = RIGHT;
  1605.         } else {
  1606.             Tcl_AppendResult(interp, "bad side \"", argv[i+1],
  1607.                 "\": must be top, bottom, left, or right",
  1608.                 (char *) NULL);
  1609.             return TCL_ERROR;
  1610.         }
  1611.         } else {
  1612.         badOption:
  1613.         Tcl_AppendResult(interp, "unknown or ambiguous option \"",
  1614.             argv[i], "\": must be -after, -anchor, -before, ",
  1615.             "-expand, -fill, -in, -ipadx, -ipady, -padx, ",
  1616.             "-pady, or -side", (char *) NULL);
  1617.         return TCL_ERROR;
  1618.         }
  1619.     }
  1620.  
  1621.     /*
  1622.      * If no position in a packing list was specified and the slave
  1623.      * is already packed, then leave it in its current location in
  1624.      * its current packing list.
  1625.      */
  1626.  
  1627.     if (!positionGiven && (slavePtr->masterPtr != NULL)) {
  1628.         continue;
  1629.     }
  1630.  
  1631.     /*
  1632.      * If the slave is going to be put back after itself then
  1633.      * skip the whole operation, since it won't work anyway.
  1634.      */
  1635.  
  1636.     if (prevPtr == slavePtr) {
  1637.         continue;
  1638.     }
  1639.     
  1640.     /*
  1641.      * If none of the "-in", "-before", or "-after" options has
  1642.      * been specified, arrange for the slave to go at the end of
  1643.      * the order for its parent.
  1644.      */
  1645.     
  1646.     if (!positionGiven) {
  1647.         masterPtr = GetPacker(Tk_Parent(slave));
  1648.         prevPtr = masterPtr->slavePtr;
  1649.         if (prevPtr != NULL) {
  1650.         while (prevPtr->nextPtr != NULL) {
  1651.             prevPtr = prevPtr->nextPtr;
  1652.         }
  1653.         }
  1654.     }
  1655.  
  1656.     /*
  1657.      * Make sure that the slave's parent is either the master or
  1658.      * an ancestor of the master.
  1659.      */
  1660.     
  1661.     parent = Tk_Parent(slave);
  1662.     for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
  1663.         if (ancestor == parent) {
  1664.         break;
  1665.         }
  1666.         if (Tk_IsTopLevel(ancestor)) {
  1667.         Tcl_AppendResult(interp, "can't pack ", argv[j],
  1668.             " inside ", Tk_PathName(masterPtr->tkwin),
  1669.             (char *) NULL);
  1670.         return TCL_ERROR;
  1671.         }
  1672.     }
  1673.  
  1674.     /*
  1675.      * Unpack the slave if it's currently packed, then position it
  1676.      * after prevPtr.
  1677.      */
  1678.  
  1679.     if (slavePtr->masterPtr != NULL) {
  1680.         Unlink(slavePtr);
  1681.     }
  1682.     slavePtr->masterPtr = masterPtr;
  1683.     if (prevPtr == NULL) {
  1684.         slavePtr->nextPtr = masterPtr->slavePtr;
  1685.         masterPtr->slavePtr = slavePtr;
  1686.     } else {
  1687.         slavePtr->nextPtr = prevPtr->nextPtr;
  1688.         prevPtr->nextPtr = slavePtr;
  1689.     }
  1690.     Tk_ManageGeometry(slave, PackReqProc, (ClientData) slavePtr);
  1691.     prevPtr = slavePtr;
  1692.  
  1693.     /*
  1694.      * Arrange for the parent to be re-packed at the first
  1695.      * idle moment.
  1696.      */
  1697.  
  1698.     if (masterPtr->abortPtr != NULL) {
  1699.         *masterPtr->abortPtr = 1;
  1700.     }
  1701.     if (!(masterPtr->flags & REQUESTED_REPACK)) {
  1702.         masterPtr->flags |= REQUESTED_REPACK;
  1703.         Tk_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
  1704.     }
  1705.     }
  1706.     return TCL_OK;
  1707. }
  1708.