Initial Commit
[packages] / xemacs-packages / oo-browser / tree-w32 / draw.c
1 /* ----------------------------------------------------------------------------\r
2  * File    : draw.c\r
3  * Purpose : drawing-specific routines for dynamic tree program\r
4  * ----------------------------------------------------------------------------\r
5  */\r
6 \r
7 #ifdef MSW\r
8 #include <windows.h>\r
9 #else\r
10 #include <X11/Intrinsic.h>\r
11 #include <X11/StringDefs.h>\r
12 #endif\r
13 \r
14 #include <stdlib.h>\r
15 #include "defs.h"\r
16 #include "tree.h"\r
17 #include "dbl.h"\r
18 #include "intf.h"\r
19 \r
20 /* ------------------------------------------------------------------------- */\r
21 /*                              Global Variables                             */\r
22 /* ------------------------------------------------------------------------- */\r
23 \r
24 Tree *TheTree;\r
25 \r
26 /* ------------------------------------------------------------------------- */\r
27 /*                              Local Variables                              */\r
28 /* ------------------------------------------------------------------------- */\r
29 \r
30 static char AnimationMode = FALSE;        \r
31 static char strbuf[BUFSIZ];\r
32 static int  AnimationStep = ANIMATION_STEP;\r
33 \r
34 #ifdef MSW\r
35 static int DrawingWidth;\r
36 static int DrawingHeight;\r
37 #endif\r
38 \r
39 /* ------------------------------------------------------------------------- */\r
40 /*                       Forward Function Declarations                       */\r
41 /* ------------------------------------------------------------------------- */\r
42 \r
43 void DrawNode();\r
44 void DrawTreeContour();\r
45 \r
46 \r
47 /* ------------------------------------------------------------------------- */\r
48 /*                                 Functions                                 */\r
49 /* ------------------------------------------------------------------------- */\r
50 \r
51 #ifdef MSW\r
52 static void\r
53 SelectNewDeleteOld (HGDIOBJ obj)\r
54 {\r
55   obj = SelectObject (TreeDrawingAreaDB->hdc, obj);\r
56   DeleteObject (obj);\r
57 }\r
58 \r
59 static void\r
60 RefreshPen (void)\r
61 {\r
62   HPEN pen = CreatePen (PS_SOLID, TreeDrawingAreaDB->LineWidth,\r
63                         GetTextColor (TreeDrawingAreaDB->hdc));\r
64   SelectNewDeleteOld (pen);\r
65 }\r
66 \r
67 BOOL SetNewFont (LPLOGFONT lplf)\r
68 {\r
69   HFONT font = CreateFontIndirect (lplf);\r
70   if (font)\r
71     SelectNewDeleteOld (font);\r
72   return font != NULL;\r
73 }\r
74 #endif\r
75 \r
76 /* ----------------------------------------------------------------------------\r
77  * \r
78  *   BeginFrame() provides an abstraction for double buffering. It should\r
79  *   be called prior to creating a new frame of animation.\r
80  * \r
81  * ----------------------------------------------------------------------------\r
82  */\r
83 \r
84 void\r
85 BeginFrame()\r
86 {\r
87 #ifndef MSW\r
88   DBLbegin_frame(TreeDrawingAreaDB);\r
89 #endif\r
90 }\r
91 \r
92 \r
93 /* ----------------------------------------------------------------------------\r
94  * \r
95  *   EndFrame() provides an abstraction for double buffering. It should\r
96  *   be called after creating a new frame of animation.\r
97  * \r
98  * ----------------------------------------------------------------------------\r
99  */\r
100 \r
101 void\r
102 EndFrame()\r
103 {\r
104 #ifndef MSW\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
108 #endif\r
109 }\r
110 \r
111 \r
112 /* ----------------------------------------------------------------------------\r
113  * \r
114  *   GetDrawingSize() gets the size of the drawing area, and returns the\r
115  *   dimensions in the arguments.\r
116  * \r
117  * ----------------------------------------------------------------------------\r
118  */\r
119 \r
120 void\r
121 GetDrawingSize(width, height)\r
122      int *width, *height;\r
123 {\r
124 #ifdef MSW\r
125   *width  = TreeDrawingAreaDB->DrawingWidth;\r
126   *height = TreeDrawingAreaDB->DrawingHeight;\r
127 #else\r
128   Dimension w, h;\r
129   \r
130   XtVaGetValues(TreeDrawingArea, \r
131                 XtNwidth, &w,\r
132                 XtNheight, &h,\r
133                 NULL);\r
134 \r
135   *width  = (int) w;\r
136   *height = (int) h;\r
137 #endif\r
138 }\r
139 \r
140 \r
141 /* ----------------------------------------------------------------------------\r
142  * \r
143  *   SetDrawingSize() sets the size of the drawing area to the given\r
144  *   dimensions. \r
145  * \r
146  * ----------------------------------------------------------------------------\r
147  */\r
148 \r
149 void\r
150 SetDrawingSize(width, height)\r
151      int width, height;\r
152 {\r
153 #ifdef MSW\r
154   RECT rc;\r
155   POINT pt;\r
156   SCROLLINFO si;\r
157 \r
158   TreeDrawingAreaDB->DrawingWidth = width;\r
159   TreeDrawingAreaDB->DrawingHeight = height;\r
160 \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
166 \r
167   /* Vertical bar */\r
168   si.nMin = 0;\r
169   si.nMax = height;\r
170   si.nPage = rc.bottom;\r
171   si.nPos = pt.y;\r
172   SetScrollInfo (hwndClient, SB_VERT, &si, TRUE);\r
173 \r
174   /* Horizontal bar */\r
175   si.nMin = 0;\r
176   si.nMax = width;\r
177   si.nPage = rc.right;\r
178   si.nPos = pt.x;\r
179   SetScrollInfo (hwndClient, SB_HORZ, &si, TRUE);\r
180 #else\r
181   XtVaSetValues(TreeDrawingArea,\r
182                 XtNwidth, (Dimension) width,\r
183                 XtNheight,  (Dimension) height,\r
184                 NULL);\r
185 #endif\r
186 }\r
187 \r
188 \r
189 /* ----------------------------------------------------------------------------\r
190  * \r
191  *   SetDrawingTree() is used to specify what tree is to be drawn in the\r
192  *   drawing area. \r
193  * \r
194  * ----------------------------------------------------------------------------\r
195  */\r
196 \r
197 void\r
198 SetDrawingTree(tree)\r
199    Tree *tree;\r
200 {\r
201    TheTree = tree;\r
202 }\r
203 \r
204 \r
205 /* ----------------------------------------------------------------------------\r
206  * \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
212  * \r
213  * ----------------------------------------------------------------------------\r
214  */\r
215 \r
216 void\r
217 SetNodeLabel(node, label)\r
218      Tree *node;\r
219      char *label;\r
220 {\r
221   int         len;\r
222 #ifdef MSW\r
223   SIZE        sz;\r
224 #else\r
225   int         dummy;\r
226   XCharStruct rtrn;\r
227 #endif\r
228   \r
229   len = strlen(label);\r
230   while (len > 1) {\r
231 #ifdef MSW\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
235 #else\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
239 #endif\r
240     if (TreeAlignNodes) {\r
241       if (node->width <= (2 * TreeParentDistance))\r
242         break;\r
243       else\r
244         len--;\r
245     }\r
246     else\r
247       break;\r
248   }\r
249   \r
250   node->label.text = label;\r
251   node->label.len  = len;\r
252   node->label.xoffset = LABEL_MAT_WIDTH + 1,\r
253 #ifdef MSW\r
254   node->label.yoffset = LABEL_MAT_HEIGHT + 1;\r
255 #else\r
256   node->label.yoffset = rtrn.ascent + LABEL_MAT_HEIGHT + 1;\r
257 #endif\r
258 }\r
259 \r
260 \r
261 /* ----------------------------------------------------------------------------\r
262  * \r
263  *   SetDrawColor() sets the drawing color of the TreeDrawingArea. \r
264  * \r
265  * ----------------------------------------------------------------------------\r
266  */\r
267 \r
268 void\r
269 SetDrawColor(color)\r
270      int color;\r
271 {\r
272 #ifdef MSW\r
273   SetTextColor (TreeDrawingAreaDB->hdc, TreeDrawingAreaDB->colors[color]);\r
274 #else\r
275   XSetForeground(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,\r
276                  TreeDrawingAreaDB->colors[color]);\r
277 #endif\r
278 }\r
279 \r
280 /* ----------------------------------------------------------------------------\r
281  * \r
282  *   SetLineWidth() sets the line width of lines drawn in the TreeDrawingArea.\r
283  * \r
284  * ----------------------------------------------------------------------------\r
285  */\r
286 \r
287 void\r
288 SetLineWidth(width, color)\r
289      unsigned int width;\r
290      int color;\r
291 {\r
292 #ifdef MSW\r
293   TreeDrawingAreaDB->LineWidth = width;\r
294 #else\r
295   XSetLineAttributes(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,\r
296                      width, LineSolid, CapButt, JoinRound);\r
297 #endif\r
298 }\r
299 \r
300 \r
301 /* ----------------------------------------------------------------------------\r
302  * \r
303  *   SetContours() sets the visibility of three possible contour modes: \r
304  *   the outside contour, all subtree contours, or selected contours.\r
305  * \r
306  * ----------------------------------------------------------------------------\r
307  */\r
308 \r
309 void\r
310 SetContours(option)\r
311      ContourOption option;\r
312 {\r
313   if (option == NoContours) {\r
314     switch (TreeShowContourOption) {\r
315     case OutsideContour:\r
316       DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, FALSE);\r
317       break;\r
318     case AllContours:\r
319       DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);\r
320       break;\r
321     case SelectedContours:\r
322       DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, TRUE, TRUE);\r
323       break;\r
324     default:\r
325       ;\r
326     }\r
327     DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);\r
328   }\r
329   else if (option == OutsideContour) {\r
330     switch (TreeShowContourOption) {\r
331     case AllContours:\r
332       DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);\r
333       break;\r
334     case SelectedContours:\r
335       DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, TRUE, TRUE);\r
336       break;\r
337     default:\r
338       ;\r
339     }\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
345     case AllContours:\r
346       DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);\r
347       break;\r
348     case OutsideContour:\r
349       DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, FALSE);\r
350       break;\r
351     default:\r
352       DrawTreeContour(TheTree, New, BACKGROUND_COLOR, FALSE, FALSE, TRUE);\r
353     }\r
354     DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, TRUE, TRUE);\r
355   }\r
356   TreeShowContourOption = option;\r
357 }\r
358 \r
359 \r
360 /* ----------------------------------------------------------------------------\r
361  * \r
362  *   HiliteNode() is called by Unzip() to change the color of a node. \r
363  * \r
364  * ----------------------------------------------------------------------------\r
365  */\r
366 \r
367 void\r
368 HiliteNode(tree, pos_mode)\r
369      Tree *tree;\r
370 {\r
371   SetDrawColor(HIGHLIGHT_COLOR);\r
372   DrawNode(tree, pos_mode);\r
373   SetDrawColor(TREE_COLOR);\r
374 }\r
375 \r
376 \r
377 /* ----------------------------------------------------------------------------\r
378  * \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
387  * \r
388  * ----------------------------------------------------------------------------\r
389  */\r
390 \r
391 static void\r
392 DrawNodeHelper (node, pos, parent_pos)\r
393      Tree     *node;\r
394      Point    *pos;\r
395      Point    *parent_pos;\r
396 {\r
397 #ifdef MSW\r
398   HDC hdc = TreeDrawingAreaDB->hdc;\r
399   RECT rcNode;\r
400 \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
405 \r
406   if (node->highlight)\r
407     {\r
408       SetDrawColor(HIGHLIGHT_COLOR);\r
409       SetBkColor (hdc, TreeDrawingAreaDB->colors [HIGHLIGHT_BK_COLOR]);\r
410     }\r
411   else\r
412     {\r
413       SetDrawColor(TREE_COLOR);\r
414       SetBkColor (hdc, TreeDrawingAreaDB->colors [BACKGROUND_COLOR]);\r
415     }\r
416       \r
417   ExtTextOut (hdc,\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
422 \r
423   if (node->elision)\r
424     {\r
425       POINT pt[3];\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
432       RefreshPen ();\r
433       SelectNewDeleteOld (CreateSolidBrush (GetTextColor (hdc)));\r
434       Polygon (hdc, pt, 3);\r
435     }\r
436 \r
437   SetDrawColor(TREE_COLOR);\r
438   RefreshPen ();\r
439 \r
440   if (!node->highlight)\r
441     {\r
442       SelectNewDeleteOld (GetStockObject (HOLLOW_BRUSH));\r
443       Rectangle (hdc, rcNode.left, rcNode.top, rcNode.right, rcNode.bottom);\r
444     }\r
445 \r
446   if (node->focus)\r
447     DrawFocusRect (hdc, &rcNode);\r
448   \r
449   if (node->parent)\r
450     {\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
454     }\r
455 #else \r
456   Widget   w;\r
457   GC       gc;\r
458   \r
459   w  = TreeDrawingArea;\r
460   gc = TreeDrawingAreaDB->gc;\r
461   \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
467                  pos->x, pos->y,\r
468                  node->width, node->height);\r
469   if (node->parent) \r
470     XDrawLine(XtDisplay(w), XtWindow(w), gc,\r
471               pos->x - 1,\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
477                   FillTiled);\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
482                   FillSolid);\r
483   }\r
484 #endif\r
485 }\r
486 \r
487 void\r
488 DrawNode(node, pos_mode)\r
489      Tree     *node;\r
490      PosMode  pos_mode;\r
491 {\r
492   if (pos_mode == Old)\r
493     DrawNodeHelper (node, &node->old_pos, node->parent ? &node->parent->old_pos : NULL);\r
494   else\r
495     DrawNodeHelper (node, &node->pos, node->parent ? &node->parent->pos : NULL);\r
496 }\r
497 \r
498 \r
499 /* ----------------------------------------------------------------------------\r
500  * \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
507  * \r
508  * ----------------------------------------------------------------------------\r
509  */\r
510 \r
511 void\r
512 DrawTreeContour(tree, pos_mode, color, detach, select, recursive)\r
513      Tree *tree;\r
514      PosMode pos_mode;\r
515      int color;\r
516      int detach;\r
517      int select;\r
518      int recursive;\r
519 {\r
520 #ifndef MSW\r
521   Widget w = TreeDrawingArea;\r
522 #endif\r
523   TreePolyline *contour, *tail;\r
524   Tree *child;\r
525   int x, y, i;\r
526 \r
527   if (tree == NULL)\r
528     return;\r
529 \r
530   if ((select && tree->show_contour) || !select) {\r
531 \r
532     SetDrawColor(color);\r
533     SetLineWidth(TreeContourWidth, color);\r
534    \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
541     }\r
542     else {\r
543       x = tree->pos.x - tree->border;\r
544       y = tree->pos.y - tree->border;\r
545     }\r
546 \r
547     if (detach) {               /* skip over attaching lines */\r
548       for (i = 0 ; i < 2 ; i++) {\r
549         x += contour->dx;\r
550         y += contour->dy;\r
551         contour = contour->link;\r
552       }\r
553     }\r
554 \r
555 #ifdef MSW\r
556     RefreshPen ();\r
557     MoveToEx (TreeDrawingAreaDB->hdc, x, y, NULL);\r
558 #endif\r
559     while (contour) {\r
560       x += contour->dx;\r
561       y += contour->dy;\r
562 #ifdef MSW\r
563       LineTo (TreeDrawingAreaDB->hdc, x, y);\r
564 #else\r
565       XDrawLine(XtDisplay(w), XtWindow(w), TreeDrawingAreaDB->gc,\r
566                 x - contour->dx, y - contour->dy, x, y);\r
567 #endif\r
568       if (contour == tail)      /* make sure you don't follow bridges */\r
569         contour = NULL;\r
570       else\r
571         contour = contour->link;\r
572     }\r
573 \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
580     } else {\r
581       x = tree->pos.x - tree->border;\r
582       y = tree->pos.y + tree->border + tree->height;\r
583     }\r
584 \r
585     if (detach) {               /* skip over attaching lines */\r
586       for (i = 0 ; i < 2 ; i++) {\r
587         x += contour->dx;\r
588         y += contour->dy;\r
589         contour = contour->link;\r
590       }\r
591     }\r
592 \r
593 #ifdef MSW\r
594     MoveToEx (TreeDrawingAreaDB->hdc, x, y, NULL);\r
595 #endif\r
596     while (contour) {\r
597       x += contour->dx;\r
598       y += contour->dy;\r
599 #ifdef MSW\r
600       LineTo (TreeDrawingAreaDB->hdc, x, y);\r
601 #else\r
602       XDrawLine(XtDisplay(w), XtWindow(w), TreeDrawingAreaDB->gc,\r
603                 x - contour->dx, y - contour->dy, x, y);\r
604 #endif\r
605       if (contour == tail)      /* make sure you don't follow bridges */\r
606         contour = NULL;\r
607       else\r
608         contour = contour->link;\r
609     }\r
610   }\r
611   \r
612   if (recursive) {\r
613     FOREACH_CHILD(child, tree)\r
614       if (!child->elision)\r
615         DrawTreeContour(child, pos_mode, color,\r
616                         detach, select, recursive);\r
617   }\r
618 \r
619   SetDrawColor(TREE_COLOR);\r
620   SetLineWidth(0, TREE_COLOR);\r
621 }\r
622 \r
623 \r
624 /* ----------------------------------------------------------------------------\r
625  * \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
632  *   node.\r
633  * \r
634  * ----------------------------------------------------------------------------\r
635  */\r
636 \r
637 void\r
638 DrawTree(tree, pos_mode)\r
639      Tree     *tree;\r
640      PosMode  pos_mode;\r
641 {\r
642   if (tree == NULL)\r
643     return;\r
644 \r
645   DrawNode(tree, pos_mode);\r
646 \r
647   /* do stuff that animates Unzip() */\r
648   if (tree->split) {\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
653     else\r
654       DrawTreeContour(tree, pos_mode, ACTION_COLOR, FALSE, FALSE, FALSE);\r
655   }\r
656   if (tree->on_path)\r
657     HiliteNode(tree, pos_mode);\r
658 \r
659   if (tree->child && !tree->elision) \r
660     DrawTree(tree->child, pos_mode);\r
661   if (tree->sibling)\r
662     DrawTree(tree->sibling, pos_mode);\r
663 }\r
664 \r
665 \r
666 /* ----------------------------------------------------------------------------\r
667  * \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
672  * \r
673  * ----------------------------------------------------------------------------\r
674  */\r
675 \r
676 void\r
677 ShiftTree(tree, done)\r
678      Tree *tree;\r
679      int  *done;\r
680 {\r
681   Tree *child;\r
682   \r
683   if (tree->old_pos.x != tree->pos.x ||\r
684       tree->old_pos.y != tree->pos.y)\r
685     {\r
686       tree->old_pos.x = tree->pos.x;\r
687       tree->old_pos.y = tree->pos.y;\r
688     }\r
689   \r
690   FOREACH_CHILD(child, tree)\r
691     ShiftTree(child, done);\r
692 }\r
693 \r
694 \r
695 /* ----------------------------------------------------------------------------\r
696  * \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
699  *   tree. \r
700  * \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
705  * \r
706  * ----------------------------------------------------------------------------\r
707  */\r
708 \r
709 void\r
710 AnimateTree(tree)\r
711      Tree *tree;\r
712 {\r
713   int done = FALSE;\r
714 \r
715   AnimationMode = FALSE;\r
716   /* highlight which nodes have to move */\r
717   BeginFrame();\r
718     DrawTree(tree, Old);\r
719   EndFrame();\r
720   Pause(); \r
721   if (PauseAfterStep)\r
722     AnimationStep = ANIMATION_STEP_STEP;\r
723   while (!done) {\r
724     done = TRUE;\r
725     ShiftTree(tree, &done);\r
726     BeginFrame();\r
727       DrawTree(tree, Old);\r
728     EndFrame();\r
729     if (PauseAfterStep)\r
730       Pause();\r
731   }\r
732   if (PauseAfterStep)\r
733     AnimationStep = ANIMATION_STEP;\r
734   AnimationMode = FALSE;\r
735 }\r
736 \r
737 \r
738 /* ----------------------------------------------------------------------------\r
739  * \r
740  *   AnimateZip() generates a sequence of frames that animates the Zip() step.\r
741  *   It is similar in logical structure to Zip().\r
742  * \r
743  * ----------------------------------------------------------------------------\r
744  */\r
745 \r
746 void \r
747 AnimateZip(tree)\r
748      Tree *tree;\r
749 {\r
750   Tree *child;\r
751    \r
752   /* show results of Join() step */\r
753   if (tree->child) {\r
754     BeginFrame();\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
759     EndFrame();\r
760 \r
761     StatusMsg("Zip: merge and join contours", FALSE);\r
762     Pause(); \r
763    \r
764     /* show results of AttachParent() step */\r
765     BeginFrame();\r
766       DrawTree(TheTree, New);\r
767       DrawTreeContour(tree, New, CONTOUR_COLOR, FALSE, FALSE, FALSE);\r
768     EndFrame();\r
769 \r
770     StatusMsg("Zip: attach parents", FALSE);\r
771     Pause(); \r
772   }\r
773   \r
774   tree->on_path = FALSE;\r
775    \r
776   if (tree->parent)\r
777     AnimateZip(tree->parent);\r
778   else {\r
779     tree->on_path = FALSE;\r
780     BeginFrame();\r
781       DrawTree(TheTree, New);\r
782       DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, FALSE, FALSE);\r
783     EndFrame();\r
784     StatusMsg("Zip: reassemble entire contour", FALSE);\r
785     Pause(); \r
786   }\r
787 }\r
788 \r
789 \r
790 /* ----------------------------------------------------------------------------\r
791  * \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
794  * \r
795  * ----------------------------------------------------------------------------\r
796  */\r
797 \r
798 int\r
799 CountNodes(tree)\r
800      Tree *tree;\r
801 {\r
802   int num_nodes = 1;            /* count root of subtree */\r
803   Tree *child;\r
804   \r
805   if (!tree->elision) {\r
806     FOREACH_CHILD(child, tree)\r
807       num_nodes += CountNodes(child);\r
808   }\r
809   return (num_nodes);\r
810 }\r
811 \r
812 \r
813 /* ----------------------------------------------------------------------------\r
814  * \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
818  * \r
819  * ----------------------------------------------------------------------------\r
820  */\r
821 \r
822 void\r
823 CollectNodeRectangles(node, rectangles, fill)\r
824      Tree *node;\r
825      XRectangle **rectangles;\r
826      int fill;\r
827 {\r
828   Tree *child;\r
829    \r
830   (*rectangles)->x = node->pos.x;\r
831   (*rectangles)->y = node->pos.y;\r
832   if (fill) {\r
833     (*rectangles)->width = node->width + 1;\r
834     (*rectangles)->height = node->height + 1;\r
835   } else {\r
836     (*rectangles)->width = node->width;\r
837     (*rectangles)->height = node->height;\r
838   }\r
839   (*rectangles)++;\r
840   \r
841   if (!node->elision)\r
842     FOREACH_CHILD(child, node) \r
843       CollectNodeRectangles(child, rectangles, fill);\r
844 }\r
845 \r
846 \r
847 /* ----------------------------------------------------------------------------\r
848  * \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
857  * \r
858  * ----------------------------------------------------------------------------\r
859  */\r
860 \r
861 void\r
862 GetSubTreeRectangles(tree, rectangles, nrectangles, fill)\r
863      Tree *tree;\r
864      XRectangle **rectangles;\r
865      int *nrectangles, fill;\r
866 {\r
867   Tree *child;\r
868   XRectangle *crect;            /* current rectangle */\r
869   \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
873   \r
874   crect = *rectangles;\r
875   if (!tree->elision)\r
876     FOREACH_CHILD(child, tree)\r
877       CollectNodeRectangles(child, &crect, fill);\r
878 }\r
879 \r
880 \r
881 /* ----------------------------------------------------------------------------\r
882  * \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
886  * \r
887  * ----------------------------------------------------------------------------\r
888  */\r
889 \r
890 void\r
891 CollectNodeSegments(node, segments)\r
892      Tree *node;\r
893      XSegment **segments;\r
894 {\r
895   Tree *child;\r
896    \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
901   (*segments)++;\r
902 \r
903   if (!node->elision)\r
904     FOREACH_CHILD(child, node) \r
905       CollectNodeSegments(child, segments);\r
906 }\r
907 \r
908 \r
909 /* ----------------------------------------------------------------------------\r
910  * \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
917  * \r
918  * ----------------------------------------------------------------------------\r
919  */\r
920 \r
921 void\r
922 GetSubTreeSegments(tree, segments, nsegments)\r
923      Tree *tree;\r
924      XSegment **segments;\r
925      int *nsegments;\r
926 {\r
927   Tree *child;\r
928   XSegment *cseg;               /* current segment */\r
929 \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
933   \r
934   cseg = *segments;\r
935   if (!tree->elision)\r
936     FOREACH_CHILD(child, tree)\r
937       CollectNodeSegments(child, &cseg);\r
938 }\r
939 \r
940 \r
941 /* ----------------------------------------------------------------------------\r
942  * \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
947  * \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
950  * \r
951  * ----------------------------------------------------------------------------\r
952  */\r
953 \r
954 void\r
955 ComputeSubTreeExtent(tree)\r
956      Tree *tree;\r
957 {\r
958   int width, height;\r
959   int x_offset, y_offset;\r
960 \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
966 }\r
967 \r
968 \r
969 /* ----------------------------------------------------------------------------\r
970  * \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
978  * \r
979  *   The extent of a subtree is computed by ComputeSubTreeExtent() and is\r
980  *   stored in each node of the tree.\r
981  * \r
982  * ----------------------------------------------------------------------------\r
983  */\r
984 \r
985 int\r
986 SearchTree(tree, x, y, node)\r
987      Tree *tree, **node;\r
988      int x, y;\r
989 {\r
990   Tree *child;\r
991   \r
992   if (tree == NULL)\r
993     return (FALSE);\r
994 \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
998     *node = tree;\r
999     return (TRUE);\r
1000   }\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
1004         return (TRUE);\r
1005     }\r
1006   return (FALSE);\r
1007 }\r
1008 \r
1009 \r
1010 /* ----------------------------------------------------------------------------\r
1011  * \r
1012  *   ExposeHandler() handles expose events in the TreeDrawingArea. This\r
1013  *   function is not intelligent; it just redraws the entire contents.\r
1014  * \r
1015  * ----------------------------------------------------------------------------\r
1016  */\r
1017 #ifdef MSW\r
1018 \r
1019 void\r
1020 ExposeHandler (void)\r
1021 {\r
1022   BeginFrame();\r
1023     SetContours(TreeShowContourOption);\r
1024     DrawTree(TheTree, New);\r
1025   EndFrame();\r
1026 }\r
1027 \r
1028 #else\r
1029 \r
1030 void\r
1031 ExposeHandler(w, client_data, event)\r
1032      Widget   w;\r
1033      caddr_t client_data;\r
1034      XExposeEvent  *event;\r
1035 {\r
1036   if (event->count == 0) {\r
1037     BeginFrame();\r
1038       SetContours(TreeShowContourOption);\r
1039       DrawTree(TheTree, New);\r
1040     EndFrame();\r
1041   }\r
1042 }\r
1043 \r
1044 #endif\r
1045 \r
1046 /* ----------------------------------------------------------------------------\r
1047  * \r
1048  *   ExpandCollapseNode is called to expand or collapse a node in the tree.\r
1049  * \r
1050  * ----------------------------------------------------------------------------\r
1051  */\r
1052 \r
1053 void\r
1054 ExpandCollapseNode(node)\r
1055      Tree *node;\r
1056 {\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
1061 #ifndef MSW\r
1062   XRectangle *rectangles;\r
1063   XSegment   *segments;\r
1064   int        nrectangles, nsegments;\r
1065   Widget     w = TreeDrawingArea;\r
1066 #endif\r
1067   \r
1068   StatusMsg("", TRUE);\r
1069   \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
1074   \r
1075   /* erase the contour before changing in the tree */\r
1076   if ((TreeShowContourOption != NoContours) || TreeShowSteps) {\r
1077     BeginFrame();\r
1078       DrawTree(TheTree, New);\r
1079     EndFrame();\r
1080   }\r
1081    \r
1082   sprintf(strbuf, "Node `%s' selected for %s", node->label.text,\r
1083           node->elision ? "expansion" : "collapse");\r
1084   StatusMsg(strbuf, FALSE);\r
1085   Pause(); \r
1086 \r
1087   if (node->parent)\r
1088     Unzip(node->parent);\r
1089   else {\r
1090     StatusMsg("Show entire contour", FALSE);\r
1091     if (TreeShowSteps) {\r
1092       BeginFrame();\r
1093         DrawTreeContour(TheTree, New, CONTOUR_COLOR, FALSE, FALSE, FALSE);\r
1094         DrawTree(TheTree, New);\r
1095       EndFrame();\r
1096       Pause(); \r
1097     }\r
1098   }\r
1099 \r
1100   /* are we collapsing a subtree? */\r
1101   if (!node->elision) {\r
1102 #ifndef MSW\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
1109     free(rectangles);\r
1110     free(segments);\r
1111     Pause(); \r
1112 #endif\r
1113     \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
1120     LayoutLeaf(node);\r
1121     BeginFrame();\r
1122       SetContours(TreeShowContourOption);\r
1123       DrawTree(TheTree, New);\r
1124     EndFrame();\r
1125     Pause();  \r
1126   } else {\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
1130     RuboutLeaf(node);\r
1131     node->contour = node->old_contour;\r
1132     expand = TRUE;\r
1133   }\r
1134   \r
1135   if (node->parent)\r
1136     Zip(node->parent);\r
1137   \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
1141 \r
1142   if (expand) {\r
1143     SetDrawingSize(width + (2 * MAT_SIZE), height + (2 * MAT_SIZE));\r
1144     BeginFrame();\r
1145       DrawTree(TheTree, Old);\r
1146     EndFrame();\r
1147     Pause(); \r
1148     StatusMsg("Move tree to new configuration", FALSE);\r
1149     AnimateTree(TheTree);\r
1150   } else {\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
1155   }\r
1156 \r
1157   if (expand) {\r
1158     StatusMsg("Expand subtree", FALSE);\r
1159     node->elision = FALSE;\r
1160     \r
1161     /* erase elision marker */\r
1162 #ifdef MSW\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
1166 #else\r
1167     XSetFunction(TreeDrawingAreaDB->display, TreeDrawingAreaDB->gc,\r
1168                  GXclear);\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
1173                  GXcopy);\r
1174 #endif\r
1175     node->width -= ELISION_WIDTH;\r
1176 \r
1177 #ifndef MSW\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
1184     free(rectangles);\r
1185     free(segments);\r
1186 #endif\r
1187     \r
1188     /* draw text of nodes */\r
1189     BeginFrame();\r
1190       SetContours(TreeShowContourOption);\r
1191       DrawTree(TheTree, New);\r
1192     EndFrame();\r
1193     Pause(); \r
1194   }\r
1195   \r
1196   if (TreeShowSteps) {\r
1197     node->on_path = FALSE;\r
1198     if (node->parent)\r
1199       AnimateZip(node->parent);\r
1200     else\r
1201       node->split = FALSE;\r
1202   }\r
1203   \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
1209 \r
1210 #ifndef MSW\r
1211   XClearArea(TreeDisplay, XtWindow(TreeDrawingArea), 0, 0, 0, 0, FALSE);\r
1212 \r
1213   BeginFrame();\r
1214     SetContours(TreeShowContourOption);\r
1215     DrawTree(TheTree, New);\r
1216   EndFrame();\r
1217 #endif\r
1218   \r
1219   StatusMsg("Ready", TRUE);\r
1220 }\r
1221 \r
1222 /* ----------------------------------------------------------------------------\r
1223  * \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
1227  * \r
1228  * ----------------------------------------------------------------------------\r
1229  */\r
1230 \r
1231 void\r
1232 InsertNode(base_node, node_pos, new_node_text)\r
1233      Tree *base_node;\r
1234      NodePosition node_pos;\r
1235      char *new_node_text;\r
1236 {\r
1237   Tree *new_node;\r
1238   Tree *parent;\r
1239   Tree *sibling = NULL;\r
1240   Tree *child;\r
1241 \r
1242   int  width, height;\r
1243   int  x_offset, y_offset;\r
1244 \r
1245   StatusMsg("", TRUE);\r
1246 \r
1247   new_node = MakeNode();        /* should check for memory failure */\r
1248   SetNodeLabel(new_node, new_node_text);\r
1249   LayoutLeaf(new_node);\r
1250 \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
1256       sibling = child;\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
1264         sibling = child;\r
1265         break;\r
1266       }\r
1267   }\r
1268 \r
1269   if (TreeShowSteps)\r
1270     parent->on_path = TRUE;\r
1271   \r
1272   if ((TreeShowContourOption != NoContours) ||\r
1273       TreeShowSteps) {\r
1274     BeginFrame();\r
1275       DrawTree(TheTree, New);\r
1276     EndFrame();\r
1277   }\r
1278 \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
1282   Pause(); \r
1283    \r
1284   /* erase the contour before changing in the tree */\r
1285   \r
1286   Insert(parent, new_node, sibling);\r
1287   \r
1288   ComputeTreeSize(TheTree, &width, &height, &x_offset, &y_offset);\r
1289   PetrifyTree(TheTree, x_offset + MAT_SIZE, y_offset + MAT_SIZE);\r
1290   \r
1291   if (sibling)\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
1295   else {\r
1296     new_node->old_pos.x = new_node->pos.x;\r
1297     new_node->old_pos.y = parent->old_pos.y;\r
1298   }\r
1299   \r
1300   if (TreeShowSteps)\r
1301     new_node->split = TRUE;\r
1302 \r
1303   SetDrawingSize(width + (2 * MAT_SIZE), height + (2 * MAT_SIZE));\r
1304   BeginFrame();\r
1305     DrawTree(TheTree, Old);\r
1306   EndFrame();\r
1307   StatusMsg("Insert: add new node and contour", FALSE);\r
1308   Pause(); \r
1309    \r
1310   StatusMsg("Move tree to new configuration", FALSE);\r
1311   AnimateTree(TheTree);\r
1312 \r
1313   if (TreeShowSteps) {\r
1314     if (parent)\r
1315       AnimateZip(parent);\r
1316   }\r
1317 \r
1318   BeginFrame();\r
1319     SetContours(TreeShowContourOption);\r
1320     DrawTree(TheTree, New);\r
1321   EndFrame();\r
1322 \r
1323   StatusMsg("Ready", TRUE);\r
1324 }   \r
1325 \r
1326 /* ----------------------------------------------------------------------------\r
1327  * \r
1328  *   DeleteNode() handles the task of deleting a given node in the tree.\r
1329  * \r
1330  * ----------------------------------------------------------------------------\r
1331  */\r
1332 \r
1333 void\r
1334 DeleteNode(node)\r
1335      Tree *node;\r
1336 {\r
1337   Tree *parent;\r
1338 \r
1339 #ifndef MSW\r
1340   XRectangle *rectangles;\r
1341   XSegment   *segments;\r
1342   int         nrectangles, nsegments;\r
1343   Widget      w = TreeDrawingArea;\r
1344 #endif\r
1345   int  width, height;\r
1346   int  x_offset, y_offset;\r
1347   \r
1348   StatusMsg("", TRUE);\r
1349 \r
1350   if (TreeShowSteps)\r
1351     node->on_path = TRUE;\r
1352    \r
1353   /* erase the contour before changing in the tree */\r
1354   if ((TreeShowContourOption != NoContours) ||\r
1355       TreeShowSteps) {\r
1356     BeginFrame();\r
1357       DrawTree(TheTree, New);\r
1358     EndFrame();\r
1359   }\r
1360 \r
1361   sprintf(strbuf, "Node `%s' selected for deletion", node->label.text);\r
1362   StatusMsg(strbuf, FALSE);\r
1363   Pause(); \r
1364   \r
1365   parent = node->parent;\r
1366   \r
1367   if (parent)\r
1368     Unzip(parent);\r
1369   else\r
1370     TheTree = NULL;             /* delete root of tree */\r
1371 \r
1372   /* fade out deleted subtree */\r
1373 #ifndef MSW\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
1380   free(rectangles);\r
1381   free(segments);\r
1382 #endif\r
1383 \r
1384   Delete(node);\r
1385 \r
1386   BeginFrame();\r
1387   if (TheTree) \r
1388     DrawTree(TheTree, New);\r
1389   EndFrame();\r
1390   Pause(); \r
1391 \r
1392   if (parent)\r
1393     Zip(parent);\r
1394   \r
1395   if (TheTree) {\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
1401     Pause(); \r
1402 \r
1403     if (TreeShowSteps) {\r
1404       if (parent)\r
1405         AnimateZip(parent);\r
1406     }\r
1407 \r
1408     BeginFrame();\r
1409       SetContours(TreeShowContourOption);\r
1410       DrawTree(TheTree, New);\r
1411     EndFrame();\r
1412 \r
1413   }\r
1414 \r
1415   StatusMsg("Ready", TRUE);\r
1416 }\r
1417 \r
1418 \r
1419 /* ----------------------------------------------------------------------------\r
1420  * \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
1424  * \r
1425  * ----------------------------------------------------------------------------\r
1426  */\r
1427 \r
1428 ResetLabels(tree)\r
1429      Tree *tree;\r
1430 {\r
1431   Tree *child;\r
1432   \r
1433   SetNodeLabel(tree, tree->label.text);\r
1434   FOREACH_CHILD(child, tree)\r
1435     ResetLabels(child);\r
1436 }\r
1437 \r
1438 \r
1439 /* ----------------------------------------------------------------------------\r
1440  * \r
1441  *   SetupTree() handles the task of setting up the specified tree in \r
1442  *   the drawing area.\r
1443  * \r
1444  * ----------------------------------------------------------------------------\r
1445  */\r
1446 \r
1447 void\r
1448 SetupTree(tree)\r
1449      Tree *tree;\r
1450 {\r
1451   int width, height;\r
1452   int x_offset, y_offset;\r
1453   \r
1454   LayoutTree(tree);\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
1459   BeginFrame();\r
1460     SetContours(TreeShowContourOption);\r
1461     DrawTree(tree, New);\r
1462   EndFrame();\r
1463 }\r