1 /* ----------------------------------------------------------------------------
3 * Purpose : drawing-specific routines for dynamic tree program
4 * ----------------------------------------------------------------------------
8 #include <X11/Intrinsic.h>
9 #include <X11/StringDefs.h>
17 /* ------------------------------------------------------------------------- */
18 /* Global Variables */
19 /* ------------------------------------------------------------------------- */
24 /* ------------------------------------------------------------------------- */
26 /* ------------------------------------------------------------------------- */
28 static char AnimationMode = FALSE;
29 static char strbuf[BUFSIZ];
30 static int AnimationStep = ANIMATION_STEP;
33 /* ------------------------------------------------------------------------- */
35 /* ------------------------------------------------------------------------- */
38 /* ----------------------------------------------------------------------------
40 * BeginFrame() provides an abstraction for double buffering. It should
41 * be called prior to creating a new frame of animation.
43 * ----------------------------------------------------------------------------
49 DBLbegin_frame(TreeDrawingAreaDB);
53 /* ----------------------------------------------------------------------------
55 * EndFrame() provides an abstraction for double buffering. It should
56 * be called after creating a new frame of animation.
58 * ----------------------------------------------------------------------------
64 /* Changed second parameter from 0 to 1 at Torgeir Veimo's suggestion
65 on 9/21/1997. -- Bob Weiner, BeOpen.com */
66 DBLend_frame(TreeDrawingAreaDB, 1);
70 /* ----------------------------------------------------------------------------
72 * GetDrawingSize() gets the size of the drawing area, and returns the
73 * dimensions in the arguments.
75 * ----------------------------------------------------------------------------
79 GetDrawingSize(int *width, int *height)
84 XtSetArg (al [0], XtNwidth, &w);
85 XtSetArg (al [1], XtNheight, &h);
86 XtGetValues(TreeDrawingArea, al, 2);
93 /* ----------------------------------------------------------------------------
95 * SetDrawingSize() sets the size of the drawing area to the given
98 * ----------------------------------------------------------------------------
102 SetDrawingSize(int width, int height)
105 XtSetArg (al [0], XtNwidth, (Dimension) width);
106 XtSetArg (al [1], XtNheight, (Dimension) height);
107 XtSetValues (TreeDrawingArea, al, 2);
109 XClearArea(TreeDrawingAreaDB->display,
110 TreeDrawingAreaDB->window, 0, 0, 0, 0, FALSE);
114 /* ----------------------------------------------------------------------------
116 * SetDrawingTree() is used to specify what tree is to be drawn in the
119 * ----------------------------------------------------------------------------
123 SetDrawingTree(Tree *tree)
129 /* ----------------------------------------------------------------------------
131 * SetNodeLabel() sets the label text of the specified node and computes
132 * the bounding rectangle so that the layout can be determined. This
133 * function is called when new nodes are created. If TreeAlignNodes is
134 * True, the string is truncated so that the node's width is no longer
135 * than TreeParentDistance.
137 * ----------------------------------------------------------------------------
141 SetNodeLabel(Tree *node, char *label)
149 XTextExtents(TreeLabelFont, label, len, &dummy, &dummy, &dummy, &rtrn);
150 node->width = rtrn.lbearing + rtrn.rbearing + (LABEL_MAT_WIDTH * 2) + 1;
151 node->height = rtrn.ascent + rtrn.descent + (LABEL_MAT_HEIGHT * 2) + 1;
152 if (TreeAlignNodes) {
153 if (node->width <= (2 * TreeParentDistance))
162 node->label.text = label;
163 node->label.len = len;
164 node->label.xoffset = LABEL_MAT_WIDTH + 1,
165 node->label.yoffset = rtrn.ascent + LABEL_MAT_HEIGHT + 1;
169 /* ----------------------------------------------------------------------------
171 * SetDrawColor() sets the drawing color of the TreeDrawingArea.
173 * ----------------------------------------------------------------------------
177 SetDrawColor(int color)
179 XSetForeground(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
180 TreeDrawingAreaDB->colors[color]);
183 /* ----------------------------------------------------------------------------
185 * SetLineWidth() sets the line width of lines drawn in the TreeDrawingArea.
187 * ----------------------------------------------------------------------------
191 SetLineWidth(unsigned int width)
193 XSetLineAttributes(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
194 width, LineSolid, CapButt, JoinRound);
198 /* ----------------------------------------------------------------------------
200 * SetContours() sets the visibility of three possible contour modes:
201 * the outside contour, all subtree contours, or selected contours.
203 * ----------------------------------------------------------------------------
207 SetContours(ContourOption option)
209 if (option == NoContours) {
210 switch (TreeShowContourOption) {
212 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, FALSE);
215 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);
217 case SelectedContours:
218 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, TRUE, TRUE);
223 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);
225 else if (option == OutsideContour) {
226 switch (TreeShowContourOption) {
228 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);
230 case SelectedContours:
231 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, TRUE, TRUE);
236 DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, FALSE, FALSE);
237 } else if (option == AllContours) {
238 DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, FALSE, TRUE);
239 } else if (option == SelectedContours) {
240 switch (TreeShowContourOption) {
242 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);
245 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, FALSE);
248 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);
250 DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, TRUE, TRUE);
252 TreeShowContourOption = option;
256 /* ----------------------------------------------------------------------------
258 * HiliteNode() is called by Unzip() to change the color of a node.
260 * ----------------------------------------------------------------------------
264 HiliteNode(Tree *tree, PosMode pos_mode)
266 SetDrawColor(HIGHLIGHT_COLOR);
267 DrawNode(tree, pos_mode);
268 SetDrawColor(TREE_COLOR);
272 /* ----------------------------------------------------------------------------
274 * DrawNode() takes a node and draws the node in the specified widget
275 * at its (x,y) coordinate. (x, y) indicates the upper-left corner where
276 * the node is drawn. Also, a line is drawn from the center of the left
277 * edge to the center of the parent's right edge. 'draw_mode' specifies
278 * the drawing mode (whether or not the node is erased, rather than drawn).
279 * 'pos_mode' determines whether or not to use the old position of the node.
280 * This flag is used in animating the movement of a node from its old
281 * position to its new position.
283 * ----------------------------------------------------------------------------
287 DrawNode(Tree *node, PosMode pos_mode)
293 gc = TreeDrawingAreaDB->gc;
295 if (pos_mode == Old) {
296 XDrawString(XtDisplay(w), XtWindow(w), gc,
297 node->old_pos.x + node->label.xoffset,
298 node->old_pos.y + node->label.yoffset,
299 node->label.text, node->label.len);
300 XDrawRectangle(XtDisplay(w), XtWindow(w), gc,
301 node->old_pos.x, node->old_pos.y,
302 node->width, node->height);
304 XDrawLine(XtDisplay(w), XtWindow(w), gc,
306 node->old_pos.y + (node->height / 2),
307 node->parent->old_pos.x + node->parent->width + 1,
308 node->parent->old_pos.y + (node->parent->height / 2));
310 XSetFillStyle(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
312 XFillRectangle(XtDisplay(w), XtWindow(w), gc,
313 node->old_pos.x + node->width - ELISION_WIDTH,
314 node->old_pos.y + 1, ELISION_WIDTH, node->height - 1);
315 XSetFillStyle(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
319 XDrawString(XtDisplay(w), XtWindow(w), gc,
320 node->pos.x + node->label.xoffset,
321 node->pos.y + node->label.yoffset,
322 node->label.text, node->label.len);
324 XDrawRectangle(XtDisplay(w), XtWindow(w), gc,
325 node->pos.x, node->pos.y,
326 node->width, node->height);
328 XDrawLine(XtDisplay(w), XtWindow(w), gc,
330 node->pos.y + (node->height / 2),
331 node->parent->pos.x + node->parent->width + 1,
332 node->parent->pos.y + (node->parent->height / 2));
334 XSetFillStyle(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
336 XFillRectangle(XtDisplay(w), XtWindow(w), gc,
337 node->pos.x + node->width - ELISION_WIDTH,
338 node->pos.y + 1, ELISION_WIDTH, node->height - 1);
339 XSetFillStyle(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
346 /* ----------------------------------------------------------------------------
348 * DrawTreeContour() draws the contour of the specified subtree. Bridges
349 * are not traversed, so the actual subtree contour is drawn, as opposed
350 * to the merged contour. 'color' specifies the drawing color. If 'detach'
351 * is True, the lines attaching the subtree contour to the node are not
352 * drawn. If 'select' is true, then only subtrees that are flagged as
353 * selected are shown. If 'recursive' is True, the entire tree is traversed.
355 * ----------------------------------------------------------------------------
359 DrawTreeContour(Tree *tree, PosMode pos_mode,
360 int color, int detach_p, int select_p, int recursive)
362 Widget w = TreeDrawingArea;
363 Polyline *contour, *tail;
370 if ((select_p && tree->show_contour) || !select_p) {
373 SetLineWidth(TreeContourWidth);
375 /* draw upper contour */
376 contour = tree->contour.upper.head;
377 tail = tree->contour.upper.tail;
378 if (pos_mode == Old) {
379 x = tree->old_pos.x - tree->border;
380 y = tree->old_pos.y - tree->border;
383 x = tree->pos.x - tree->border;
384 y = tree->pos.y - tree->border;
387 if (detach_p) { /* skip over attaching lines */
388 for (i = 0 ; i < 2 ; i++) {
391 contour = contour->link;
396 XDrawLine(XtDisplay(w), XtWindow(w), TreeDrawingAreaDB->gc,
397 x, y, x + contour->dx, y + contour->dy);
400 if (contour == tail) /* make sure you don't follow bridges */
403 contour = contour->link;
406 /* draw lower contour */
407 contour = tree->contour.lower.head;
408 tail = tree->contour.lower.tail;
409 if (pos_mode == Old) {
410 x = tree->old_pos.x - tree->border;
411 y = tree->old_pos.y + tree->border + tree->height;
413 x = tree->pos.x - tree->border;
414 y = tree->pos.y + tree->border + tree->height;
417 if (detach_p) { /* skip over attaching lines */
418 for (i = 0 ; i < 2 ; i++) {
421 contour = contour->link;
426 XDrawLine(XtDisplay(w), XtWindow(w), TreeDrawingAreaDB->gc,
427 x, y, x + contour->dx, y + contour->dy);
430 if (contour == tail) /* make sure you don't follow bridges */
433 contour = contour->link;
438 FOREACH_CHILD(child, tree)
440 DrawTreeContour(child, pos_mode, color,
441 detach_p, select_p, recursive);
444 SetDrawColor(TREE_COLOR);
449 /* ----------------------------------------------------------------------------
451 * DrawTree() traverses the given tree, drawing the node and connecting
452 * segments. The tree contours are also drawn at each step, if enabled.
453 * 'draw_mode' specifies the drawing mode in which the tree is drawn.
454 * 'pos_mode' determines whether or not to use the old position of the node.
455 * This flag is used in animating the movement of a node from its old
456 * position to its new position. DrawNode() is called to draw an individual
459 * ----------------------------------------------------------------------------
463 DrawTree(Tree *tree, PosMode pos_mode)
468 DrawNode(tree, pos_mode);
470 /* do stuff that animates Unzip() */
472 if (!AnimationMode ||
473 (tree->pos.x == tree->old_pos.x &&
474 tree->pos.y == tree->old_pos.y))
475 DrawTreeContour(tree, pos_mode, SPLIT_COLOR, FALSE, FALSE, FALSE);
477 DrawTreeContour(tree, pos_mode, ACTION_COLOR, FALSE, FALSE, FALSE);
480 HiliteNode(tree, pos_mode);
482 if (tree->child && !tree->elision)
483 DrawTree(tree->child, pos_mode);
485 DrawTree(tree->sibling, pos_mode);
487 maybe_highlight_node(tree);
491 /* ----------------------------------------------------------------------------
493 * ShiftTree() adjusts the positions of each node so that it moves from
494 * the "old" position towards the "new position". This is used by
495 * AnimateTree(). 'done' is set to FALSE if the tree is not in its
496 * final position; it is used to determine when to stop animating the tree.
498 * ----------------------------------------------------------------------------
502 ShiftTree(Tree *tree, int *done)
506 if (tree->old_pos.x != tree->pos.x ||
507 tree->old_pos.y != tree->pos.y)
509 tree->old_pos.x = tree->pos.x;
510 tree->old_pos.y = tree->pos.y;
513 FOREACH_CHILD(child, tree)
514 ShiftTree(child, done);
518 /* ----------------------------------------------------------------------------
520 * AnimateTree() draws the given tree in a series of steps to give the
521 * effect of animation from the "old" layout to the "new" layout of the
524 * The algorithm used here is not efficient; the entire tree is drawn
525 * on each iteration of the animation sequence; it would be more efficient
526 * to only redraw what is necessary. However, the method used here takes
527 * advantage of existing code without modification.
529 * ----------------------------------------------------------------------------
533 AnimateTree(Tree *tree)
537 AnimationMode = FALSE;
538 /* highlight which nodes have to move */
544 AnimationStep = ANIMATION_STEP_STEP;
547 ShiftTree(tree, &done);
555 AnimationStep = ANIMATION_STEP;
556 AnimationMode = FALSE;
560 /* ----------------------------------------------------------------------------
562 * AnimateZip() generates a sequence of frames that animates the Zip() step.
563 * It is similar in logical structure to Zip().
565 * ----------------------------------------------------------------------------
569 AnimateZip(Tree *tree)
573 /* show results of Join() step */
576 FOREACH_CHILD(child, tree)
577 child->split = FALSE;
578 DrawTree(TheTree, New);
579 DrawTreeContour(tree, New, CONTOUR_COLOR, TRUE, FALSE, FALSE);
582 StatusMsg("Zip: merge and join contours", FALSE);
585 /* show results of AttachParent() step */
587 DrawTree(TheTree, New);
588 DrawTreeContour(tree, New, CONTOUR_COLOR, FALSE, FALSE, FALSE);
591 StatusMsg("Zip: attach parents", FALSE);
595 tree->on_path = FALSE;
598 AnimateZip(tree->parent);
600 tree->on_path = FALSE;
602 DrawTree(TheTree, New);
603 DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, FALSE, FALSE);
605 StatusMsg("Zip: reassemble entire contour", FALSE);
611 /* ----------------------------------------------------------------------------
613 * CountNodes() returns the number of nodes in the specified tree.
614 * Nodes below a node that has been collapsed are ignored.
616 * ----------------------------------------------------------------------------
620 CountNodes(Tree *tree)
622 int num_nodes = 1; /* count root of subtree */
625 if (!tree->elision) {
626 FOREACH_CHILD(child, tree)
627 num_nodes += CountNodes(child);
633 /* ----------------------------------------------------------------------------
635 * CollectNodeRectangles() is a recursive function used by
636 * GetSubTreeRectangles() to collect the rectangles of descendant nodes
637 * into the pre-allocated storage passed to this function.
639 * ----------------------------------------------------------------------------
643 CollectNodeRectangles(Tree *node, XRectangle **rectangles, int fill)
647 (*rectangles)->x = node->pos.x;
648 (*rectangles)->y = node->pos.y;
650 (*rectangles)->width = node->width + 1;
651 (*rectangles)->height = node->height + 1;
653 (*rectangles)->width = node->width;
654 (*rectangles)->height = node->height;
659 FOREACH_CHILD(child, node)
660 CollectNodeRectangles(child, rectangles, fill);
664 /* ----------------------------------------------------------------------------
666 * GetSubTreeRectangles() builds an array of XRectangles that contain
667 * all the node rectangles in the tree, except the root node itself.
668 * The array is returned in 'rectangles' and the number of rectangles
669 * is returned in 'nrectangles.' Storage for the rectangles is allocated
670 * in this function. This function is used by PickAction to determine
671 * what rectangles need to be dissolved away. 'fill', if True, specifies
672 * that the rectangles should be 1 pixel larger in each dimension to
673 * compensate for FillRectangle behavior.
675 * ----------------------------------------------------------------------------
679 GetSubTreeRectangles(Tree *tree, XRectangle **rectangles,
680 int *nrectangles, int fill)
683 XRectangle *crect; /* current rectangle */
685 *nrectangles = CountNodes(tree) - 1; /* don't count root node */
686 *rectangles = (XRectangle *) malloc(sizeof(XRectangle) * *nrectangles);
687 ASSERT(*rectangles, "could not allocate memory for rectangles");
691 FOREACH_CHILD(child, tree)
692 CollectNodeRectangles(child, &crect, fill);
696 /* ----------------------------------------------------------------------------
698 * CollectNodeSegments() is a recursive function used by GetSubTreeSegments()
699 * to collect the line segments connecting nodes into the pre-allocated
700 * storage passed to this function.
702 * ----------------------------------------------------------------------------
706 CollectNodeSegments(Tree *node, XSegment **segments)
710 (*segments)->x1 = node->pos.x - 1;
711 (*segments)->y1 = node->pos.y + (node->height / 2),
712 (*segments)->x2 = node->parent->pos.x + node->parent->width + 1;
713 (*segments)->y2 = node->parent->pos.y + (node->parent->height / 2);
717 FOREACH_CHILD(child, node)
718 CollectNodeSegments(child, segments);
722 /* ----------------------------------------------------------------------------
724 * GetSubTreeSegments() builds an array of XSegments that contain
725 * all the line segments connecting the nodes in the tree. The array is
726 * returned in 'segments' and the number of segments is returned in
727 * 'nsegments.' Storage for the segments is allocated in this function.
728 * This function is used by PickAction to determine what line segments
729 * rectangles need to be dissolved away.
731 * ----------------------------------------------------------------------------
735 GetSubTreeSegments(Tree *tree, XSegment **segments, int *nsegments)
738 XSegment *cseg; /* current segment */
740 *nsegments = CountNodes(tree) - 1;
741 *segments = (XSegment *) malloc(sizeof(XSegment) * *nsegments);
742 ASSERT(*segments, "could not allocate memory for segments");
746 FOREACH_CHILD(child, tree)
747 CollectNodeSegments(child, &cseg);
751 /* ----------------------------------------------------------------------------
753 * ComputeSubTreeExtent() computes the extent of a subtree. This is
754 * easily computed based on the tree's contour, as in ComputeTreeSize().
755 * This extent is stored in the node, and used by SearchTree for
758 * This function assumes that the given tree has at least one child; do not
759 * pass a leaf node to this function.
761 * ----------------------------------------------------------------------------
765 ComputeSubTreeExtent(Tree *tree)
768 int x_offset, y_offset;
770 ComputeTreeSize(tree, &width, &height, &x_offset, &y_offset);
771 tree->subextent.pos.x = tree->child->pos.x - tree->child->border;
772 tree->subextent.pos.y = tree->pos.y - y_offset;
773 tree->subextent.width = width - (tree->child->pos.x - tree->pos.x) - 1;
774 tree->subextent.height = height - 1;
778 /* ----------------------------------------------------------------------------
780 * SearchTree() determines if a node's rectangular region encloses the
781 * specified point in (x,y). Rather than using a brute-force search
782 * through all node rectangles of a given tree, the subtree extents
783 * are used in a recursive fashion to drive the search as long as the
784 * given point is enclosed in an extent. In the worst case, the search
785 * time would be on the order of a brute-force search, but with complex
786 * trees, this method reduces the number of visits.
788 * The extent of a subtree is computed by ComputeSubTreeExtent() and is
789 * stored in each node of the tree.
791 * ----------------------------------------------------------------------------
795 SearchTree(Tree *tree, int x, int y, Tree **node)
802 if (PT_IN_RECT(x, y, tree->pos.x, tree->pos.y,
803 tree->pos.x + tree->width,
804 tree->pos.y + tree->height)) {
808 if (tree->child && (PT_IN_EXTENT(x, y, tree->subextent)))
809 FOREACH_CHILD(child, tree) {
810 if (SearchTree(child, x, y, node))
817 /* ----------------------------------------------------------------------------
819 * ExposeHandler() handles expose events in the TreeDrawingArea. This
820 * function is not intelligent; it just redraws the entire contents.
822 * ----------------------------------------------------------------------------
826 ExposeHandler(Widget w, XtPointer closure,
827 XEvent *event, Boolean *continue_to_dispatch)
829 if (event->xexpose.count == 0) {
831 SetContours(TreeShowContourOption);
832 DrawTree(TheTree, New);
838 /* ----------------------------------------------------------------------------
840 * ExpandCollapseNode is called to expand or collapse a node in the tree.
842 * ----------------------------------------------------------------------------
846 ExpandCollapseNode(Tree *node)
849 int old_width, old_height;
850 int x_offset, y_offset;
851 XRectangle *rectangles;
853 int nrectangles, nsegments;
855 Widget w = TreeDrawingArea;
859 /* hilite node so that we know where we are */
860 /* DrawTree will hilite it as a side effect */
862 node->on_path = TRUE;
864 /* erase the contour before changing in the tree */
865 if ((TreeShowContourOption != NoContours) || TreeShowSteps) {
867 DrawTree(TheTree, New);
871 sprintf(strbuf, "Node `%s' selected for %s", node->label.text,
872 node->elision ? "expansion" : "collapse");
873 StatusMsg(strbuf, FALSE);
879 StatusMsg("Show entire contour", FALSE);
882 DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, FALSE, FALSE);
883 DrawTree(TheTree, New);
889 /* are we collapsing a subtree? */
890 if (!node->elision) {
891 StatusMsg("Collapse subtree", FALSE);
892 GetSubTreeRectangles(node, &rectangles, &nrectangles, TRUE);
893 GetSubTreeSegments(node, &segments, &nsegments);
894 DissolveTree(XtDisplay(w), XtWindow(w),
895 rectangles, nrectangles,
896 segments, nsegments, TRUE);
901 StatusMsg("Replace subtree contour with leaf contour", FALSE);
902 node->elision = TRUE;
904 node->split = TRUE; /* turned off in AnimateZip */
905 node->old_contour = node->contour;
906 node->width += ELISION_WIDTH;
909 SetContours(TreeShowContourOption);
910 DrawTree(TheTree, New);
914 StatusMsg("Replace leaf contour with old subtree contour", FALSE);
916 node->split = TRUE; /* turned off in AnimateZip */
918 node->contour = node->old_contour;
925 ComputeTreeSize(TheTree, &width, &height, &x_offset, &y_offset);
926 PetrifyTree(TheTree, x_offset + MAT_SIZE, y_offset + MAT_SIZE);
927 GetDrawingSize(&old_width, &old_height);
930 SetDrawingSize(width + (2 * MAT_SIZE), height + (2 * MAT_SIZE));
932 DrawTree(TheTree, Old);
935 StatusMsg("Move tree to new configuration", FALSE);
936 AnimateTree(TheTree);
938 /* we are shrinking or staying the same */
939 StatusMsg("Move tree to new configuration", FALSE);
940 AnimateTree(TheTree);
941 SetDrawingSize(width + (2 * MAT_SIZE), height + (2 * MAT_SIZE));
945 StatusMsg("Expand subtree", FALSE);
946 node->elision = FALSE;
948 /* erase elision marker */
949 XSetFunction(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
951 XFillRectangle(XtDisplay(w), XtWindow(w), TreeDrawingAreaDB->gc,
952 node->pos.x + node->width - ELISION_WIDTH + 1,
953 node->pos.y, ELISION_WIDTH, node->height + 1);
954 XSetFunction(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
956 node->width -= ELISION_WIDTH;
958 GetSubTreeRectangles(node, &rectangles, &nrectangles, FALSE);
959 GetSubTreeSegments(node, &segments, &nsegments);
960 /* dissolve the tree back in */
961 DissolveTree(XtDisplay(w), XtWindow(w),
962 rectangles, nrectangles,
963 segments, nsegments, FALSE);
967 /* draw text of nodes */
969 SetContours(TreeShowContourOption);
970 DrawTree(TheTree, New);
976 node->on_path = FALSE;
978 AnimateZip(node->parent);
983 /* BUG: the display isn't properly updated here! */
984 /* There should probably be some code here that
985 clears the tree below the node currently being
986 collapsed or expanded. Hack added 20.03.95 (torgeir@ii.uib.no).
987 I'll try to fix this later. */
989 XClearArea(TreeDisplay, XtWindow(TreeDrawingArea), 0, 0, 0, 0, FALSE);
992 SetContours(TreeShowContourOption);
993 DrawTree(TheTree, New);
996 StatusMsg("Ready", TRUE);
999 /* ----------------------------------------------------------------------------
1001 * InsertNode() handles the task of inserting a new node in the tree,
1002 * at the given position with respect to 'base_node'. When 'node_pos' is
1003 * either Before or After, it is assumed that 'base_node' has a parent.
1005 * ----------------------------------------------------------------------------
1009 InsertNode(Tree *base_node, NodePosition node_pos, char *new_node_text)
1013 Tree *sibling = NULL;
1017 int x_offset, y_offset;
1019 StatusMsg("", TRUE);
1021 new_node = MakeNode(); /* should check for memory failure */
1022 SetNodeLabel(new_node, new_node_text);
1023 LayoutLeaf(new_node);
1025 /* figure out parent & sibling */
1026 if (node_pos == Child) {
1028 /* find last child, if one exists */
1029 FOREACH_CHILD(child, parent)
1031 } else if (node_pos == After) {
1032 parent = base_node->parent;
1033 sibling = base_node;
1034 } else if (node_pos == Before) {
1035 parent = base_node->parent;
1036 FOREACH_CHILD(child, parent)
1037 if (child->sibling == base_node) {
1047 parent->on_path = TRUE;
1049 if ((TreeShowContourOption != NoContours) ||
1052 DrawTree(TheTree, New);
1056 sprintf(strbuf, "Inserting `%s' as child of node `%s'",
1057 new_node_text, parent->label.text);
1058 StatusMsg(strbuf, FALSE);
1061 /* erase the contour before changing in the tree */
1063 Insert(parent, new_node, sibling);
1065 ComputeTreeSize(TheTree, &width, &height, &x_offset, &y_offset);
1066 PetrifyTree(TheTree, x_offset + MAT_SIZE, y_offset + MAT_SIZE);
1069 new_node->old_pos = sibling->old_pos;
1070 else if (new_node->sibling)
1071 new_node->old_pos = new_node->sibling->old_pos;
1073 new_node->old_pos.x = new_node->pos.x;
1074 new_node->old_pos.y = parent->old_pos.y;
1078 new_node->split = TRUE;
1080 SetDrawingSize(width + (2 * MAT_SIZE), height + (2 * MAT_SIZE));
1082 DrawTree(TheTree, Old);
1084 StatusMsg("Insert: add new node and contour", FALSE);
1087 StatusMsg("Move tree to new configuration", FALSE);
1088 AnimateTree(TheTree);
1090 if (TreeShowSteps) {
1096 SetContours(TreeShowContourOption);
1097 DrawTree(TheTree, New);
1100 StatusMsg("Ready", TRUE);
1103 /* ----------------------------------------------------------------------------
1105 * DeleteNode() handles the task of deleting a given node in the tree.
1107 * ----------------------------------------------------------------------------
1111 DeleteNode(Tree *node)
1115 XRectangle *rectangles;
1117 int nrectangles, nsegments;
1118 Widget w = TreeDrawingArea;
1120 int x_offset, y_offset;
1122 StatusMsg("", TRUE);
1125 node->on_path = TRUE;
1127 /* erase the contour before changing in the tree */
1128 if ((TreeShowContourOption != NoContours) ||
1131 DrawTree(TheTree, New);
1135 sprintf(strbuf, "Node `%s' selected for deletion", node->label.text);
1136 StatusMsg(strbuf, FALSE);
1139 parent = node->parent;
1144 TheTree = NULL; /* delete root of tree */
1146 /* fade out deleted subtree */
1147 StatusMsg("Delete subtree", FALSE);
1148 GetSubTreeRectangles(node, &rectangles, &nrectangles, TRUE);
1149 GetSubTreeSegments(node, &segments, &nsegments);
1150 DissolveTree(XtDisplay(w), XtWindow(w),
1151 rectangles, nrectangles,
1152 segments, nsegments, TRUE);
1160 DrawTree(TheTree, New);
1168 ComputeTreeSize(TheTree, &width, &height, &x_offset, &y_offset);
1169 PetrifyTree(TheTree, x_offset + MAT_SIZE, y_offset + MAT_SIZE);
1170 StatusMsg("Move tree to new configuration", FALSE);
1171 AnimateTree(TheTree);
1172 SetDrawingSize(width + (2 * MAT_SIZE), height + (2 * MAT_SIZE));
1175 if (TreeShowSteps) {
1181 SetContours(TreeShowContourOption);
1182 DrawTree(TheTree, New);
1187 StatusMsg("Ready", TRUE);
1191 /* ----------------------------------------------------------------------------
1193 * ResetLabels() is called when the TreeAlignNodes mode is changed.
1194 * When TreeParentDistance changes, the node width changes, so this
1195 * function forces each node's width to be recomputed.
1197 * ----------------------------------------------------------------------------
1201 ResetLabels(Tree *tree)
1205 SetNodeLabel(tree, tree->label.text);
1206 FOREACH_CHILD(child, tree)
1211 /* ----------------------------------------------------------------------------
1213 * SetupTree() handles the task of setting up the specified tree in
1216 * ----------------------------------------------------------------------------
1220 SetupTree(Tree *tree)
1223 int x_offset, y_offset;
1226 ComputeTreeSize(tree, &width, &height, &x_offset, &y_offset);
1227 PetrifyTree(tree, x_offset + MAT_SIZE, y_offset + MAT_SIZE);
1228 SetDrawingTree(tree);
1229 SetDrawingSize(width + (2 * MAT_SIZE), height + (2 * MAT_SIZE));
1231 SetContours(TreeShowContourOption);
1232 DrawTree(tree, New);