tkPack.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:52k
源码类别:

通讯编程

开发平台:

Visual C++

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