home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / generic / tkPack.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  49.4 KB  |  1,728 lines  |  [TEXT/CWIE]

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