This is draw.c in view mode; [Download] [Up]
/* ----------------------------------------------------------------------------
* File : draw.c
* Author : Mark Stern (mks@cs.brown.edu)
* Date : Thu Aug 30 1990
* Class : CS257 - Computational Geometry
* Purpose : drawing-specific routines for dynamic tree program
* ----------------------------------------------------------------------------
*/
#include <X11/Intrinsic.h>
#include <X11/StringDefs.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;
/* ------------------------------------------------------------------------- */
/* Forward Function Declarations */
/* ------------------------------------------------------------------------- */
void DrawNode();
void DrawTreeContour();
/* ------------------------------------------------------------------------- */
/* Functions */
/* ------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------
*
* BeginFrame() provides an abstraction for double buffering. It should
* be called prior to creating a new frame of animation.
*
* ----------------------------------------------------------------------------
*/
void
BeginFrame()
{
DBLbegin_frame(TreeDrawingAreaDB);
}
/* ----------------------------------------------------------------------------
*
* EndFrame() provides an abstraction for double buffering. It should
* be called after creating a new frame of animation.
*
* ----------------------------------------------------------------------------
*/
void
EndFrame()
{
DBLend_frame(TreeDrawingAreaDB);
}
/* ----------------------------------------------------------------------------
*
* GetDrawingSize() gets the size of the drawing area, and returns the
* dimensions in the arguments.
*
* ----------------------------------------------------------------------------
*/
void
GetDrawingSize(width, height)
int *width, *height;
{
Dimension w, h;
XtVaGetValues(TreeDrawingArea,
XtNwidth, &w,
XtNheight, &h,
NULL);
*width = (int) w;
*height = (int) h;
}
/* ----------------------------------------------------------------------------
*
* SetDrawingSize() sets the size of the drawing area to the given
* dimensions.
*
* ----------------------------------------------------------------------------
*/
void
SetDrawingSize(width, height)
int width, height;
{
XtVaSetValues(TreeDrawingArea,
XtNwidth, (Dimension) width,
XtNheight, (Dimension) height,
NULL);
}
/* ----------------------------------------------------------------------------
*
* SetDrawingTree() is used to specify what tree is to be drawn in the
* drawing area.
*
* ----------------------------------------------------------------------------
*/
void
SetDrawingTree(tree)
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(node, label)
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(color)
int color;
{
XSetForeground(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
TreeDrawingAreaDB->colors[color]);
}
/* ----------------------------------------------------------------------------
*
* SetLineWidth() sets the line width of lines drawn in the TreeDrawingArea.
*
* ----------------------------------------------------------------------------
*/
void
SetLineWidth(width)
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.
*
* ----------------------------------------------------------------------------
*/
void
SetContours(option)
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, pos_mode)
Tree *tree;
{
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(node, pos_mode)
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, pos_mode, color, detach, select, recursive)
Tree *tree;
PosMode pos_mode;
int color;
int detach;
int select;
int recursive;
{
Widget w = TreeDrawingArea;
Polyline *contour, *tail;
Tree *child;
int x, y, i;
if (tree == NULL)
return;
if ((select && tree->show_contour) || !select) {
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) { /* 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) { /* 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, select, 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, pos_mode)
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);
}
/* ----------------------------------------------------------------------------
*
* 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.
*
* ----------------------------------------------------------------------------
*/
void
ShiftTree(tree, done)
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.
*
* ----------------------------------------------------------------------------
*/
void
AnimateTree(tree)
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().
*
* ----------------------------------------------------------------------------
*/
void
AnimateZip(tree)
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.
*
* ----------------------------------------------------------------------------
*/
int
CountNodes(tree)
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.
*
* ----------------------------------------------------------------------------
*/
void
CollectNodeRectangles(node, rectangles, fill)
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.
*
* ----------------------------------------------------------------------------
*/
void
GetSubTreeRectangles(tree, rectangles, nrectangles, fill)
Tree *tree;
XRectangle **rectangles;
int *nrectangles, 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.
*
* ----------------------------------------------------------------------------
*/
void
CollectNodeSegments(node, segments)
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.
*
* ----------------------------------------------------------------------------
*/
void
GetSubTreeSegments(tree, segments, nsegments)
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 *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, x, y, node)
Tree *tree, **node;
int x, y;
{
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(w, client_data, event)
Widget w;
caddr_t client_data;
XExposeEvent *event;
{
if (event->count == 0) {
BeginFrame();
SetContours(TreeShowContourOption);
DrawTree(TheTree, New);
EndFrame();
}
}
/* ----------------------------------------------------------------------------
*
* ExpandCollapseNode is called to expand or collapse a node in the tree.
*
* ----------------------------------------------------------------------------
*/
void
ExpandCollapseNode(node)
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(base_node, node_pos, new_node_text)
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;
}
}
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(node)
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.
*
* ----------------------------------------------------------------------------
*/
ResetLabels(tree)
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 *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();
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.