Logo Search packages:      
Sourcecode: xemacs21-packages version File versions

draw.c

/* ----------------------------------------------------------------------------
 * File    : draw.c
 * Purpose : drawing-specific routines for dynamic tree program
 * ----------------------------------------------------------------------------
 */

#include <stdlib.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>

#include "dissolve.h"
#include "defs.h"
#include "tree.h"
#include "dbl.h"
#include "intf.h"

/* ------------------------------------------------------------------------- */
/*                      Global Variables                             */
/* ------------------------------------------------------------------------- */

Tree *TheTree;


/* ------------------------------------------------------------------------- */
/*                      Local Variables                              */
/* ------------------------------------------------------------------------- */

static char AnimationMode = FALSE;
static char strbuf[BUFSIZ];
static int  AnimationStep = ANIMATION_STEP;


/* ------------------------------------------------------------------------- */
/*                         Functions                                 */
/* ------------------------------------------------------------------------- */


/* ----------------------------------------------------------------------------
 *
 *   BeginFrame() provides an abstraction for double buffering. It should
 *   be called prior to creating a new frame of animation.
 *
 * ----------------------------------------------------------------------------
 */

void
BeginFrame(void)
{
  DBLbegin_frame(TreeDrawingAreaDB);
}


/* ----------------------------------------------------------------------------
 *
 *   EndFrame() provides an abstraction for double buffering. It should
 *   be called after creating a new frame of animation.
 *
 * ----------------------------------------------------------------------------
 */

void
EndFrame(void)
{
  /* Changed second parameter from 0 to 1 at Torgeir Veimo's suggestion
     on 9/21/1997.  -- Bob Weiner, BeOpen.com */
  DBLend_frame(TreeDrawingAreaDB, 1);
}


/* ----------------------------------------------------------------------------
 *
 *   GetDrawingSize() gets the size of the drawing area, and returns the
 *   dimensions in the arguments.
 *
 * ----------------------------------------------------------------------------
 */

static void
GetDrawingSize(int *width, int *height)
{
  Dimension w, h;
  Arg al [2];

  XtSetArg (al [0], XtNwidth,  &w);
  XtSetArg (al [1], XtNheight, &h);
  XtGetValues(TreeDrawingArea, al, 2);

  *width  = (int) w;
  *height = (int) h;
}


/* ----------------------------------------------------------------------------
 *
 *   SetDrawingSize() sets the size of the drawing area to the given
 *   dimensions.
 *
 * ----------------------------------------------------------------------------
 */

static void
SetDrawingSize(int width, int height)
{
  Arg al [2];
  XtSetArg (al [0], XtNwidth,   (Dimension) width);
  XtSetArg (al [1], XtNheight,  (Dimension) height);
  XtSetValues (TreeDrawingArea, al, 2);

  XClearArea(TreeDrawingAreaDB->display,
           TreeDrawingAreaDB->window, 0, 0, 0, 0, FALSE);
}


/* ----------------------------------------------------------------------------
 *
 *   SetDrawingTree() is used to specify what tree is to be drawn in the
 *   drawing area.
 *
 * ----------------------------------------------------------------------------
 */

void
SetDrawingTree(Tree *tree)
{
  TheTree = tree;
}


/* ----------------------------------------------------------------------------
 *
 *   SetNodeLabel() sets the label text of the specified node and computes
 *   the bounding rectangle so that the layout can be determined. This
 *   function is called when new nodes are created. If TreeAlignNodes is
 *   True, the string is truncated so that the node's width is no longer
 *   than TreeParentDistance.
 *
 * ----------------------------------------------------------------------------
 */

void
SetNodeLabel(Tree *node, char *label)
{
  int         len;
  int         dummy;
  XCharStruct rtrn;

  len = strlen(label);
  while (len > 1) {
    XTextExtents(TreeLabelFont, label, len, &dummy, &dummy, &dummy, &rtrn);
    node->width  = rtrn.lbearing + rtrn.rbearing + (LABEL_MAT_WIDTH * 2) + 1;
    node->height = rtrn.ascent + rtrn.descent + (LABEL_MAT_HEIGHT * 2) + 1;
    if (TreeAlignNodes) {
      if (node->width <= (2 * TreeParentDistance))
      break;
      else
      len--;
    }
    else
      break;
  }

  node->label.text = label;
  node->label.len  = len;
  node->label.xoffset = LABEL_MAT_WIDTH + 1,
  node->label.yoffset = rtrn.ascent + LABEL_MAT_HEIGHT + 1;
}


/* ----------------------------------------------------------------------------
 *
 *   SetDrawColor() sets the drawing color of the TreeDrawingArea.
 *
 * ----------------------------------------------------------------------------
 */

void
SetDrawColor(int color)
{
  XSetForeground(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
             TreeDrawingAreaDB->colors[color]);
}

/* ----------------------------------------------------------------------------
 *
 *   SetLineWidth() sets the line width of lines drawn in the TreeDrawingArea.
 *
 * ----------------------------------------------------------------------------
 */

static void
SetLineWidth(unsigned int width)
{
  XSetLineAttributes(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
                 width, LineSolid, CapButt, JoinRound);
}


/* ----------------------------------------------------------------------------
 *
 *   SetContours() sets the visibility of three possible contour modes:
 *   the outside contour, all subtree contours, or selected contours.
 *
 * ----------------------------------------------------------------------------
 */

static void
SetContours(ContourOption option)
{
  if (option == NoContours) {
    switch (TreeShowContourOption) {
    case OutsideContour:
      DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, FALSE);
      break;
    case AllContours:
      DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);
      break;
    case SelectedContours:
      DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, TRUE, TRUE);
      break;
    default:
      ;
    }
    DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);
  }
  else if (option == OutsideContour) {
    switch (TreeShowContourOption) {
    case AllContours:
      DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);
      break;
    case SelectedContours:
      DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, TRUE, TRUE);
      break;
    default:
      ;
    }
    DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, FALSE, FALSE);
  } else if (option == AllContours) {
    DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, FALSE, TRUE);
  } else if (option == SelectedContours) {
    switch (TreeShowContourOption) {
    case AllContours:
      DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);
      break;
    case OutsideContour:
      DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, FALSE);
      break;
    default:
      DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);
    }
    DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, TRUE, TRUE);
  }
  TreeShowContourOption = option;
}


/* ----------------------------------------------------------------------------
 *
 *   HiliteNode() is called by Unzip() to change the color of a node.
 *
 * ----------------------------------------------------------------------------
 */

void
HiliteNode(Tree *tree, PosMode pos_mode)
{
  SetDrawColor(HIGHLIGHT_COLOR);
  DrawNode(tree, pos_mode);
  SetDrawColor(TREE_COLOR);
}


/* ----------------------------------------------------------------------------
 *
 *   DrawNode() takes a node and draws the node in the specified widget
 *   at its (x,y) coordinate. (x, y) indicates the upper-left corner where
 *   the node is drawn. Also, a line is drawn from the center of the left
 *   edge to the center of the parent's right edge. 'draw_mode' specifies
 *   the drawing mode (whether or not the node is erased, rather than drawn).
 *   'pos_mode' determines whether or not to use the old position of the node.
 *   This flag is used in animating the movement of a node from its old
 *   position to its new position.
 *
 * ----------------------------------------------------------------------------
 */

void
DrawNode(Tree *node, PosMode pos_mode)
{
  Widget   w;
  GC       gc;

  w  = TreeDrawingArea;
  gc = TreeDrawingAreaDB->gc;

  if (pos_mode == Old) {
    XDrawString(XtDisplay(w), XtWindow(w), gc,
            node->old_pos.x + node->label.xoffset,
            node->old_pos.y + node->label.yoffset,
            node->label.text, node->label.len);
    XDrawRectangle(XtDisplay(w), XtWindow(w), gc,
               node->old_pos.x, node->old_pos.y,
               node->width, node->height);
    if (node->parent)
      XDrawLine(XtDisplay(w), XtWindow(w), gc,
            node->old_pos.x - 1,
            node->old_pos.y + (node->height / 2),
            node->parent->old_pos.x + node->parent->width + 1,
            node->parent->old_pos.y + (node->parent->height / 2));
    if (node->elision) {
      XSetFillStyle(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
                FillTiled);
      XFillRectangle(XtDisplay(w), XtWindow(w), gc,
                 node->old_pos.x + node->width - ELISION_WIDTH,
                 node->old_pos.y + 1, ELISION_WIDTH, node->height - 1);
      XSetFillStyle(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
                FillSolid);
    }
  } else {
    XDrawString(XtDisplay(w), XtWindow(w), gc,
            node->pos.x + node->label.xoffset,
            node->pos.y + node->label.yoffset,
            node->label.text, node->label.len);

    XDrawRectangle(XtDisplay(w), XtWindow(w), gc,
               node->pos.x, node->pos.y,
               node->width, node->height);
    if (node->parent)
      XDrawLine(XtDisplay(w), XtWindow(w), gc,
            node->pos.x - 1,
            node->pos.y + (node->height / 2),
            node->parent->pos.x + node->parent->width + 1,
            node->parent->pos.y + (node->parent->height / 2));
    if (node->elision) {
      XSetFillStyle(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
                FillTiled);
      XFillRectangle(XtDisplay(w), XtWindow(w), gc,
                 node->pos.x + node->width - ELISION_WIDTH,
                 node->pos.y + 1, ELISION_WIDTH, node->height - 1);
      XSetFillStyle(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
                FillSolid);
    }
  }
}


/* ----------------------------------------------------------------------------
 *
 *   DrawTreeContour() draws the contour of the specified subtree. Bridges
 *   are not traversed, so the actual subtree contour is drawn, as opposed
 *   to the merged contour. 'color' specifies the drawing color. If 'detach'
 *   is True,  the lines attaching the subtree contour to the node are not
 *   drawn.  If 'select' is true, then only subtrees that are flagged as
 *   selected are shown. If 'recursive' is True, the entire tree is traversed.
 *
 * ----------------------------------------------------------------------------
 */

void
DrawTreeContour(Tree *tree, PosMode pos_mode,
            int color, int detach_p, int select_p, int recursive)
{
  Widget w = TreeDrawingArea;
  Polyline *contour, *tail;
  Tree *child;
  int x, y, i;

  if (tree == NULL)
    return;

  if ((select_p && tree->show_contour) || !select_p) {

    SetDrawColor(color);
    SetLineWidth(TreeContourWidth);

    /* draw upper contour */
    contour = tree->contour.upper.head;
    tail    = tree->contour.upper.tail;
    if (pos_mode == Old) {
      x = tree->old_pos.x - tree->border;
      y = tree->old_pos.y - tree->border;
    }
    else {
      x = tree->pos.x - tree->border;
      y = tree->pos.y - tree->border;
    }

    if (detach_p) {           /* skip over attaching lines */
      for (i = 0 ; i < 2 ; i++) {
      x += contour->dx;
      y += contour->dy;
      contour = contour->link;
      }
    }

    while (contour) {
      XDrawLine(XtDisplay(w), XtWindow(w), TreeDrawingAreaDB->gc,
            x, y, x + contour->dx, y + contour->dy);
      x += contour->dx;
      y += contour->dy;
      if (contour == tail)    /* make sure you don't follow bridges */
      contour = NULL;
      else
      contour = contour->link;
    }

    /* draw lower contour */
    contour = tree->contour.lower.head;
    tail    = tree->contour.lower.tail;
    if (pos_mode == Old) {
      x = tree->old_pos.x - tree->border;
      y = tree->old_pos.y + tree->border + tree->height;
    } else {
      x = tree->pos.x - tree->border;
      y = tree->pos.y + tree->border + tree->height;
    }

    if (detach_p) {           /* skip over attaching lines */
      for (i = 0 ; i < 2 ; i++) {
      x += contour->dx;
      y += contour->dy;
      contour = contour->link;
      }
    }

    while (contour) {
      XDrawLine(XtDisplay(w), XtWindow(w), TreeDrawingAreaDB->gc,
            x, y, x + contour->dx, y + contour->dy);
      x += contour->dx;
      y += contour->dy;
      if (contour == tail)    /* make sure you don't follow bridges */
      contour = NULL;
      else
      contour = contour->link;
    }
  }

  if (recursive) {
    FOREACH_CHILD(child, tree)
      if (!child->elision)
      DrawTreeContour(child, pos_mode, color,
                  detach_p, select_p, recursive);
  }

  SetDrawColor(TREE_COLOR);
  SetLineWidth(0);
}


/* ----------------------------------------------------------------------------
 *
 *   DrawTree() traverses the given tree, drawing the node and connecting
 *   segments. The tree contours are also drawn at each step, if enabled.
 *   'draw_mode' specifies the drawing mode in which the tree is drawn.
 *   'pos_mode' determines whether or not to use the old position of the node.
 *   This flag is used in animating the movement of a node from its old
 *   position to its new position. DrawNode() is called to draw an individual
 *   node.
 *
 * ----------------------------------------------------------------------------
 */

void
DrawTree(Tree *tree, PosMode pos_mode)
{
  if (tree == NULL)
    return;

  DrawNode(tree, pos_mode);

  /* do stuff that animates Unzip() */
  if (tree->split) {
    if (!AnimationMode ||
      (tree->pos.x == tree->old_pos.x &&
       tree->pos.y == tree->old_pos.y))
      DrawTreeContour(tree, pos_mode, SPLIT_COLOR, FALSE, FALSE, FALSE);
    else
      DrawTreeContour(tree, pos_mode, ACTION_COLOR, FALSE, FALSE, FALSE);
  }
  if (tree->on_path)
    HiliteNode(tree, pos_mode);

  if (tree->child && !tree->elision)
    DrawTree(tree->child, pos_mode);
  if (tree->sibling)
    DrawTree(tree->sibling, pos_mode);

  maybe_highlight_node(tree);
}


/* ----------------------------------------------------------------------------
 *
 *   ShiftTree() adjusts the positions of each node so that it moves from
 *   the "old" position towards the "new position". This is used by
 *   AnimateTree(). 'done' is set to FALSE if the tree is not in its
 *   final position; it is used to determine when to stop animating the tree.
 *
 * ----------------------------------------------------------------------------
 */

static void
ShiftTree(Tree *tree, int *done)
{
  Tree *child;

  if (tree->old_pos.x != tree->pos.x ||
      tree->old_pos.y != tree->pos.y)
    {
      tree->old_pos.x = tree->pos.x;
      tree->old_pos.y = tree->pos.y;
    }

  FOREACH_CHILD(child, tree)
    ShiftTree(child, done);
}


/* ----------------------------------------------------------------------------
 *
 *   AnimateTree() draws the given tree in a series of steps to give the
 *   effect of animation from the "old" layout to the "new" layout of the
 *   tree.
 *
 *   The algorithm used here is not efficient; the entire tree is drawn
 *   on each iteration of the animation sequence; it would be more efficient
 *   to only redraw what is necessary. However, the method used here takes
 *   advantage of existing code without modification.
 *
 * ----------------------------------------------------------------------------
 */

static void
AnimateTree(Tree *tree)
{
  int done = FALSE;

  AnimationMode = FALSE;
  /* highlight which nodes have to move */
  BeginFrame();
    DrawTree(tree, Old);
  EndFrame();
  Pause();
  if (PauseAfterStep)
    AnimationStep = ANIMATION_STEP_STEP;
  while (!done) {
    done = TRUE;
    ShiftTree(tree, &done);
    BeginFrame();
      DrawTree(tree, Old);
    EndFrame();
    if (PauseAfterStep)
      Pause();
  }
  if (PauseAfterStep)
    AnimationStep = ANIMATION_STEP;
  AnimationMode = FALSE;
}


/* ----------------------------------------------------------------------------
 *
 *   AnimateZip() generates a sequence of frames that animates the Zip() step.
 *   It is similar in logical structure to Zip().
 *
 * ----------------------------------------------------------------------------
 */

static void
AnimateZip(Tree *tree)
{
  Tree *child;

  /* show results of Join() step */
  if (tree->child) {
    BeginFrame();
      FOREACH_CHILD(child, tree)
        child->split = FALSE;
      DrawTree(TheTree, New);
      DrawTreeContour(tree, New, CONTOUR_COLOR, TRUE, FALSE, FALSE);
    EndFrame();

    StatusMsg("Zip: merge and join contours", FALSE);
    Pause();

    /* show results of AttachParent() step */
    BeginFrame();
      DrawTree(TheTree, New);
      DrawTreeContour(tree, New, CONTOUR_COLOR, FALSE, FALSE, FALSE);
    EndFrame();

    StatusMsg("Zip: attach parents", FALSE);
    Pause();
  }

  tree->on_path = FALSE;

  if (tree->parent)
    AnimateZip(tree->parent);
  else {
    tree->on_path = FALSE;
    BeginFrame();
      DrawTree(TheTree, New);
      DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, FALSE, FALSE);
    EndFrame();
    StatusMsg("Zip: reassemble entire contour", FALSE);
    Pause();
  }
}


/* ----------------------------------------------------------------------------
 *
 *   CountNodes() returns the number of nodes in the specified tree.
 *   Nodes below a node that has been collapsed are ignored.
 *
 * ----------------------------------------------------------------------------
 */

static int
CountNodes(Tree *tree)
{
  int num_nodes = 1;          /* count root of subtree */
  Tree *child;

  if (!tree->elision) {
    FOREACH_CHILD(child, tree)
      num_nodes += CountNodes(child);
  }
  return num_nodes;
}


/* ----------------------------------------------------------------------------
 *
 *   CollectNodeRectangles() is a recursive function used by
 *   GetSubTreeRectangles() to collect the rectangles of descendant nodes
 *   into the pre-allocated storage passed to this function.
 *
 * ----------------------------------------------------------------------------
 */

static void
CollectNodeRectangles(Tree *node, XRectangle **rectangles, int fill)
{
  Tree *child;

  (*rectangles)->x = node->pos.x;
  (*rectangles)->y = node->pos.y;
  if (fill) {
    (*rectangles)->width = node->width + 1;
    (*rectangles)->height = node->height + 1;
  } else {
    (*rectangles)->width = node->width;
    (*rectangles)->height = node->height;
  }
  (*rectangles)++;

  if (!node->elision)
    FOREACH_CHILD(child, node)
      CollectNodeRectangles(child, rectangles, fill);
}


/* ----------------------------------------------------------------------------
 *
 *   GetSubTreeRectangles() builds an array of XRectangles that contain
 *   all the node rectangles in the tree, except the root node itself.
 *   The array is returned in 'rectangles' and the number of rectangles
 *   is returned in 'nrectangles.' Storage for the rectangles is allocated
 *   in this function. This function is used by PickAction to determine
 *   what rectangles need to be dissolved away. 'fill', if True, specifies
 *   that the rectangles should be 1 pixel larger in each dimension to
 *   compensate for FillRectangle behavior.
 *
 * ----------------------------------------------------------------------------
 */

static void
GetSubTreeRectangles(Tree *tree, XRectangle **rectangles,
                 int *nrectangles, int fill)
{
  Tree *child;
  XRectangle *crect;          /* current rectangle */

  *nrectangles = CountNodes(tree) - 1;        /* don't count root node */
  *rectangles = (XRectangle *) malloc(sizeof(XRectangle) * *nrectangles);
  ASSERT(*rectangles, "could not allocate memory for rectangles");

  crect = *rectangles;
  if (!tree->elision)
    FOREACH_CHILD(child, tree)
      CollectNodeRectangles(child, &crect, fill);
}


/* ----------------------------------------------------------------------------
 *
 *   CollectNodeSegments() is a recursive function used by GetSubTreeSegments()
 *   to collect the line segments connecting nodes into the pre-allocated
 *   storage passed to this function.
 *
 * ----------------------------------------------------------------------------
 */

static void
CollectNodeSegments(Tree *node, XSegment **segments)
{
  Tree *child;

  (*segments)->x1 = node->pos.x - 1;
  (*segments)->y1 = node->pos.y + (node->height / 2),
  (*segments)->x2 = node->parent->pos.x + node->parent->width + 1;
  (*segments)->y2 = node->parent->pos.y + (node->parent->height / 2);
  (*segments)++;

  if (!node->elision)
    FOREACH_CHILD(child, node)
      CollectNodeSegments(child, segments);
}


/* ----------------------------------------------------------------------------
 *
 *   GetSubTreeSegments() builds an array of XSegments that contain
 *   all the line segments connecting the nodes in the tree. The array is
 *   returned in 'segments' and the number of segments is returned in
 *   'nsegments.' Storage for the segments is allocated in this function.
 *   This function is used by PickAction to determine what line segments
 *   rectangles need to be dissolved away.
 *
 * ----------------------------------------------------------------------------
 */

static void
GetSubTreeSegments(Tree *tree, XSegment **segments, int *nsegments)
{
  Tree *child;
  XSegment *cseg;       /* current segment */

  *nsegments = CountNodes(tree) - 1;
  *segments = (XSegment *) malloc(sizeof(XSegment) * *nsegments);
  ASSERT(*segments, "could not allocate memory for segments");

  cseg = *segments;
  if (!tree->elision)
    FOREACH_CHILD(child, tree)
      CollectNodeSegments(child, &cseg);
}


/* ----------------------------------------------------------------------------
 *
 *   ComputeSubTreeExtent() computes the extent of a subtree. This is
 *   easily computed based on the tree's contour, as in ComputeTreeSize().
 *   This extent is stored in the node, and used by SearchTree for
 *   pick-correlation.
 *
 *   This function assumes that the given tree has at least one child; do not
 *   pass a leaf node to this function.
 *
 * ----------------------------------------------------------------------------
 */

void
ComputeSubTreeExtent(Tree *tree)
{
  int width, height;
  int x_offset, y_offset;

  ComputeTreeSize(tree, &width, &height, &x_offset, &y_offset);
  tree->subextent.pos.x  = tree->child->pos.x - tree->child->border;
  tree->subextent.pos.y  = tree->pos.y - y_offset;
  tree->subextent.width  = width - (tree->child->pos.x - tree->pos.x) - 1;
  tree->subextent.height = height - 1;
}


/* ----------------------------------------------------------------------------
 *
 *   SearchTree() determines if a node's rectangular region encloses the
 *   specified point in (x,y). Rather than using a brute-force search
 *   through all node rectangles of a given tree, the subtree extents
 *   are used in a recursive fashion to drive the search as long as the
 *   given point is enclosed in an extent. In the worst case, the search
 *   time would be on the order of a brute-force search, but with complex
 *   trees, this method reduces the number of visits.
 *
 *   The extent of a subtree is computed by ComputeSubTreeExtent() and is
 *   stored in each node of the tree.
 *
 * ----------------------------------------------------------------------------
 */

int
SearchTree(Tree *tree, int x, int y, Tree **node)
{
  Tree *child;

  if (tree == NULL)
    return FALSE;

  if (PT_IN_RECT(x, y, tree->pos.x, tree->pos.y,
             tree->pos.x + tree->width,
             tree->pos.y + tree->height)) {
    *node = tree;
    return TRUE;
  }
  if (tree->child && (PT_IN_EXTENT(x, y, tree->subextent)))
    FOREACH_CHILD(child, tree) {
      if (SearchTree(child, x, y, node))
      return TRUE;
    }
  return FALSE;
}


/* ----------------------------------------------------------------------------
 *
 *   ExposeHandler() handles expose events in the TreeDrawingArea. This
 *   function is not intelligent; it just redraws the entire contents.
 *
 * ----------------------------------------------------------------------------
 */

void
ExposeHandler(Widget w, XtPointer closure,
            XEvent *event, Boolean *continue_to_dispatch)
{
  if (event->xexpose.count == 0) {
    BeginFrame();
      SetContours(TreeShowContourOption);
      DrawTree(TheTree, New);
    EndFrame();
  }
}


/* ----------------------------------------------------------------------------
 *
 *   ExpandCollapseNode is called to expand or collapse a node in the tree.
 *
 * ----------------------------------------------------------------------------
 */

void
ExpandCollapseNode(Tree *node)
{
  int        width, height;
  int        old_width, old_height;
  int        x_offset, y_offset;
  XRectangle *rectangles;
  XSegment   *segments;
  int        nrectangles, nsegments;
  int        expand = FALSE;
  Widget     w = TreeDrawingArea;

  StatusMsg("", TRUE);

  /* hilite node so that we know where we are */
  /* DrawTree will hilite it as a side effect */
  if (TreeShowSteps)
    node->on_path = TRUE;

  /* erase the contour before changing in the tree */
  if ((TreeShowContourOption != NoContours) || TreeShowSteps) {
    BeginFrame();
      DrawTree(TheTree, New);
    EndFrame();
  }

  sprintf(strbuf, "Node `%s' selected for %s", node->label.text,
        node->elision ? "expansion" : "collapse");
  StatusMsg(strbuf, FALSE);
  Pause();

  if (node->parent)
    Unzip(node->parent);
  else {
    StatusMsg("Show entire contour", FALSE);
    if (TreeShowSteps) {
      BeginFrame();
        DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, FALSE, FALSE);
        DrawTree(TheTree, New);
      EndFrame();
      Pause();
    }
  }

  /* are we collapsing a subtree? */
  if (!node->elision) {
    StatusMsg("Collapse subtree", FALSE);
    GetSubTreeRectangles(node, &rectangles, &nrectangles, TRUE);
    GetSubTreeSegments(node, &segments, &nsegments);
    DissolveTree(XtDisplay(w), XtWindow(w),
             rectangles, nrectangles,
             segments, nsegments, TRUE);
    free(rectangles);
    free(segments);
    Pause();

    StatusMsg("Replace subtree contour with leaf contour", FALSE);
    node->elision = TRUE;
    if (TreeShowSteps)
      node->split = TRUE;     /* turned off in AnimateZip */
    node->old_contour = node->contour;
    node->width += ELISION_WIDTH;
    LayoutLeaf(node);
    BeginFrame();
      SetContours(TreeShowContourOption);
      DrawTree(TheTree, New);
    EndFrame();
    Pause();
  } else {
    StatusMsg("Replace leaf contour with old subtree contour", FALSE);
    if (TreeShowSteps)
      node->split = TRUE;     /* turned off in AnimateZip */
    RuboutLeaf(node);
    node->contour = node->old_contour;
    expand = TRUE;
  }

  if (node->parent)
    Zip(node->parent);

  ComputeTreeSize(TheTree, &width, &height, &x_offset, &y_offset);
  PetrifyTree(TheTree, x_offset + MAT_SIZE, y_offset + MAT_SIZE);
  GetDrawingSize(&old_width, &old_height);

  if (expand) {
    SetDrawingSize(width + (2 * MAT_SIZE), height + (2 * MAT_SIZE));
    BeginFrame();
      DrawTree(TheTree, Old);
    EndFrame();
    Pause();
    StatusMsg("Move tree to new configuration", FALSE);
    AnimateTree(TheTree);
  } else {
    /* we are shrinking or staying the same */
    StatusMsg("Move tree to new configuration", FALSE);
    AnimateTree(TheTree);
    SetDrawingSize(width + (2 * MAT_SIZE), height + (2 * MAT_SIZE));
  }

  if (expand) {
    StatusMsg("Expand subtree", FALSE);
    node->elision = FALSE;

    /* erase elision marker */
    XSetFunction(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
             GXclear);
    XFillRectangle(XtDisplay(w), XtWindow(w), TreeDrawingAreaDB->gc,
               node->pos.x + node->width - ELISION_WIDTH + 1,
               node->pos.y, ELISION_WIDTH, node->height + 1);
    XSetFunction(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
             GXcopy);
    node->width -= ELISION_WIDTH;

    GetSubTreeRectangles(node, &rectangles, &nrectangles, FALSE);
    GetSubTreeSegments(node, &segments, &nsegments);
    /* dissolve the tree back in */
    DissolveTree(XtDisplay(w), XtWindow(w),
             rectangles, nrectangles,
             segments, nsegments, FALSE);
    free(rectangles);
    free(segments);

    /* draw text of nodes */
    BeginFrame();
      SetContours(TreeShowContourOption);
      DrawTree(TheTree, New);
    EndFrame();
    Pause();
  }

  if (TreeShowSteps) {
    node->on_path = FALSE;
    if (node->parent)
      AnimateZip(node->parent);
    else
      node->split = FALSE;
  }

  /* BUG: the display isn't properly updated here! */
  /* There should probably be some code here that
     clears the tree below the node currently being
     collapsed or expanded. Hack added 20.03.95 (torgeir@ii.uib.no).
     I'll try to fix this later. */

  XClearArea(TreeDisplay, XtWindow(TreeDrawingArea), 0, 0, 0, 0, FALSE);

  BeginFrame();
    SetContours(TreeShowContourOption);
    DrawTree(TheTree, New);
  EndFrame();

  StatusMsg("Ready", TRUE);
}

/* ----------------------------------------------------------------------------
 *
 *   InsertNode() handles the task of inserting a new node in the tree,
 *   at the given position with respect to 'base_node'. When 'node_pos' is
 *   either Before or After, it is assumed that 'base_node' has a parent.
 *
 * ----------------------------------------------------------------------------
 */

void
InsertNode(Tree *base_node, NodePosition node_pos, char *new_node_text)
{
  Tree *new_node;
  Tree *parent;
  Tree *sibling = NULL;
  Tree *child;

  int  width, height;
  int  x_offset, y_offset;

  StatusMsg("", TRUE);

  new_node = MakeNode();      /* should check for memory failure */
  SetNodeLabel(new_node, new_node_text);
  LayoutLeaf(new_node);

  /* figure out parent & sibling */
  if (node_pos == Child) {
    parent = base_node;
    /* find last child, if one exists */
    FOREACH_CHILD(child, parent)
      sibling = child;
  } else if (node_pos == After) {
    parent = base_node->parent;
    sibling = base_node;
  } else if (node_pos == Before) {
    parent = base_node->parent;
    FOREACH_CHILD(child, parent)
      if (child->sibling == base_node) {
      sibling = child;
      break;
      }
  } else {
    parent = NULL;
    abort();
  }

  if (TreeShowSteps)
    parent->on_path = TRUE;

  if ((TreeShowContourOption != NoContours) ||
      TreeShowSteps) {
    BeginFrame();
      DrawTree(TheTree, New);
    EndFrame();
  }

  sprintf(strbuf, "Inserting `%s' as child of node `%s'",
        new_node_text, parent->label.text);
  StatusMsg(strbuf, FALSE);
  Pause();

  /* erase the contour before changing in the tree */

  Insert(parent, new_node, sibling);

  ComputeTreeSize(TheTree, &width, &height, &x_offset, &y_offset);
  PetrifyTree(TheTree, x_offset + MAT_SIZE, y_offset + MAT_SIZE);

  if (sibling)
    new_node->old_pos = sibling->old_pos;
  else if (new_node->sibling)
    new_node->old_pos = new_node->sibling->old_pos;
  else {
    new_node->old_pos.x = new_node->pos.x;
    new_node->old_pos.y = parent->old_pos.y;
  }

  if (TreeShowSteps)
    new_node->split = TRUE;

  SetDrawingSize(width + (2 * MAT_SIZE), height + (2 * MAT_SIZE));
  BeginFrame();
    DrawTree(TheTree, Old);
  EndFrame();
  StatusMsg("Insert: add new node and contour", FALSE);
  Pause();

  StatusMsg("Move tree to new configuration", FALSE);
  AnimateTree(TheTree);

  if (TreeShowSteps) {
    if (parent)
      AnimateZip(parent);
  }

  BeginFrame();
    SetContours(TreeShowContourOption);
    DrawTree(TheTree, New);
  EndFrame();

  StatusMsg("Ready", TRUE);
}

/* ----------------------------------------------------------------------------
 *
 *   DeleteNode() handles the task of deleting a given node in the tree.
 *
 * ----------------------------------------------------------------------------
 */

void
DeleteNode(Tree *node)
{
  Tree *parent;

  XRectangle *rectangles;
  XSegment   *segments;
  int         nrectangles, nsegments;
  Widget      w = TreeDrawingArea;
  int  width, height;
  int  x_offset, y_offset;

  StatusMsg("", TRUE);

  if (TreeShowSteps)
    node->on_path = TRUE;

  /* erase the contour before changing in the tree */
  if ((TreeShowContourOption != NoContours) ||
      TreeShowSteps) {
    BeginFrame();
      DrawTree(TheTree, New);
    EndFrame();
  }

  sprintf(strbuf, "Node `%s' selected for deletion", node->label.text);
  StatusMsg(strbuf, FALSE);
  Pause();

  parent = node->parent;

  if (parent)
    Unzip(parent);
  else
    TheTree = NULL;           /* delete root of tree */

  /* fade out deleted subtree */
  StatusMsg("Delete subtree", FALSE);
  GetSubTreeRectangles(node, &rectangles, &nrectangles, TRUE);
  GetSubTreeSegments(node, &segments, &nsegments);
  DissolveTree(XtDisplay(w), XtWindow(w),
             rectangles, nrectangles,
             segments, nsegments, TRUE);
  free(rectangles);
  free(segments);

  Delete(node);

  BeginFrame();
  if (TheTree)
    DrawTree(TheTree, New);
  EndFrame();
  Pause();

  if (parent)
    Zip(parent);

  if (TheTree) {
    ComputeTreeSize(TheTree, &width, &height, &x_offset, &y_offset);
    PetrifyTree(TheTree, x_offset + MAT_SIZE, y_offset + MAT_SIZE);
    StatusMsg("Move tree to new configuration", FALSE);
    AnimateTree(TheTree);
    SetDrawingSize(width + (2 * MAT_SIZE), height + (2 * MAT_SIZE));
    Pause();

    if (TreeShowSteps) {
      if (parent)
      AnimateZip(parent);
    }

    BeginFrame();
      SetContours(TreeShowContourOption);
      DrawTree(TheTree, New);
    EndFrame();

  }

  StatusMsg("Ready", TRUE);
}


/* ----------------------------------------------------------------------------
 *
 *   ResetLabels() is called when the TreeAlignNodes mode is changed.
 *   When TreeParentDistance changes, the node width changes, so this
 *   function forces each node's width to be recomputed.
 *
 * ----------------------------------------------------------------------------
 */

void
ResetLabels(Tree *tree)
{
  Tree *child;

  SetNodeLabel(tree, tree->label.text);
  FOREACH_CHILD(child, tree)
    ResetLabels(child);
}


/* ----------------------------------------------------------------------------
 *
 *   SetupTree() handles the task of setting up the specified tree in
 *   the drawing area.
 *
 * ----------------------------------------------------------------------------
 */

void
SetupTree(Tree *tree)
{
  int width, height;
  int x_offset, y_offset;

  LayoutTree(tree);
  ComputeTreeSize(tree, &width, &height, &x_offset, &y_offset);
  PetrifyTree(tree, x_offset + MAT_SIZE, y_offset + MAT_SIZE);
  SetDrawingTree(tree);
  SetDrawingSize(width + (2 * MAT_SIZE), height + (2 * MAT_SIZE));
  BeginFrame();
    SetContours(TreeShowContourOption);
    DrawTree(tree, New);
  EndFrame();
}

Generated by  Doxygen 1.6.0   Back to index