Logo Search packages:      
Sourcecode: tcltk8.0-ja version File versions  Download package

tkPlace.c

/* 
 * tkPlace.c --
 *
 *    This file contains code to implement a simple geometry manager
 *    for Tk based on absolute placement or "rubber-sheet" placement.
 *
 * Copyright (c) 1992-1994 The Regents of the University of California.
 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tkPlace.c,v 1.2 1998/09/14 18:23:15 stanton Exp $
 */

#include "tkPort.h"
#include "tkInt.h"

/*
 * Border modes for relative placement:
 *
 * BM_INSIDE:           relative distances computed using area inside
 *                all borders of master window.
 * BM_OUTSIDE:          relative distances computed using outside area
 *                that includes all borders of master.
 * BM_IGNORE:           border issues are ignored:  place relative to
 *                master's actual window size.
 */

typedef enum {BM_INSIDE, BM_OUTSIDE, BM_IGNORE} BorderMode;

/*
 * For each window whose geometry is managed by the placer there is
 * a structure of the following type:
 */

typedef struct Slave {
    Tk_Window tkwin;          /* Tk's token for window. */
    struct Master *masterPtr; /* Pointer to information for window
                         * relative to which tkwin is placed.
                         * This isn't necessarily the logical
                         * parent of tkwin.  NULL means the
                         * master was deleted or never assigned. */
    struct Slave *nextPtr;    /* Next in list of windows placed relative
                         * to same master (NULL for end of list). */

    /*
     * Geometry information for window;  where there are both relative
     * and absolute values for the same attribute (e.g. x and relX) only
     * one of them is actually used, depending on flags.
     */

    int x, y;                 /* X and Y pixel coordinates for tkwin. */
    float relX, relY;         /* X and Y coordinates relative to size of
                         * master. */
    int width, height;        /* Absolute dimensions for tkwin. */
    float relWidth, relHeight;      /* Dimensions for tkwin relative to size of
                         * master. */
    Tk_Anchor anchor;         /* Which point on tkwin is placed at the
                         * given position. */
    BorderMode borderMode;    /* How to treat borders of master window. */
    int flags;                /* Various flags;  see below for bit
                         * definitions. */
} Slave;

/*
 * Flag definitions for Slave structures:
 *
 * CHILD_WIDTH -        1 means -width was specified;
 * CHILD_REL_WIDTH -          1 means -relwidth was specified.
 * CHILD_HEIGHT -       1 means -height was specified;
 * CHILD_REL_HEIGHT -         1 means -relheight was specified.
 */

#define CHILD_WIDTH           1
#define CHILD_REL_WIDTH       2
#define CHILD_HEIGHT          4
#define CHILD_REL_HEIGHT      8

/*
 * For each master window that has a slave managed by the placer there
 * is a structure of the following form:
 */

typedef struct Master {
    Tk_Window tkwin;          /* Tk's token for master window. */
    struct Slave *slavePtr;   /* First in linked list of slaves
                         * placed relative to this master. */
    int flags;                /* See below for bit definitions. */
} Master;

/*
 * Flag definitions for masters:
 *
 * PARENT_RECONFIG_PENDING -  1 means that a call to RecomputePlacement
 *                      is already pending via a Do_When_Idle handler.
 */

#define PARENT_RECONFIG_PENDING     1

/*
 * The hash tables below both use Tk_Window tokens as keys.  They map
 * from Tk_Windows to Slave and Master structures for windows, if they
 * exist.
 */

static int initialized = 0;
static Tcl_HashTable masterTable;
static Tcl_HashTable slaveTable;
/*
 * The following structure is the official type record for the
 * placer:
 */

static void       PlaceRequestProc _ANSI_ARGS_((ClientData clientData,
                      Tk_Window tkwin));
static void       PlaceLostSlaveProc _ANSI_ARGS_((ClientData clientData,
                      Tk_Window tkwin));

static Tk_GeomMgr placerType = {
    "place",                        /* name */
    PlaceRequestProc,               /* requestProc */
    PlaceLostSlaveProc,             /* lostSlaveProc */
};

/*
 * Forward declarations for procedures defined later in this file:
 */

static void       SlaveStructureProc _ANSI_ARGS_((ClientData clientData,
                      XEvent *eventPtr));
static int        ConfigureSlave _ANSI_ARGS_((Tcl_Interp *interp,
                      Slave *slavePtr, int argc, char **argv));
static Slave *          FindSlave _ANSI_ARGS_((Tk_Window tkwin));
static Master *         FindMaster _ANSI_ARGS_((Tk_Window tkwin));
static void       MasterStructureProc _ANSI_ARGS_((ClientData clientData,
                      XEvent *eventPtr));
static void       RecomputePlacement _ANSI_ARGS_((ClientData clientData));
static void       UnlinkSlave _ANSI_ARGS_((Slave *slavePtr));

/*
 *--------------------------------------------------------------
 *
 * Tk_PlaceCmd --
 *
 *    This procedure is invoked to process the "place" Tcl
 *    commands.  See the user documentation for details on
 *    what it does.
 *
 * Results:
 *    A standard Tcl result.
 *
 * Side effects:
 *    See the user documentation.
 *
 *--------------------------------------------------------------
 */

int
Tk_PlaceCmd(clientData, interp, argc, argv)
    ClientData clientData;    /* Main window associated with interpreter. */
    Tcl_Interp *interp;       /* Current interpreter. */
    int argc;                 /* Number of arguments. */
    char **argv;        /* Argument strings. */
{
    Tk_Window tkwin;
    Slave *slavePtr;
    Tcl_HashEntry *hPtr;
    size_t length;
    int c;

    /*
     * Initialize, if that hasn't been done yet.
     */

    if (!initialized) {
      Tcl_InitHashTable(&masterTable, TCL_ONE_WORD_KEYS);
      Tcl_InitHashTable(&slaveTable, TCL_ONE_WORD_KEYS);
      initialized = 1;
    }

    if (argc < 3) {
      Tcl_AppendResult(interp, "wrong # args: should be \"",
            argv[0], " option|pathName args", (char *) NULL);
      return TCL_ERROR;
    }
    c = argv[1][0];
    length = strlen(argv[1]);

    /*
     * Handle special shortcut where window name is first argument.
     */

    if (c == '.') {
      tkwin = Tk_NameToWindow(interp, argv[1], (Tk_Window) clientData);
      if (tkwin == NULL) {
          return TCL_ERROR;
      }
      slavePtr = FindSlave(tkwin);
      return ConfigureSlave(interp, slavePtr, argc-2, argv+2);
    }

    /*
     * Handle more general case of option followed by window name followed
     * by possible additional arguments.
     */

    tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
    if (tkwin == NULL) {
      return TCL_ERROR;
    }
    if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
      if (argc < 5) {
          Tcl_AppendResult(interp, "wrong # args: should be \"",
                argv[0],
                " configure pathName option value ?option value ...?\"",
                (char *) NULL);
          return TCL_ERROR;
      }
      slavePtr = FindSlave(tkwin);
      return ConfigureSlave(interp, slavePtr, argc-3, argv+3);
    } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
      if (argc != 3) {
          Tcl_AppendResult(interp, "wrong # args: should be \"",
                argv[0], " forget pathName\"", (char *) NULL);
          return TCL_ERROR;
      }
      hPtr = Tcl_FindHashEntry(&slaveTable, (char *) tkwin);
      if (hPtr == NULL) {
          return TCL_OK;
      }
      slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
      if ((slavePtr->masterPtr != NULL) &&
            (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin))) {
          Tk_UnmaintainGeometry(slavePtr->tkwin,
                slavePtr->masterPtr->tkwin);
      }
      UnlinkSlave(slavePtr);
      Tcl_DeleteHashEntry(hPtr);
      Tk_DeleteEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc,
            (ClientData) slavePtr);
      Tk_ManageGeometry(tkwin, (Tk_GeomMgr *) NULL, (ClientData) NULL);
      Tk_UnmapWindow(tkwin);
      ckfree((char *) slavePtr);
    } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
      char buffer[50];

      if (argc != 3) {
          Tcl_AppendResult(interp, "wrong # args: should be \"",
                argv[0], " info pathName\"", (char *) NULL);
          return TCL_ERROR;
      }
      hPtr = Tcl_FindHashEntry(&slaveTable, (char *) tkwin);
      if (hPtr == NULL) {
          return TCL_OK;
      }
      slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
      sprintf(buffer, "-x %d", slavePtr->x);
      Tcl_AppendResult(interp, buffer, (char *) NULL);
      sprintf(buffer, " -relx %.4g", slavePtr->relX);
      Tcl_AppendResult(interp, buffer, (char *) NULL);
      sprintf(buffer, " -y %d", slavePtr->y);
      Tcl_AppendResult(interp, buffer, (char *) NULL);
      sprintf(buffer, " -rely %.4g", slavePtr->relY);
      Tcl_AppendResult(interp, buffer, (char *) NULL);
      if (slavePtr->flags & CHILD_WIDTH) {
          sprintf(buffer, " -width %d", slavePtr->width);
          Tcl_AppendResult(interp, buffer, (char *) NULL);
      } else {
          Tcl_AppendResult(interp, " -width {}", (char *) NULL);
      }
      if (slavePtr->flags & CHILD_REL_WIDTH) {
          sprintf(buffer, " -relwidth %.4g", slavePtr->relWidth);
          Tcl_AppendResult(interp, buffer, (char *) NULL);
      } else {
          Tcl_AppendResult(interp, " -relwidth {}", (char *) NULL);
      }
      if (slavePtr->flags & CHILD_HEIGHT) {
          sprintf(buffer, " -height %d", slavePtr->height);
          Tcl_AppendResult(interp, buffer, (char *) NULL);
      } else {
          Tcl_AppendResult(interp, " -height {}", (char *) NULL);
      }
      if (slavePtr->flags & CHILD_REL_HEIGHT) {
          sprintf(buffer, " -relheight %.4g", slavePtr->relHeight);
          Tcl_AppendResult(interp, buffer, (char *) NULL);
      } else {
          Tcl_AppendResult(interp, " -relheight {}", (char *) NULL);
      }

      Tcl_AppendResult(interp, " -anchor ", Tk_NameOfAnchor(slavePtr->anchor),
            (char *) NULL);
      if (slavePtr->borderMode == BM_OUTSIDE) {
          Tcl_AppendResult(interp, " -bordermode outside", (char *) NULL);
      } else if (slavePtr->borderMode == BM_IGNORE) {
          Tcl_AppendResult(interp, " -bordermode ignore", (char *) NULL);
      }
      if ((slavePtr->masterPtr != NULL)
            && (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin))) {
          Tcl_AppendResult(interp, " -in ",
                Tk_PathName(slavePtr->masterPtr->tkwin), (char *) NULL);
      }
    } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
      if (argc != 3) {
          Tcl_AppendResult(interp, "wrong # args: should be \"",
                argv[0], " slaves pathName\"", (char *) NULL);
          return TCL_ERROR;
      }
      hPtr = Tcl_FindHashEntry(&masterTable, (char *) tkwin);
      if (hPtr != NULL) {
          Master *masterPtr;
          masterPtr = (Master *) Tcl_GetHashValue(hPtr);
          for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
                slavePtr = slavePtr->nextPtr) {
            Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin));
          }
      }
    } else {
      Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
            "\": must be configure, forget, info, or slaves",
            (char *) NULL);
      return TCL_ERROR;
    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * FindSlave --
 *
 *    Given a Tk_Window token, find the Slave structure corresponding
 *    to that token (making a new one if necessary).
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    A new Slave structure may be created.
 *
 *----------------------------------------------------------------------
 */

static Slave *
FindSlave(tkwin)
    Tk_Window tkwin;          /* Token for desired slave. */
{
    Tcl_HashEntry *hPtr;
    register Slave *slavePtr;
    int new;

    hPtr = Tcl_CreateHashEntry(&slaveTable, (char *) tkwin, &new);
    if (new) {
      slavePtr = (Slave *) ckalloc(sizeof(Slave));
      slavePtr->tkwin = tkwin;
      slavePtr->masterPtr = NULL;
      slavePtr->nextPtr = NULL;
      slavePtr->x = slavePtr->y = 0;
      slavePtr->relX = slavePtr->relY = (float) 0.0;
      slavePtr->width = slavePtr->height = 0;
      slavePtr->relWidth = slavePtr->relHeight = (float) 0.0;
      slavePtr->anchor = TK_ANCHOR_NW;
      slavePtr->borderMode = BM_INSIDE;
      slavePtr->flags = 0;
      Tcl_SetHashValue(hPtr, slavePtr);
      Tk_CreateEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc,
            (ClientData) slavePtr);
      Tk_ManageGeometry(tkwin, &placerType, (ClientData) slavePtr);
    } else {
      slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
    }
    return slavePtr;
}

/*
 *----------------------------------------------------------------------
 *
 * UnlinkSlave --
 *
 *    This procedure removes a slave window from the chain of slaves
 *    in its master.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The slave list of slavePtr's master changes.
 *
 *----------------------------------------------------------------------
 */

static void
UnlinkSlave(slavePtr)
    Slave *slavePtr;          /* Slave structure to be unlinked. */
{
    register Master *masterPtr;
    register Slave *prevPtr;

    masterPtr = slavePtr->masterPtr;
    if (masterPtr == NULL) {
      return;
    }
    if (masterPtr->slavePtr == slavePtr) {
      masterPtr->slavePtr = slavePtr->nextPtr;
    } else {
      for (prevPtr = masterPtr->slavePtr; ;
            prevPtr = prevPtr->nextPtr) {
          if (prevPtr == NULL) {
            panic("UnlinkSlave couldn't find slave to unlink");
          }
          if (prevPtr->nextPtr == slavePtr) {
            prevPtr->nextPtr = slavePtr->nextPtr;
            break;
          }
      }
    }
    slavePtr->masterPtr = NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * FindMaster --
 *
 *    Given a Tk_Window token, find the Master structure corresponding
 *    to that token (making a new one if necessary).
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    A new Master structure may be created.
 *
 *----------------------------------------------------------------------
 */

static Master *
FindMaster(tkwin)
    Tk_Window tkwin;          /* Token for desired master. */
{
    Tcl_HashEntry *hPtr;
    register Master *masterPtr;
    int new;

    hPtr = Tcl_CreateHashEntry(&masterTable, (char *) tkwin, &new);
    if (new) {
      masterPtr = (Master *) ckalloc(sizeof(Master));
      masterPtr->tkwin = tkwin;
      masterPtr->slavePtr = NULL;
      masterPtr->flags = 0;
      Tcl_SetHashValue(hPtr, masterPtr);
      Tk_CreateEventHandler(masterPtr->tkwin, StructureNotifyMask,
            MasterStructureProc, (ClientData) masterPtr);
    } else {
      masterPtr = (Master *) Tcl_GetHashValue(hPtr);
    }
    return masterPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * ConfigureSlave --
 *
 *    This procedure is called to process an argv/argc list to
 *    reconfigure the placement of a window.
 *
 * Results:
 *    A standard Tcl result.  If an error occurs then a message is
 *    left in interp->result.
 *
 * Side effects:
 *    Information in slavePtr may change, and slavePtr's master is
 *    scheduled for reconfiguration.
 *
 *----------------------------------------------------------------------
 */

static int
ConfigureSlave(interp, slavePtr, argc, argv)
    Tcl_Interp *interp;       /* Used for error reporting. */
    Slave *slavePtr;          /* Pointer to current information
                         * about slave. */
    int argc;                 /* Number of config arguments. */
    char **argv;        /* String values for arguments. */
{
    register Master *masterPtr;
    int c, result;
    size_t length;
    double d;

    result = TCL_OK;
    if (Tk_IsTopLevel(slavePtr->tkwin)) {
      Tcl_AppendResult(interp, "can't use placer on top-level window \"",
            Tk_PathName(slavePtr->tkwin), "\"; use wm command instead",
            (char *) NULL);
      return TCL_ERROR;
    }
    for ( ; argc > 0; argc -= 2, argv += 2) {
      if (argc < 2) {
          Tcl_AppendResult(interp, "extra option \"", argv[0],
                "\" (option with no value?)", (char *) NULL);
          result = TCL_ERROR;
          goto done;
      }
      length = strlen(argv[0]);
      c = argv[0][1];
      if ((c == 'a') && (strncmp(argv[0], "-anchor", length) == 0)) {
          if (Tk_GetAnchor(interp, argv[1], &slavePtr->anchor) != TCL_OK) {
            result = TCL_ERROR;
            goto done;
          }
      } else if ((c == 'b')
            && (strncmp(argv[0], "-bordermode", length) == 0)) {
          c = argv[1][0];
          length = strlen(argv[1]);
          if ((c == 'i') && (strncmp(argv[1], "ignore", length) == 0)
                && (length >= 2)) {
            slavePtr->borderMode = BM_IGNORE;
          } else if ((c == 'i') && (strncmp(argv[1], "inside", length) == 0)
                && (length >= 2)) {
            slavePtr->borderMode = BM_INSIDE;
          } else if ((c == 'o')
                && (strncmp(argv[1], "outside", length) == 0)) {
            slavePtr->borderMode = BM_OUTSIDE;
          } else {
            Tcl_AppendResult(interp, "bad border mode \"", argv[1],
                  "\": must be ignore, inside, or outside",
                  (char *) NULL);
            result = TCL_ERROR;
            goto done;
          }
      } else if ((c == 'h') && (strncmp(argv[0], "-height", length) == 0)) {
          if (argv[1][0] == 0) {
            slavePtr->flags &= ~CHILD_HEIGHT;
          } else {
            if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
                  &slavePtr->height) != TCL_OK) {
                result = TCL_ERROR;
                goto done;
            }
            slavePtr->flags |= CHILD_HEIGHT;
          }
      } else if ((c == 'i') && (strncmp(argv[0], "-in", length) == 0)) {
          Tk_Window tkwin;
          Tk_Window ancestor;

          tkwin = Tk_NameToWindow(interp, argv[1], slavePtr->tkwin);
          if (tkwin == NULL) {
            result = TCL_ERROR;
            goto done;
          }

          /*
           * Make sure that the new master is either the logical parent
           * of the slave or a descendant of that window, and that the
           * master and slave aren't the same.
           */

          for (ancestor = tkwin; ; ancestor = Tk_Parent(ancestor)) {
            if (ancestor == Tk_Parent(slavePtr->tkwin)) {
                break;
            }
            if (Tk_IsTopLevel(ancestor)) {
                Tcl_AppendResult(interp, "can't place ",
                      Tk_PathName(slavePtr->tkwin), " relative to ",
                      Tk_PathName(tkwin), (char *) NULL);
                result = TCL_ERROR;
                goto done;
            }
          }
          if (slavePtr->tkwin == tkwin) {
            Tcl_AppendResult(interp, "can't place ",
                  Tk_PathName(slavePtr->tkwin), " relative to itself",
                  (char *) NULL);
            result = TCL_ERROR;
            goto done;
          }
          if ((slavePtr->masterPtr != NULL)
                && (slavePtr->masterPtr->tkwin == tkwin)) {
            /*
             * Re-using same old master.  Nothing to do.
             */
          } else {
            if ((slavePtr->masterPtr != NULL)
                  && (slavePtr->masterPtr->tkwin
                  != Tk_Parent(slavePtr->tkwin))) {
                Tk_UnmaintainGeometry(slavePtr->tkwin,
                      slavePtr->masterPtr->tkwin);
            }
            UnlinkSlave(slavePtr);
            slavePtr->masterPtr = FindMaster(tkwin);
            slavePtr->nextPtr = slavePtr->masterPtr->slavePtr;
            slavePtr->masterPtr->slavePtr = slavePtr;
          }
      } else if ((c == 'r') && (strncmp(argv[0], "-relheight", length) == 0)
            && (length >= 5)) {
          if (argv[1][0] == 0) {
            slavePtr->flags &= ~CHILD_REL_HEIGHT;
          } else {
            if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
                result = TCL_ERROR;
                goto done;
            }
            slavePtr->relHeight = (float) d;
            slavePtr->flags |= CHILD_REL_HEIGHT;
          }
      } else if ((c == 'r') && (strncmp(argv[0], "-relwidth", length) == 0)
            && (length >= 5)) {
          if (argv[1][0] == 0) {
            slavePtr->flags &= ~CHILD_REL_WIDTH;
          } else {
            if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
                result = TCL_ERROR;
                goto done;
            }
            slavePtr->relWidth = (float) d;
            slavePtr->flags |= CHILD_REL_WIDTH;
          }
      } else if ((c == 'r') && (strncmp(argv[0], "-relx", length) == 0)
            && (length >= 5)) {
          if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
            result = TCL_ERROR;
            goto done;
          }
          slavePtr->relX = (float) d;
      } else if ((c == 'r') && (strncmp(argv[0], "-rely", length) == 0)
            && (length >= 5)) {
          if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
            result = TCL_ERROR;
            goto done;
          }
          slavePtr->relY = (float) d;
      } else if ((c == 'w') && (strncmp(argv[0], "-width", length) == 0)) {
          if (argv[1][0] == 0) {
            slavePtr->flags &= ~CHILD_WIDTH;
          } else {
            if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
                  &slavePtr->width) != TCL_OK) {
                result = TCL_ERROR;
                goto done;
            }
            slavePtr->flags |= CHILD_WIDTH;
          }
      } else if ((c == 'x') && (strncmp(argv[0], "-x", length) == 0)) {
          if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
                &slavePtr->x) != TCL_OK) {
            result = TCL_ERROR;
            goto done;
          }
      } else if ((c == 'y') && (strncmp(argv[0], "-y", length) == 0)) {
          if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
                &slavePtr->y) != TCL_OK) {
            result = TCL_ERROR;
            goto done;
          }
      } else {
          Tcl_AppendResult(interp, "unknown or ambiguous option \"",
                argv[0], "\": must be -anchor, -bordermode, -height, ",
                "-in, -relheight, -relwidth, -relx, -rely, -width, ",
                "-x, or -y", (char *) NULL);
          result = TCL_ERROR;
          goto done;
      }
    }

    /*
     * If there's no master specified for this slave, use its Tk_Parent.
     * Then arrange for a placement recalculation in the master.
     */

    done:
    masterPtr = slavePtr->masterPtr;
    if (masterPtr == NULL) {
      masterPtr = FindMaster(Tk_Parent(slavePtr->tkwin));
      slavePtr->masterPtr = masterPtr;
      slavePtr->nextPtr = masterPtr->slavePtr;
      masterPtr->slavePtr = slavePtr;
    }
    if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
      masterPtr->flags |= PARENT_RECONFIG_PENDING;
      Tcl_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
    }
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * RecomputePlacement --
 *
 *    This procedure is called as a when-idle handler.  It recomputes
 *    the geometries of all the slaves of a given master.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Windows may change size or shape.
 *
 *----------------------------------------------------------------------
 */

static void
RecomputePlacement(clientData)
    ClientData clientData;    /* Pointer to Master record. */
{
    register Master *masterPtr = (Master *) clientData;
    register Slave *slavePtr;
    int x, y, width, height, tmp;
    int masterWidth, masterHeight, masterBW;
    double x1, y1, x2, y2;

    masterPtr->flags &= ~PARENT_RECONFIG_PENDING;

    /*
     * Iterate over all the slaves for the master.  Each slave's
     * geometry can be computed independently of the other slaves.
     */

    for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
          slavePtr = slavePtr->nextPtr) {
      /*
       * Step 1: compute size and borderwidth of master, taking into
       * account desired border mode.
       */

      masterBW = 0;
      masterWidth = Tk_Width(masterPtr->tkwin);
      masterHeight = Tk_Height(masterPtr->tkwin);
      if (slavePtr->borderMode == BM_INSIDE) {
          masterBW = Tk_InternalBorderWidth(masterPtr->tkwin);
      } else if (slavePtr->borderMode == BM_OUTSIDE) {
          masterBW = -Tk_Changes(masterPtr->tkwin)->border_width;
      }
      masterWidth -= 2*masterBW;
      masterHeight -= 2*masterBW;

      /*
       * Step 2:  compute size of slave (outside dimensions including
       * border) and location of anchor point within master.
       */

      x1 = slavePtr->x + masterBW + (slavePtr->relX*masterWidth);
      x = (int) (x1 + ((x1 > 0) ? 0.5 : -0.5));
      y1 = slavePtr->y + masterBW + (slavePtr->relY*masterHeight);
      y = (int) (y1 + ((y1 > 0) ? 0.5 : -0.5));
      if (slavePtr->flags & (CHILD_WIDTH|CHILD_REL_WIDTH)) {
          width = 0;
          if (slavePtr->flags & CHILD_WIDTH) {
            width += slavePtr->width;
          }
          if (slavePtr->flags & CHILD_REL_WIDTH) {
            /*
             * The code below is a bit tricky.  In order to round
             * correctly when both relX and relWidth are specified,
             * compute the location of the right edge and round that,
             * then compute width.  If we compute the width and round
             * it, rounding errors in relX and relWidth accumulate.
             */

            x2 = x1 + (slavePtr->relWidth*masterWidth);
            tmp = (int) (x2 + ((x2 > 0) ? 0.5 : -0.5));
            width += tmp - x;
          }
      } else {
          width = Tk_ReqWidth(slavePtr->tkwin)
                + 2*Tk_Changes(slavePtr->tkwin)->border_width;
      }
      if (slavePtr->flags & (CHILD_HEIGHT|CHILD_REL_HEIGHT)) {
          height = 0;
          if (slavePtr->flags & CHILD_HEIGHT) {
            height += slavePtr->height;
          }
          if (slavePtr->flags & CHILD_REL_HEIGHT) {
            /* 
             * See note above for rounding errors in width computation.
             */

            y2 = y1 + (slavePtr->relHeight*masterHeight);
            tmp = (int) (y2 + ((y2 > 0) ? 0.5 : -0.5));
            height += tmp - y;
          }
      } else {
          height = Tk_ReqHeight(slavePtr->tkwin)
                + 2*Tk_Changes(slavePtr->tkwin)->border_width;
      }

      /*
       * Step 3: adjust the x and y positions so that the desired
       * anchor point on the slave appears at that position.  Also
       * adjust for the border mode and master's border.
       */

      switch (slavePtr->anchor) {
          case TK_ANCHOR_N:
            x -= width/2;
            break;
          case TK_ANCHOR_NE:
            x -= width;
            break;
          case TK_ANCHOR_E:
            x -= width;
            y -= height/2;
            break;
          case TK_ANCHOR_SE:
            x -= width;
            y -= height;
            break;
          case TK_ANCHOR_S:
            x -= width/2;
            y -= height;
            break;
          case TK_ANCHOR_SW:
            y -= height;
            break;
          case TK_ANCHOR_W:
            y -= height/2;
            break;
          case TK_ANCHOR_NW:
            break;
          case TK_ANCHOR_CENTER:
            x -= width/2;
            y -= height/2;
            break;
      }

      /*
       * Step 4: adjust width and height again to reflect inside dimensions
       * of window rather than outside.  Also make sure that the width and
       * height aren't zero.
       */

      width -= 2*Tk_Changes(slavePtr->tkwin)->border_width;
      height -= 2*Tk_Changes(slavePtr->tkwin)->border_width;
      if (width <= 0) {
          width = 1;
      }
      if (height <= 0) {
          height = 1;
      }

      /*
       * Step 5: reconfigure the window and map it if needed.  If the
       * slave is a child of the master, we do this ourselves.  If the
       * slave isn't a child of the master, let Tk_MaintainWindow do
       * the work (it will re-adjust things as relevant windows map,
       * unmap, and move).
       */

      if (masterPtr->tkwin == Tk_Parent(slavePtr->tkwin)) {
          if ((x != Tk_X(slavePtr->tkwin))
                || (y != Tk_Y(slavePtr->tkwin))
                || (width != Tk_Width(slavePtr->tkwin))
                || (height != Tk_Height(slavePtr->tkwin))) {
            Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height);
          }

          /*
           * Don't map the slave unless the master is mapped: the slave
           * will get mapped later, when the master is mapped.
           */

          if (Tk_IsMapped(masterPtr->tkwin)) {
            Tk_MapWindow(slavePtr->tkwin);
          }
      } else {
          if ((width <= 0) || (height <= 0)) {
            Tk_UnmaintainGeometry(slavePtr->tkwin, masterPtr->tkwin);
            Tk_UnmapWindow(slavePtr->tkwin);
          } else {
            Tk_MaintainGeometry(slavePtr->tkwin, masterPtr->tkwin,
                  x, y, width, height);
          }
      }
    }
}

/*
 *----------------------------------------------------------------------
 *
 * MasterStructureProc --
 *
 *    This procedure is invoked by the Tk event handler when
 *    StructureNotify events occur for a master window.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Structures get cleaned up if the window was deleted.  If the
 *    window was resized then slave geometries get recomputed.
 *
 *----------------------------------------------------------------------
 */

static void
MasterStructureProc(clientData, eventPtr)
    ClientData clientData;    /* Pointer to Master structure for window
                         * referred to by eventPtr. */
    XEvent *eventPtr;         /* Describes what just happened. */
{
    register Master *masterPtr = (Master *) clientData;
    register Slave *slavePtr, *nextPtr;

    if (eventPtr->type == ConfigureNotify) {
      if ((masterPtr->slavePtr != NULL)
            && !(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
          masterPtr->flags |= PARENT_RECONFIG_PENDING;
          Tcl_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
      }
    } else if (eventPtr->type == DestroyNotify) {
      for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
            slavePtr = nextPtr) {
          slavePtr->masterPtr = NULL;
          nextPtr = slavePtr->nextPtr;
          slavePtr->nextPtr = NULL;
      }
      Tcl_DeleteHashEntry(Tcl_FindHashEntry(&masterTable,
            (char *) masterPtr->tkwin));
      if (masterPtr->flags & PARENT_RECONFIG_PENDING) {
          Tcl_CancelIdleCall(RecomputePlacement, (ClientData) masterPtr);
      }
      masterPtr->tkwin = NULL;
      ckfree((char *) masterPtr);
    } else if (eventPtr->type == MapNotify) {
      /*
       * When a master gets mapped, must redo the geometry computation
       * so that all of its slaves get remapped.
       */

      if ((masterPtr->slavePtr != NULL)
            && !(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
          masterPtr->flags |= PARENT_RECONFIG_PENDING;
          Tcl_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
      }
    } else if (eventPtr->type == UnmapNotify) {
      /*
       * Unmap all of the slaves when the master gets unmapped,
       * so that they don't keep redisplaying themselves.
       */

      for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
            slavePtr = slavePtr->nextPtr) {
          Tk_UnmapWindow(slavePtr->tkwin);
      }
    }
}

/*
 *----------------------------------------------------------------------
 *
 * SlaveStructureProc --
 *
 *    This procedure is invoked by the Tk event handler when
 *    StructureNotify events occur for a slave window.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Structures get cleaned up if the window was deleted.
 *
 *----------------------------------------------------------------------
 */

static void
SlaveStructureProc(clientData, eventPtr)
    ClientData clientData;    /* Pointer to Slave structure for window
                         * referred to by eventPtr. */
    XEvent *eventPtr;         /* Describes what just happened. */
{
    register Slave *slavePtr = (Slave *) clientData;

    if (eventPtr->type == DestroyNotify) {
      UnlinkSlave(slavePtr);
      Tcl_DeleteHashEntry(Tcl_FindHashEntry(&slaveTable,
            (char *) slavePtr->tkwin));
      ckfree((char *) slavePtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * PlaceRequestProc --
 *
 *    This procedure is invoked by Tk whenever a slave managed by us
 *    changes its requested geometry.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The window will get relayed out, if its requested size has
 *    anything to do with its actual size.
 *
 *----------------------------------------------------------------------
 */

      /* ARGSUSED */
static void
PlaceRequestProc(clientData, tkwin)
    ClientData clientData;          /* Pointer to our record for slave. */
    Tk_Window tkwin;                /* Window that changed its desired
                               * size. */
{
    Slave *slavePtr = (Slave *) clientData;
    Master *masterPtr;

    if (((slavePtr->flags & (CHILD_WIDTH|CHILD_REL_WIDTH)) != 0)
          && ((slavePtr->flags & (CHILD_HEIGHT|CHILD_REL_HEIGHT)) != 0)) {
      return;
    }
    masterPtr = slavePtr->masterPtr;
    if (masterPtr == NULL) {
      return;
    }
    if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
      masterPtr->flags |= PARENT_RECONFIG_PENDING;
      Tcl_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
    }
}

/*
 *--------------------------------------------------------------
 *
 * PlaceLostSlaveProc --
 *
 *    This procedure is invoked by Tk whenever some other geometry
 *    claims control over a slave that used to be managed by us.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Forgets all placer-related information about the slave.
 *
 *--------------------------------------------------------------
 */

      /* ARGSUSED */
static void
PlaceLostSlaveProc(clientData, tkwin)
    ClientData clientData;    /* Slave structure for slave window that
                         * was stolen away. */
    Tk_Window tkwin;          /* Tk's handle for the slave window. */
{
    register Slave *slavePtr = (Slave *) clientData;

    if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
      Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
    }
    Tk_UnmapWindow(tkwin);
    UnlinkSlave(slavePtr);
    Tcl_DeleteHashEntry(Tcl_FindHashEntry(&slaveTable, (char *) tkwin));
    Tk_DeleteEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc,
          (ClientData) slavePtr);
    ckfree((char *) slavePtr);
}

Generated by  Doxygen 1.6.0   Back to index