1 /* ----------------------------------------------------------------------------
\r
3 * Purpose : drawing-specific routines for dynamic tree program
\r
4 * ----------------------------------------------------------------------------
\r
10 #include <X11/Intrinsic.h>
\r
11 #include <X11/StringDefs.h>
\r
20 /* ------------------------------------------------------------------------- */
\r
21 /* Global Variables */
\r
22 /* ------------------------------------------------------------------------- */
\r
26 /* ------------------------------------------------------------------------- */
\r
27 /* Local Variables */
\r
28 /* ------------------------------------------------------------------------- */
\r
30 static char AnimationMode = FALSE;
\r
31 static char strbuf[BUFSIZ];
\r
32 static int AnimationStep = ANIMATION_STEP;
\r
35 static int DrawingWidth;
\r
36 static int DrawingHeight;
\r
39 /* ------------------------------------------------------------------------- */
\r
40 /* Forward Function Declarations */
\r
41 /* ------------------------------------------------------------------------- */
\r
44 void DrawTreeContour();
\r
47 /* ------------------------------------------------------------------------- */
\r
49 /* ------------------------------------------------------------------------- */
\r
53 SelectNewDeleteOld (HGDIOBJ obj)
\r
55 obj = SelectObject (TreeDrawingAreaDB->hdc, obj);
\r
62 HPEN pen = CreatePen (PS_SOLID, TreeDrawingAreaDB->LineWidth,
\r
63 GetTextColor (TreeDrawingAreaDB->hdc));
\r
64 SelectNewDeleteOld (pen);
\r
67 BOOL SetNewFont (LPLOGFONT lplf)
\r
69 HFONT font = CreateFontIndirect (lplf);
\r
71 SelectNewDeleteOld (font);
\r
72 return font != NULL;
\r
76 /* ----------------------------------------------------------------------------
\r
78 * BeginFrame() provides an abstraction for double buffering. It should
\r
79 * be called prior to creating a new frame of animation.
\r
81 * ----------------------------------------------------------------------------
\r
88 DBLbegin_frame(TreeDrawingAreaDB);
\r
93 /* ----------------------------------------------------------------------------
\r
95 * EndFrame() provides an abstraction for double buffering. It should
\r
96 * be called after creating a new frame of animation.
\r
98 * ----------------------------------------------------------------------------
\r
105 /* Changed second parameter from 0 to 1 at Torgeir Veimo's suggestion
\r
106 on 9/21/1997. -- Bob Weiner, BeOpen.com */
\r
107 DBLend_frame(TreeDrawingAreaDB, 1);
\r
112 /* ----------------------------------------------------------------------------
\r
114 * GetDrawingSize() gets the size of the drawing area, and returns the
\r
115 * dimensions in the arguments.
\r
117 * ----------------------------------------------------------------------------
\r
121 GetDrawingSize(width, height)
\r
122 int *width, *height;
\r
125 *width = TreeDrawingAreaDB->DrawingWidth;
\r
126 *height = TreeDrawingAreaDB->DrawingHeight;
\r
130 XtVaGetValues(TreeDrawingArea,
\r
141 /* ----------------------------------------------------------------------------
\r
143 * SetDrawingSize() sets the size of the drawing area to the given
\r
146 * ----------------------------------------------------------------------------
\r
150 SetDrawingSize(width, height)
\r
158 TreeDrawingAreaDB->DrawingWidth = width;
\r
159 TreeDrawingAreaDB->DrawingHeight = height;
\r
161 /* Setup scrollbars */
\r
162 GetClientRect (hwndClient, &rc);
\r
163 GetWindowOrgEx (TreeDrawingAreaDB->hdc, &pt);
\r
164 si.cbSize = sizeof (si);
\r
165 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
\r
170 si.nPage = rc.bottom;
\r
172 SetScrollInfo (hwndClient, SB_VERT, &si, TRUE);
\r
174 /* Horizontal bar */
\r
177 si.nPage = rc.right;
\r
179 SetScrollInfo (hwndClient, SB_HORZ, &si, TRUE);
\r
181 XtVaSetValues(TreeDrawingArea,
\r
182 XtNwidth, (Dimension) width,
\r
183 XtNheight, (Dimension) height,
\r
189 /* ----------------------------------------------------------------------------
\r
191 * SetDrawingTree() is used to specify what tree is to be drawn in the
\r
194 * ----------------------------------------------------------------------------
\r
198 SetDrawingTree(tree)
\r
205 /* ----------------------------------------------------------------------------
\r
207 * SetNodeLabel() sets the label text of the specified node and computes
\r
208 * the bounding rectangle so that the layout can be determined. This
\r
209 * function is called when new nodes are created. If TreeAlignNodes is
\r
210 * True, the string is truncated so that the node's width is no longer
\r
211 * than TreeParentDistance.
\r
213 * ----------------------------------------------------------------------------
\r
217 SetNodeLabel(node, label)
\r
229 len = strlen(label);
\r
232 GetTextExtentPoint32 (TreeDrawingAreaDB->hdc, label, len, &sz);
\r
233 node->width = sz.cx + (LABEL_MAT_WIDTH * 2) + 1;
\r
234 node->height = sz.cy + (LABEL_MAT_HEIGHT * 2) + 1;
\r
236 XTextExtents(TreeLabelFont, label, len, &dummy, &dummy, &dummy, &rtrn);
\r
237 node->width = rtrn.lbearing + rtrn.rbearing + (LABEL_MAT_WIDTH * 2) + 1;
\r
238 node->height = rtrn.ascent + rtrn.descent + (LABEL_MAT_HEIGHT * 2) + 1;
\r
240 if (TreeAlignNodes) {
\r
241 if (node->width <= (2 * TreeParentDistance))
\r
250 node->label.text = label;
\r
251 node->label.len = len;
\r
252 node->label.xoffset = LABEL_MAT_WIDTH + 1,
\r
254 node->label.yoffset = LABEL_MAT_HEIGHT + 1;
\r
256 node->label.yoffset = rtrn.ascent + LABEL_MAT_HEIGHT + 1;
\r
261 /* ----------------------------------------------------------------------------
\r
263 * SetDrawColor() sets the drawing color of the TreeDrawingArea.
\r
265 * ----------------------------------------------------------------------------
\r
269 SetDrawColor(color)
\r
273 SetTextColor (TreeDrawingAreaDB->hdc, TreeDrawingAreaDB->colors[color]);
\r
275 XSetForeground(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
\r
276 TreeDrawingAreaDB->colors[color]);
\r
280 /* ----------------------------------------------------------------------------
\r
282 * SetLineWidth() sets the line width of lines drawn in the TreeDrawingArea.
\r
284 * ----------------------------------------------------------------------------
\r
288 SetLineWidth(width, color)
\r
289 unsigned int width;
\r
293 TreeDrawingAreaDB->LineWidth = width;
\r
295 XSetLineAttributes(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
\r
296 width, LineSolid, CapButt, JoinRound);
\r
301 /* ----------------------------------------------------------------------------
\r
303 * SetContours() sets the visibility of three possible contour modes:
\r
304 * the outside contour, all subtree contours, or selected contours.
\r
306 * ----------------------------------------------------------------------------
\r
310 SetContours(option)
\r
311 ContourOption option;
\r
313 if (option == NoContours) {
\r
314 switch (TreeShowContourOption) {
\r
315 case OutsideContour:
\r
316 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, FALSE);
\r
319 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);
\r
321 case SelectedContours:
\r
322 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, TRUE, TRUE);
\r
327 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);
\r
329 else if (option == OutsideContour) {
\r
330 switch (TreeShowContourOption) {
\r
332 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);
\r
334 case SelectedContours:
\r
335 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, TRUE, TRUE);
\r
340 DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, FALSE, FALSE);
\r
341 } else if (option == AllContours) {
\r
342 DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, FALSE, TRUE);
\r
343 } else if (option == SelectedContours) {
\r
344 switch (TreeShowContourOption) {
\r
346 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);
\r
348 case OutsideContour:
\r
349 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, FALSE);
\r
352 DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);
\r
354 DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, TRUE, TRUE);
\r
356 TreeShowContourOption = option;
\r
360 /* ----------------------------------------------------------------------------
\r
362 * HiliteNode() is called by Unzip() to change the color of a node.
\r
364 * ----------------------------------------------------------------------------
\r
368 HiliteNode(tree, pos_mode)
\r
371 SetDrawColor(HIGHLIGHT_COLOR);
\r
372 DrawNode(tree, pos_mode);
\r
373 SetDrawColor(TREE_COLOR);
\r
377 /* ----------------------------------------------------------------------------
\r
379 * DrawNode() takes a node and draws the node in the specified widget
\r
380 * at its (x,y) coordinate. (x, y) indicates the upper-left corner where
\r
381 * the node is drawn. Also, a line is drawn from the center of the left
\r
382 * edge to the center of the parent's right edge. 'draw_mode' specifies
\r
383 * the drawing mode (whether or not the node is erased, rather than drawn).
\r
384 * 'pos_mode' determines whether or not to use the old position of the node.
\r
385 * This flag is used in animating the movement of a node from its old
\r
386 * position to its new position.
\r
388 * ----------------------------------------------------------------------------
\r
392 DrawNodeHelper (node, pos, parent_pos)
\r
398 HDC hdc = TreeDrawingAreaDB->hdc;
\r
401 rcNode.left = pos->x;
\r
402 rcNode.top = pos->y;
\r
403 rcNode.right = pos->x + node->width;
\r
404 rcNode.bottom = pos->y + node->height;
\r
406 if (node->highlight)
\r
408 SetDrawColor(HIGHLIGHT_COLOR);
\r
409 SetBkColor (hdc, TreeDrawingAreaDB->colors [HIGHLIGHT_BK_COLOR]);
\r
413 SetDrawColor(TREE_COLOR);
\r
414 SetBkColor (hdc, TreeDrawingAreaDB->colors [BACKGROUND_COLOR]);
\r
418 pos->x + node->label.xoffset,
\r
419 pos->y + node->label.yoffset,
\r
420 ETO_OPAQUE, &rcNode,
\r
421 node->label.text, node->label.len, NULL);
\r
426 pt[0].x = pos->x + node->width - ELISION_WIDTH;
\r
427 pt[0].y = pos->y + 2;
\r
428 pt[1].x = pos->x + node->width - 2;
\r
429 pt[1].y = pos->y + node->height / 2;
\r
430 pt[2].x = pos->x + node->width - ELISION_WIDTH;
\r
431 pt[2].y = pos->y + node->height - 2;
\r
433 SelectNewDeleteOld (CreateSolidBrush (GetTextColor (hdc)));
\r
434 Polygon (hdc, pt, 3);
\r
437 SetDrawColor(TREE_COLOR);
\r
440 if (!node->highlight)
\r
442 SelectNewDeleteOld (GetStockObject (HOLLOW_BRUSH));
\r
443 Rectangle (hdc, rcNode.left, rcNode.top, rcNode.right, rcNode.bottom);
\r
447 DrawFocusRect (hdc, &rcNode);
\r
451 MoveToEx (hdc, pos->x - 1, pos->y + (node->height / 2), NULL);
\r
452 LineTo (hdc, parent_pos->x + node->parent->width + 1,
\r
453 parent_pos->y + (node->parent->height / 2));
\r
459 w = TreeDrawingArea;
\r
460 gc = TreeDrawingAreaDB->gc;
\r
462 XDrawString(XtDisplay(w), XtWindow(w), gc,
\r
463 pos->x + node->label.xoffset,
\r
464 pos->y + node->label.yoffset,
\r
465 node->label.text, node->label.len);
\r
466 XDrawRectangle(XtDisplay(w), XtWindow(w), gc,
\r
468 node->width, node->height);
\r
470 XDrawLine(XtDisplay(w), XtWindow(w), gc,
\r
472 pos->y + (node->height / 2),
\r
473 parent_pos->x + node->parent->width + 1,
\r
474 parent_pos->y + (node->parent->height / 2));
\r
475 if (node->elision) {
\r
476 XSetFillStyle(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
\r
478 XFillRectangle(XtDisplay(w), XtWindow(w), gc,
\r
479 pos->x + node->width - ELISION_WIDTH,
\r
480 pos->y + 1, ELISION_WIDTH, node->height - 1);
\r
481 XSetFillStyle(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
\r
488 DrawNode(node, pos_mode)
\r
492 if (pos_mode == Old)
\r
493 DrawNodeHelper (node, &node->old_pos, node->parent ? &node->parent->old_pos : NULL);
\r
495 DrawNodeHelper (node, &node->pos, node->parent ? &node->parent->pos : NULL);
\r
499 /* ----------------------------------------------------------------------------
\r
501 * DrawTreeContour() draws the contour of the specified subtree. Bridges
\r
502 * are not traversed, so the actual subtree contour is drawn, as opposed
\r
503 * to the merged contour. 'color' specifies the drawing color. If 'detach'
\r
504 * is True, the lines attaching the subtree contour to the node are not
\r
505 * drawn. If 'select' is true, then only subtrees that are flagged as
\r
506 * selected are shown. If 'recursive' is True, the entire tree is traversed.
\r
508 * ----------------------------------------------------------------------------
\r
512 DrawTreeContour(tree, pos_mode, color, detach, select, recursive)
\r
521 Widget w = TreeDrawingArea;
\r
523 TreePolyline *contour, *tail;
\r
530 if ((select && tree->show_contour) || !select) {
\r
532 SetDrawColor(color);
\r
533 SetLineWidth(TreeContourWidth, color);
\r
535 /* draw upper contour */
\r
536 contour = tree->contour.upper.head;
\r
537 tail = tree->contour.upper.tail;
\r
538 if (pos_mode == Old) {
\r
539 x = tree->old_pos.x - tree->border;
\r
540 y = tree->old_pos.y - tree->border;
\r
543 x = tree->pos.x - tree->border;
\r
544 y = tree->pos.y - tree->border;
\r
547 if (detach) { /* skip over attaching lines */
\r
548 for (i = 0 ; i < 2 ; i++) {
\r
551 contour = contour->link;
\r
557 MoveToEx (TreeDrawingAreaDB->hdc, x, y, NULL);
\r
563 LineTo (TreeDrawingAreaDB->hdc, x, y);
\r
565 XDrawLine(XtDisplay(w), XtWindow(w), TreeDrawingAreaDB->gc,
\r
566 x - contour->dx, y - contour->dy, x, y);
\r
568 if (contour == tail) /* make sure you don't follow bridges */
\r
571 contour = contour->link;
\r
574 /* draw lower contour */
\r
575 contour = tree->contour.lower.head;
\r
576 tail = tree->contour.lower.tail;
\r
577 if (pos_mode == Old) {
\r
578 x = tree->old_pos.x - tree->border;
\r
579 y = tree->old_pos.y + tree->border + tree->height;
\r
581 x = tree->pos.x - tree->border;
\r
582 y = tree->pos.y + tree->border + tree->height;
\r
585 if (detach) { /* skip over attaching lines */
\r
586 for (i = 0 ; i < 2 ; i++) {
\r
589 contour = contour->link;
\r
594 MoveToEx (TreeDrawingAreaDB->hdc, x, y, NULL);
\r
600 LineTo (TreeDrawingAreaDB->hdc, x, y);
\r
602 XDrawLine(XtDisplay(w), XtWindow(w), TreeDrawingAreaDB->gc,
\r
603 x - contour->dx, y - contour->dy, x, y);
\r
605 if (contour == tail) /* make sure you don't follow bridges */
\r
608 contour = contour->link;
\r
613 FOREACH_CHILD(child, tree)
\r
614 if (!child->elision)
\r
615 DrawTreeContour(child, pos_mode, color,
\r
616 detach, select, recursive);
\r
619 SetDrawColor(TREE_COLOR);
\r
620 SetLineWidth(0, TREE_COLOR);
\r
624 /* ----------------------------------------------------------------------------
\r
626 * DrawTree() traverses the given tree, drawing the node and connecting
\r
627 * segments. The tree contours are also drawn at each step, if enabled.
\r
628 * 'draw_mode' specifies the drawing mode in which the tree is drawn.
\r
629 * 'pos_mode' determines whether or not to use the old position of the node.
\r
630 * This flag is used in animating the movement of a node from its old
\r
631 * position to its new position. DrawNode() is called to draw an individual
\r
634 * ----------------------------------------------------------------------------
\r
638 DrawTree(tree, pos_mode)
\r
645 DrawNode(tree, pos_mode);
\r
647 /* do stuff that animates Unzip() */
\r
649 if (!AnimationMode ||
\r
650 (tree->pos.x == tree->old_pos.x &&
\r
651 tree->pos.y == tree->old_pos.y))
\r
652 DrawTreeContour(tree, pos_mode, SPLIT_COLOR, FALSE, FALSE, FALSE);
\r
654 DrawTreeContour(tree, pos_mode, ACTION_COLOR, FALSE, FALSE, FALSE);
\r
657 HiliteNode(tree, pos_mode);
\r
659 if (tree->child && !tree->elision)
\r
660 DrawTree(tree->child, pos_mode);
\r
662 DrawTree(tree->sibling, pos_mode);
\r
666 /* ----------------------------------------------------------------------------
\r
668 * ShiftTree() adjusts the positions of each node so that it moves from
\r
669 * the "old" position towards the "new position". This is used by
\r
670 * AnimateTree(). 'done' is set to FALSE if the tree is not in its
\r
671 * final position; it is used to determine when to stop animating the tree.
\r
673 * ----------------------------------------------------------------------------
\r
677 ShiftTree(tree, done)
\r
683 if (tree->old_pos.x != tree->pos.x ||
\r
684 tree->old_pos.y != tree->pos.y)
\r
686 tree->old_pos.x = tree->pos.x;
\r
687 tree->old_pos.y = tree->pos.y;
\r
690 FOREACH_CHILD(child, tree)
\r
691 ShiftTree(child, done);
\r
695 /* ----------------------------------------------------------------------------
\r
697 * AnimateTree() draws the given tree in a series of steps to give the
\r
698 * effect of animation from the "old" layout to the "new" layout of the
\r
701 * The algorithm used here is not efficient; the entire tree is drawn
\r
702 * on each iteration of the animation sequence; it would be more efficient
\r
703 * to only redraw what is necessary. However, the method used here takes
\r
704 * advantage of existing code without modification.
\r
706 * ----------------------------------------------------------------------------
\r
715 AnimationMode = FALSE;
\r
716 /* highlight which nodes have to move */
\r
718 DrawTree(tree, Old);
\r
721 if (PauseAfterStep)
\r
722 AnimationStep = ANIMATION_STEP_STEP;
\r
725 ShiftTree(tree, &done);
\r
727 DrawTree(tree, Old);
\r
729 if (PauseAfterStep)
\r
732 if (PauseAfterStep)
\r
733 AnimationStep = ANIMATION_STEP;
\r
734 AnimationMode = FALSE;
\r
738 /* ----------------------------------------------------------------------------
\r
740 * AnimateZip() generates a sequence of frames that animates the Zip() step.
\r
741 * It is similar in logical structure to Zip().
\r
743 * ----------------------------------------------------------------------------
\r
752 /* show results of Join() step */
\r
755 FOREACH_CHILD(child, tree)
\r
756 child->split = FALSE;
\r
757 DrawTree(TheTree, New);
\r
758 DrawTreeContour(tree, New, CONTOUR_COLOR, TRUE, FALSE, FALSE);
\r
761 StatusMsg("Zip: merge and join contours", FALSE);
\r
764 /* show results of AttachParent() step */
\r
766 DrawTree(TheTree, New);
\r
767 DrawTreeContour(tree, New, CONTOUR_COLOR, FALSE, FALSE, FALSE);
\r
770 StatusMsg("Zip: attach parents", FALSE);
\r
774 tree->on_path = FALSE;
\r
777 AnimateZip(tree->parent);
\r
779 tree->on_path = FALSE;
\r
781 DrawTree(TheTree, New);
\r
782 DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, FALSE, FALSE);
\r
784 StatusMsg("Zip: reassemble entire contour", FALSE);
\r
790 /* ----------------------------------------------------------------------------
\r
792 * CountNodes() returns the number of nodes in the specified tree.
\r
793 * Nodes below a node that has been collapsed are ignored.
\r
795 * ----------------------------------------------------------------------------
\r
802 int num_nodes = 1; /* count root of subtree */
\r
805 if (!tree->elision) {
\r
806 FOREACH_CHILD(child, tree)
\r
807 num_nodes += CountNodes(child);
\r
809 return (num_nodes);
\r
813 /* ----------------------------------------------------------------------------
\r
815 * CollectNodeRectangles() is a recursive function used by
\r
816 * GetSubTreeRectangles() to collect the rectangles of descendant nodes
\r
817 * into the pre-allocated storage passed to this function.
\r
819 * ----------------------------------------------------------------------------
\r
823 CollectNodeRectangles(node, rectangles, fill)
\r
825 XRectangle **rectangles;
\r
830 (*rectangles)->x = node->pos.x;
\r
831 (*rectangles)->y = node->pos.y;
\r
833 (*rectangles)->width = node->width + 1;
\r
834 (*rectangles)->height = node->height + 1;
\r
836 (*rectangles)->width = node->width;
\r
837 (*rectangles)->height = node->height;
\r
841 if (!node->elision)
\r
842 FOREACH_CHILD(child, node)
\r
843 CollectNodeRectangles(child, rectangles, fill);
\r
847 /* ----------------------------------------------------------------------------
\r
849 * GetSubTreeRectangles() builds an array of XRectangles that contain
\r
850 * all the node rectangles in the tree, except the root node itself.
\r
851 * The array is returned in 'rectangles' and the number of rectangles
\r
852 * is returned in 'nrectangles.' Storage for the rectangles is allocated
\r
853 * in this function. This function is used by PickAction to determine
\r
854 * what rectangles need to be dissolved away. 'fill', if True, specifies
\r
855 * that the rectangles should be 1 pixel larger in each dimension to
\r
856 * compensate for FillRectangle behavior.
\r
858 * ----------------------------------------------------------------------------
\r
862 GetSubTreeRectangles(tree, rectangles, nrectangles, fill)
\r
864 XRectangle **rectangles;
\r
865 int *nrectangles, fill;
\r
868 XRectangle *crect; /* current rectangle */
\r
870 *nrectangles = CountNodes(tree) - 1; /* don't count root node */
\r
871 *rectangles = (XRectangle *) malloc(sizeof(XRectangle) * *nrectangles);
\r
872 ASSERT(*rectangles, "could not allocate memory for rectangles");
\r
874 crect = *rectangles;
\r
875 if (!tree->elision)
\r
876 FOREACH_CHILD(child, tree)
\r
877 CollectNodeRectangles(child, &crect, fill);
\r
881 /* ----------------------------------------------------------------------------
\r
883 * CollectNodeSegments() is a recursive function used by GetSubTreeSegments()
\r
884 * to collect the line segments connecting nodes into the pre-allocated
\r
885 * storage passed to this function.
\r
887 * ----------------------------------------------------------------------------
\r
891 CollectNodeSegments(node, segments)
\r
893 XSegment **segments;
\r
897 (*segments)->x1 = node->pos.x - 1;
\r
898 (*segments)->y1 = node->pos.y + (node->height / 2),
\r
899 (*segments)->x2 = node->parent->pos.x + node->parent->width + 1;
\r
900 (*segments)->y2 = node->parent->pos.y + (node->parent->height / 2);
\r
903 if (!node->elision)
\r
904 FOREACH_CHILD(child, node)
\r
905 CollectNodeSegments(child, segments);
\r
909 /* ----------------------------------------------------------------------------
\r
911 * GetSubTreeSegments() builds an array of XSegments that contain
\r
912 * all the line segments connecting the nodes in the tree. The array is
\r
913 * returned in 'segments' and the number of segments is returned in
\r
914 * 'nsegments.' Storage for the segments is allocated in this function.
\r
915 * This function is used by PickAction to determine what line segments
\r
916 * rectangles need to be dissolved away.
\r
918 * ----------------------------------------------------------------------------
\r
922 GetSubTreeSegments(tree, segments, nsegments)
\r
924 XSegment **segments;
\r
928 XSegment *cseg; /* current segment */
\r
930 *nsegments = CountNodes(tree) - 1;
\r
931 *segments = (XSegment *) malloc(sizeof(XSegment) * *nsegments);
\r
932 ASSERT(*segments, "could not allocate memory for segments");
\r
935 if (!tree->elision)
\r
936 FOREACH_CHILD(child, tree)
\r
937 CollectNodeSegments(child, &cseg);
\r
941 /* ----------------------------------------------------------------------------
\r
943 * ComputeSubTreeExtent() computes the extent of a subtree. This is
\r
944 * easily computed based on the tree's contour, as in ComputeTreeSize().
\r
945 * This extent is stored in the node, and used by SearchTree for
\r
946 * pick-correlation.
\r
948 * This function assumes that the given tree has at least one child; do not
\r
949 * pass a leaf node to this function.
\r
951 * ----------------------------------------------------------------------------
\r
955 ComputeSubTreeExtent(tree)
\r
959 int x_offset, y_offset;
\r
961 ComputeTreeSize(tree, &width, &height, &x_offset, &y_offset);
\r
962 tree->subextent.pos.x = tree->child->pos.x - tree->child->border;
\r
963 tree->subextent.pos.y = tree->pos.y - y_offset;
\r
964 tree->subextent.width = width - (tree->child->pos.x - tree->pos.x) - 1;
\r
965 tree->subextent.height = height - 1;
\r
969 /* ----------------------------------------------------------------------------
\r
971 * SearchTree() determines if a node's rectangular region encloses the
\r
972 * specified point in (x,y). Rather than using a brute-force search
\r
973 * through all node rectangles of a given tree, the subtree extents
\r
974 * are used in a recursive fashion to drive the search as long as the
\r
975 * given point is enclosed in an extent. In the worst case, the search
\r
976 * time would be on the order of a brute-force search, but with complex
\r
977 * trees, this method reduces the number of visits.
\r
979 * The extent of a subtree is computed by ComputeSubTreeExtent() and is
\r
980 * stored in each node of the tree.
\r
982 * ----------------------------------------------------------------------------
\r
986 SearchTree(tree, x, y, node)
\r
987 Tree *tree, **node;
\r
995 if (PT_IN_RECT(x, y, tree->pos.x, tree->pos.y,
\r
996 tree->pos.x + tree->width,
\r
997 tree->pos.y + tree->height)) {
\r
1001 if (tree->child && (PT_IN_EXTENT(x, y, tree->subextent)))
\r
1002 FOREACH_CHILD(child, tree) {
\r
1003 if (SearchTree(child, x, y, node))
\r
1010 /* ----------------------------------------------------------------------------
\r
1012 * ExposeHandler() handles expose events in the TreeDrawingArea. This
\r
1013 * function is not intelligent; it just redraws the entire contents.
\r
1015 * ----------------------------------------------------------------------------
\r
1020 ExposeHandler (void)
\r
1023 SetContours(TreeShowContourOption);
\r
1024 DrawTree(TheTree, New);
\r
1031 ExposeHandler(w, client_data, event)
\r
1033 caddr_t client_data;
\r
1034 XExposeEvent *event;
\r
1036 if (event->count == 0) {
\r
1038 SetContours(TreeShowContourOption);
\r
1039 DrawTree(TheTree, New);
\r
1046 /* ----------------------------------------------------------------------------
\r
1048 * ExpandCollapseNode is called to expand or collapse a node in the tree.
\r
1050 * ----------------------------------------------------------------------------
\r
1054 ExpandCollapseNode(node)
\r
1057 int width, height;
\r
1058 int old_width, old_height;
\r
1059 int x_offset, y_offset;
\r
1060 int expand = FALSE;
\r
1062 XRectangle *rectangles;
\r
1063 XSegment *segments;
\r
1064 int nrectangles, nsegments;
\r
1065 Widget w = TreeDrawingArea;
\r
1068 StatusMsg("", TRUE);
\r
1070 /* hilite node so that we know where we are */
\r
1071 /* DrawTree will hilite it as a side effect */
\r
1072 if (TreeShowSteps)
\r
1073 node->on_path = TRUE;
\r
1075 /* erase the contour before changing in the tree */
\r
1076 if ((TreeShowContourOption != NoContours) || TreeShowSteps) {
\r
1078 DrawTree(TheTree, New);
\r
1082 sprintf(strbuf, "Node `%s' selected for %s", node->label.text,
\r
1083 node->elision ? "expansion" : "collapse");
\r
1084 StatusMsg(strbuf, FALSE);
\r
1088 Unzip(node->parent);
\r
1090 StatusMsg("Show entire contour", FALSE);
\r
1091 if (TreeShowSteps) {
\r
1093 DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, FALSE, FALSE);
\r
1094 DrawTree(TheTree, New);
\r
1100 /* are we collapsing a subtree? */
\r
1101 if (!node->elision) {
\r
1103 StatusMsg("Collapse subtree", FALSE);
\r
1104 GetSubTreeRectangles(node, &rectangles, &nrectangles, TRUE);
\r
1105 GetSubTreeSegments(node, &segments, &nsegments);
\r
1106 DissolveTree(XtDisplay(w), XtWindow(w),
\r
1107 rectangles, nrectangles,
\r
1108 segments, nsegments, TRUE);
\r
1114 StatusMsg("Replace subtree contour with leaf contour", FALSE);
\r
1115 node->elision = TRUE;
\r
1116 if (TreeShowSteps)
\r
1117 node->split = TRUE; /* turned off in AnimateZip */
\r
1118 node->old_contour = node->contour;
\r
1119 node->width += ELISION_WIDTH;
\r
1122 SetContours(TreeShowContourOption);
\r
1123 DrawTree(TheTree, New);
\r
1127 StatusMsg("Replace leaf contour with old subtree contour", FALSE);
\r
1128 if (TreeShowSteps)
\r
1129 node->split = TRUE; /* turned off in AnimateZip */
\r
1131 node->contour = node->old_contour;
\r
1136 Zip(node->parent);
\r
1138 ComputeTreeSize(TheTree, &width, &height, &x_offset, &y_offset);
\r
1139 PetrifyTree(TheTree, x_offset + MAT_SIZE, y_offset + MAT_SIZE);
\r
1140 GetDrawingSize(&old_width, &old_height);
\r
1143 SetDrawingSize(width + (2 * MAT_SIZE), height + (2 * MAT_SIZE));
\r
1145 DrawTree(TheTree, Old);
\r
1148 StatusMsg("Move tree to new configuration", FALSE);
\r
1149 AnimateTree(TheTree);
\r
1151 /* we are shrinking or staying the same */
\r
1152 StatusMsg("Move tree to new configuration", FALSE);
\r
1153 AnimateTree(TheTree);
\r
1154 SetDrawingSize(width + (2 * MAT_SIZE), height + (2 * MAT_SIZE));
\r
1158 StatusMsg("Expand subtree", FALSE);
\r
1159 node->elision = FALSE;
\r
1161 /* erase elision marker */
\r
1163 SelectNewDeleteOld (CreateSolidBrush (GetBkColor (TreeDrawingAreaDB->hdc)));
\r
1164 Rectangle (TreeDrawingAreaDB->hdc, node->pos.x + node->width - ELISION_WIDTH,
\r
1165 node->pos.y + 1, node->pos.x + node->width, node->pos.y + node->height);
\r
1167 XSetFunction(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
\r
1169 XFillRectangle(XtDisplay(w), XtWindow(w), TreeDrawingAreaDB->gc,
\r
1170 node->pos.x + node->width - ELISION_WIDTH + 1,
\r
1171 node->pos.y, ELISION_WIDTH, node->height + 1);
\r
1172 XSetFunction(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,
\r
1175 node->width -= ELISION_WIDTH;
\r
1178 GetSubTreeRectangles(node, &rectangles, &nrectangles, FALSE);
\r
1179 GetSubTreeSegments(node, &segments, &nsegments);
\r
1180 /* dissolve the tree back in */
\r
1181 DissolveTree(XtDisplay(w), XtWindow(w),
\r
1182 rectangles, nrectangles,
\r
1183 segments, nsegments, FALSE);
\r
1188 /* draw text of nodes */
\r
1190 SetContours(TreeShowContourOption);
\r
1191 DrawTree(TheTree, New);
\r
1196 if (TreeShowSteps) {
\r
1197 node->on_path = FALSE;
\r
1199 AnimateZip(node->parent);
\r
1201 node->split = FALSE;
\r
1204 /* BUG: the display isn't properly updated here! */
\r
1205 /* There should probably be some code here that
\r
1206 clears the tree below the node currently being
\r
1207 collapsed or expanded. Hack added 20.03.95 (torgeir@ii.uib.no).
\r
1208 I'll try to fix this later. */
\r
1211 XClearArea(TreeDisplay, XtWindow(TreeDrawingArea), 0, 0, 0, 0, FALSE);
\r
1214 SetContours(TreeShowContourOption);
\r
1215 DrawTree(TheTree, New);
\r
1219 StatusMsg("Ready", TRUE);
\r
1222 /* ----------------------------------------------------------------------------
\r
1224 * InsertNode() handles the task of inserting a new node in the tree,
\r
1225 * at the given position with respect to 'base_node'. When 'node_pos' is
\r
1226 * either Before or After, it is assumed that 'base_node' has a parent.
\r
1228 * ----------------------------------------------------------------------------
\r
1232 InsertNode(base_node, node_pos, new_node_text)
\r
1234 NodePosition node_pos;
\r
1235 char *new_node_text;
\r
1239 Tree *sibling = NULL;
\r
1242 int width, height;
\r
1243 int x_offset, y_offset;
\r
1245 StatusMsg("", TRUE);
\r
1247 new_node = MakeNode(); /* should check for memory failure */
\r
1248 SetNodeLabel(new_node, new_node_text);
\r
1249 LayoutLeaf(new_node);
\r
1251 /* figure out parent & sibling */
\r
1252 if (node_pos == Child) {
\r
1253 parent = base_node;
\r
1254 /* find last child, if one exists */
\r
1255 FOREACH_CHILD(child, parent)
\r
1257 } else if (node_pos == After) {
\r
1258 parent = base_node->parent;
\r
1259 sibling = base_node;
\r
1260 } else if (node_pos == Before) {
\r
1261 parent = base_node->parent;
\r
1262 FOREACH_CHILD(child, parent)
\r
1263 if (child->sibling == base_node) {
\r
1269 if (TreeShowSteps)
\r
1270 parent->on_path = TRUE;
\r
1272 if ((TreeShowContourOption != NoContours) ||
\r
1275 DrawTree(TheTree, New);
\r
1279 sprintf(strbuf, "Inserting `%s' as child of node `%s'",
\r
1280 new_node_text, parent->label.text);
\r
1281 StatusMsg(strbuf, FALSE);
\r
1284 /* erase the contour before changing in the tree */
\r
1286 Insert(parent, new_node, sibling);
\r
1288 ComputeTreeSize(TheTree, &width, &height, &x_offset, &y_offset);
\r
1289 PetrifyTree(TheTree, x_offset + MAT_SIZE, y_offset + MAT_SIZE);
\r
1292 new_node->old_pos = sibling->old_pos;
\r
1293 else if (new_node->sibling)
\r
1294 new_node->old_pos = new_node->sibling->old_pos;
\r
1296 new_node->old_pos.x = new_node->pos.x;
\r
1297 new_node->old_pos.y = parent->old_pos.y;
\r
1300 if (TreeShowSteps)
\r
1301 new_node->split = TRUE;
\r
1303 SetDrawingSize(width + (2 * MAT_SIZE), height + (2 * MAT_SIZE));
\r
1305 DrawTree(TheTree, Old);
\r
1307 StatusMsg("Insert: add new node and contour", FALSE);
\r
1310 StatusMsg("Move tree to new configuration", FALSE);
\r
1311 AnimateTree(TheTree);
\r
1313 if (TreeShowSteps) {
\r
1315 AnimateZip(parent);
\r
1319 SetContours(TreeShowContourOption);
\r
1320 DrawTree(TheTree, New);
\r
1323 StatusMsg("Ready", TRUE);
\r
1326 /* ----------------------------------------------------------------------------
\r
1328 * DeleteNode() handles the task of deleting a given node in the tree.
\r
1330 * ----------------------------------------------------------------------------
\r
1340 XRectangle *rectangles;
\r
1341 XSegment *segments;
\r
1342 int nrectangles, nsegments;
\r
1343 Widget w = TreeDrawingArea;
\r
1345 int width, height;
\r
1346 int x_offset, y_offset;
\r
1348 StatusMsg("", TRUE);
\r
1350 if (TreeShowSteps)
\r
1351 node->on_path = TRUE;
\r
1353 /* erase the contour before changing in the tree */
\r
1354 if ((TreeShowContourOption != NoContours) ||
\r
1357 DrawTree(TheTree, New);
\r
1361 sprintf(strbuf, "Node `%s' selected for deletion", node->label.text);
\r
1362 StatusMsg(strbuf, FALSE);
\r
1365 parent = node->parent;
\r
1370 TheTree = NULL; /* delete root of tree */
\r
1372 /* fade out deleted subtree */
\r
1374 StatusMsg("Delete subtree", FALSE);
\r
1375 GetSubTreeRectangles(node, &rectangles, &nrectangles, TRUE);
\r
1376 GetSubTreeSegments(node, &segments, &nsegments);
\r
1377 DissolveTree(XtDisplay(w), XtWindow(w),
\r
1378 rectangles, nrectangles,
\r
1379 segments, nsegments, TRUE);
\r
1388 DrawTree(TheTree, New);
\r
1396 ComputeTreeSize(TheTree, &width, &height, &x_offset, &y_offset);
\r
1397 PetrifyTree(TheTree, x_offset + MAT_SIZE, y_offset + MAT_SIZE);
\r
1398 StatusMsg("Move tree to new configuration", FALSE);
\r
1399 AnimateTree(TheTree);
\r
1400 SetDrawingSize(width + (2 * MAT_SIZE), height + (2 * MAT_SIZE));
\r
1403 if (TreeShowSteps) {
\r
1405 AnimateZip(parent);
\r
1409 SetContours(TreeShowContourOption);
\r
1410 DrawTree(TheTree, New);
\r
1415 StatusMsg("Ready", TRUE);
\r
1419 /* ----------------------------------------------------------------------------
\r
1421 * ResetLabels() is called when the TreeAlignNodes mode is changed.
\r
1422 * When TreeParentDistance changes, the node width changes, so this
\r
1423 * function forces each node's width to be recomputed.
\r
1425 * ----------------------------------------------------------------------------
\r
1433 SetNodeLabel(tree, tree->label.text);
\r
1434 FOREACH_CHILD(child, tree)
\r
1435 ResetLabels(child);
\r
1439 /* ----------------------------------------------------------------------------
\r
1441 * SetupTree() handles the task of setting up the specified tree in
\r
1442 * the drawing area.
\r
1444 * ----------------------------------------------------------------------------
\r
1451 int width, height;
\r
1452 int x_offset, y_offset;
\r
1455 ComputeTreeSize(tree, &width, &height, &x_offset, &y_offset);
\r
1456 PetrifyTree(tree, x_offset + MAT_SIZE, y_offset + MAT_SIZE);
\r
1457 SetDrawingTree(tree);
\r
1458 SetDrawingSize(width + (2 * MAT_SIZE), height + (2 * MAT_SIZE));
\r
1460 SetContours(TreeShowContourOption);
\r
1461 DrawTree(tree, New);
\r