2 * $XConsortium: Tree.c,v 1.45 94/04/17 20:13:20 kaleb Exp $
5 Copyright (c) 1990, 1994 X Consortium
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
14 The above copyright notice and this permission notice shall be included in
15 all copies or substantial portions of the Software.
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 Except as contained in this notice, the name of the X Consortium shall not be
25 used in advertising or otherwise to promote the sale, use or other dealings
26 in this Software without prior written authorization from the X Consortium.
28 * Copyright 1989 Prentice Hall
30 * Permission to use, copy, modify, and distribute this software for any
31 * purpose and without fee is hereby granted, provided that the above
32 * copyright notice appear in all copies and that both the copyright notice
33 * and this permission notice appear in supporting documentation.
35 * Prentice Hall and the authors disclaim all warranties with regard
36 * to this software, including all implied warranties of merchantability and
37 * fitness. In no event shall Prentice Hall or the authors be liable
38 * for any special, indirect or cosequential damages or any damages whatsoever
39 * resulting from loss of use, data or profits, whether in an action of
40 * contract, negligence or other tortious action, arising out of or in
41 * connection with the use or performance of this software.
43 * Authors: Jim Fulton, MIT X Consortium,
44 * based on a version by Douglas Young, Prentice Hall
46 * This widget is based on the Tree widget described on pages 397-419 of
47 * Douglas Young's book "The X Window System, Programming and Applications
48 * with Xt OSF/Motif Edition." The layout code has been rewritten to use
49 * additional blank space to make the structure of the graph easier to see
50 * as well as to support vertical trees.
53 #include <X11/IntrinsicP.h>
54 #include <X11/StringDefs.h>
55 #include <X11/Xaw/XawInit.h>
56 #include <X11/Xaw/Cardinals.h>
57 #include <X11/Xaw/TreeP.h>
59 #define IsHorizontal(tw) ((tw)->tree.gravity == WestGravity || \
60 (tw)->tree.gravity == EastGravity)
63 /* widget class method */
64 static void ClassInitialize();
65 static void Initialize();
66 static void ConstraintInitialize();
67 static void ConstraintDestroy();
68 static Boolean
ConstraintSetValues();
69 static void Destroy();
70 static Boolean
SetValues();
71 static XtGeometryResult
GeometryManager();
72 static void ChangeManaged();
73 static void Redisplay();
74 static XtGeometryResult
QueryGeometry();
76 /* utility routines */
77 static void insert_node();
78 static void delete_node();
79 static void layout_tree();
83 * resources of the tree itself
85 static XtResource resources
[] = {
86 { XtNautoReconfigure
, XtCAutoReconfigure
, XtRBoolean
, sizeof (Boolean
),
87 XtOffsetOf(TreeRec
, tree
.auto_reconfigure
), XtRImmediate
,
89 { XtNhSpace
, XtCHSpace
, XtRDimension
, sizeof (Dimension
),
90 XtOffsetOf(TreeRec
, tree
.hpad
), XtRImmediate
, (XtPointer
) 0 },
91 { XtNvSpace
, XtCVSpace
, XtRDimension
, sizeof (Dimension
),
92 XtOffsetOf(TreeRec
, tree
.vpad
), XtRImmediate
, (XtPointer
) 0 },
93 { XtNforeground
, XtCForeground
, XtRPixel
, sizeof (Pixel
),
94 XtOffsetOf(TreeRec
, tree
.foreground
), XtRString
,
96 { XtNlineWidth
, XtCLineWidth
, XtRDimension
, sizeof (Dimension
),
97 XtOffsetOf(TreeRec
, tree
.line_width
), XtRImmediate
, (XtPointer
) 0 },
98 { XtNgravity
, XtCGravity
, XtRGravity
, sizeof (XtGravity
),
99 XtOffsetOf(TreeRec
, tree
.gravity
), XtRImmediate
,
100 (XtPointer
) WestGravity
},
105 * resources that are attached to all children of the tree
107 static XtResource treeConstraintResources
[] = {
108 { XtNtreeParent
, XtCTreeParent
, XtRWidget
, sizeof (Widget
),
109 XtOffsetOf(TreeConstraintsRec
, tree
.parent
), XtRImmediate
, NULL
},
110 { XtNtreeGC
, XtCTreeGC
, XtRGC
, sizeof(GC
),
111 XtOffsetOf(TreeConstraintsRec
, tree
.gc
), XtRImmediate
, NULL
},
115 TreeClassRec treeClassRec
= {
117 /* core_class fields */
118 (WidgetClass
) &constraintClassRec
, /* superclass */
119 "Tree", /* class_name */
120 sizeof(TreeRec
), /* widget_size */
121 ClassInitialize
, /* class_init */
122 NULL
, /* class_part_init */
123 FALSE
, /* class_inited */
124 Initialize
, /* initialize */
125 NULL
, /* initialize_hook */
126 XtInheritRealize
, /* realize */
129 resources
, /* resources */
130 XtNumber(resources
), /* num_resources */
131 NULLQUARK
, /* xrm_class */
132 TRUE
, /* compress_motion */
133 TRUE
, /* compress_exposure */
134 TRUE
, /* compress_enterleave*/
135 TRUE
, /* visible_interest */
136 Destroy
, /* destroy */
138 Redisplay
, /* expose */
139 SetValues
, /* set_values */
140 NULL
, /* set_values_hook */
141 XtInheritSetValuesAlmost
, /* set_values_almost */
142 NULL
, /* get_values_hook */
143 NULL
, /* accept_focus */
144 XtVersion
, /* version */
145 NULL
, /* callback_private */
147 QueryGeometry
, /* query_geometry */
148 NULL
, /* display_accelerator*/
149 NULL
, /* extension */
152 /* composite_class fields */
153 GeometryManager
, /* geometry_manager */
154 ChangeManaged
, /* change_managed */
155 XtInheritInsertChild
, /* insert_child */
156 XtInheritDeleteChild
, /* delete_child */
157 NULL
, /* extension */
160 /* constraint_class fields */
161 treeConstraintResources
, /* subresources */
162 XtNumber(treeConstraintResources
), /* subresource_count */
163 sizeof(TreeConstraintsRec
), /* constraint_size */
164 ConstraintInitialize
, /* initialize */
165 ConstraintDestroy
, /* destroy */
166 ConstraintSetValues
, /* set_values */
167 NULL
, /* extension */
170 /* Tree class fields */
175 WidgetClass treeWidgetClass
= (WidgetClass
) &treeClassRec
;
178 /*****************************************************************************
180 * tree utility routines *
182 *****************************************************************************/
184 static void initialize_dimensions (listp
, sizep
, n
)
193 *listp
= (Dimension
*) XtCalloc ((unsigned int) n
,
194 (unsigned int) sizeof(Dimension
));
195 *sizep
= ((*listp
) ? n
: 0);
199 *listp
= (Dimension
*) XtRealloc((char *) *listp
,
200 (unsigned int) (n
*sizeof(Dimension
)));
205 for (i
= *sizep
, l
= (*listp
) + i
; i
< n
; i
++, l
++) *l
= 0;
211 static GC
get_tree_gc (w
)
214 XtGCMask valuemask
= GCBackground
| GCForeground
;
217 values
.background
= w
->core
.background_pixel
;
218 values
.foreground
= w
->tree
.foreground
;
219 if (w
->tree
.line_width
!= 0) {
220 valuemask
|= GCLineWidth
;
221 values
.line_width
= w
->tree
.line_width
;
224 return XtGetGC ((Widget
) w
, valuemask
, &values
);
227 static void insert_node (parent
, node
)
231 TreeConstraints nc
= TREE_CONSTRAINT(node
);
234 nc
->tree
.parent
= parent
;
236 if (parent
== NULL
) return;
239 * If there isn't more room in the children array,
240 * allocate additional space.
242 pc
= TREE_CONSTRAINT(parent
);
243 nindex
= pc
->tree
.n_children
;
245 if (pc
->tree
.n_children
== pc
->tree
.max_children
) {
246 pc
->tree
.max_children
+= (pc
->tree
.max_children
/ 2) + 2;
247 pc
->tree
.children
= (WidgetList
) XtRealloc ((char *)pc
->tree
.children
,
249 ((pc
->tree
.max_children
) *
254 * Add the sub_node in the next available slot and
255 * increment the counter.
257 pc
->tree
.children
[nindex
] = node
;
258 pc
->tree
.n_children
++;
261 static void delete_node (parent
, node
)
268 * Make sure the parent exists.
272 pc
= TREE_CONSTRAINT(parent
);
275 * Find the sub_node on its parent's list.
277 for (pos
= 0; pos
< pc
->tree
.n_children
; pos
++)
278 if (pc
->tree
.children
[pos
] == node
) break;
280 if (pos
== pc
->tree
.n_children
) return;
283 * Decrement the number of children
285 pc
->tree
.n_children
--;
288 * Fill in the gap left by the sub_node.
289 * Zero the last slot for good luck.
291 for (i
= pos
; i
< pc
->tree
.n_children
; i
++)
292 pc
->tree
.children
[i
] = pc
->tree
.children
[i
+1];
294 pc
->tree
.children
[pc
->tree
.n_children
]=0;
297 static void check_gravity (tw
, grav
)
301 switch (tw
->tree
.gravity
) {
302 case WestGravity
: case NorthGravity
: case EastGravity
: case SouthGravity
:
305 tw
->tree
.gravity
= grav
;
311 /*****************************************************************************
313 * tree class methods *
315 *****************************************************************************/
317 static void ClassInitialize ()
319 XawInitializeWidgetSet();
320 XtAddConverter (XtRString
, XtRGravity
, XmuCvtStringToGravity
,
321 (XtConvertArgList
) NULL
, (Cardinal
) 0);
326 static void Initialize (grequest
, gnew
, args
, num_args
)
327 Widget grequest
, gnew
;
331 TreeWidget request
= (TreeWidget
) grequest
, new = (TreeWidget
) gnew
;
335 * Make sure the widget's width and height are
338 if ((int)request
->core
.width
<= 0) new->core
.width
= 5;
339 if ((int)request
->core
.height
<= 0) new->core
.height
= 5;
342 * Set the padding according to the orientation
344 if (request
->tree
.hpad
== 0 && request
->tree
.vpad
== 0) {
345 if (IsHorizontal (request
)) {
346 new->tree
.hpad
= TREE_HORIZONTAL_DEFAULT_SPACING
;
347 new->tree
.vpad
= TREE_VERTICAL_DEFAULT_SPACING
;
349 new->tree
.hpad
= TREE_VERTICAL_DEFAULT_SPACING
;
350 new->tree
.vpad
= TREE_HORIZONTAL_DEFAULT_SPACING
;
355 * Create a graphics context for the connecting lines.
357 new->tree
.gc
= get_tree_gc (new);
360 * Create the hidden root widget.
362 new->tree
.tree_root
= (Widget
) NULL
;
363 XtSetArg(arglist
[0], XtNwidth
, 1);
364 XtSetArg(arglist
[1], XtNheight
, 1);
365 new->tree
.tree_root
= XtCreateWidget ("root", widgetClass
, gnew
,
369 * Allocate the array used to hold the widest values per depth
371 new->tree
.largest
= NULL
;
372 new->tree
.n_largest
= 0;
373 initialize_dimensions (&new->tree
.largest
, &new->tree
.n_largest
,
377 * make sure that our gravity is one of the acceptable values
379 check_gravity (new, WestGravity
);
384 static void ConstraintInitialize (request
, new, args
, num_args
)
389 TreeConstraints tc
= TREE_CONSTRAINT(new);
390 TreeWidget tw
= (TreeWidget
) new->core
.parent
;
393 * Initialize the widget to have no sub-nodes.
395 tc
->tree
.n_children
= 0;
396 tc
->tree
.max_children
= 0;
397 tc
->tree
.children
= (Widget
*) NULL
;
398 tc
->tree
.x
= tc
->tree
.y
= 0;
399 tc
->tree
.bbsubwidth
= 0;
400 tc
->tree
.bbsubheight
= 0;
404 * If this widget has a super-node, add it to that
405 * widget' sub-nodes list. Otherwise make it a sub-node of
406 * the tree_root widget.
409 insert_node (tc
->tree
.parent
, new);
410 else if (tw
->tree
.tree_root
)
411 insert_node (tw
->tree
.tree_root
, new);
416 static Boolean
SetValues (gcurrent
, grequest
, gnew
, args
, num_args
)
417 Widget gcurrent
, grequest
, gnew
;
421 TreeWidget current
= (TreeWidget
) gcurrent
, new = (TreeWidget
) gnew
;
422 Boolean redraw
= FALSE
;
425 * If the foreground color has changed, redo the GC's
426 * and indicate a redraw.
428 if (new->tree
.foreground
!= current
->tree
.foreground
||
429 new->core
.background_pixel
!= current
->core
.background_pixel
||
430 new->tree
.line_width
!= current
->tree
.line_width
) {
431 XtReleaseGC (gnew
, new->tree
.gc
);
432 new->tree
.gc
= get_tree_gc (new);
437 * If the minimum spacing has changed, recalculate the
438 * tree layout. layout_tree() does a redraw, so we don't
439 * need SetValues to do another one.
441 if (new->tree
.gravity
!= current
->tree
.gravity
) {
442 check_gravity (new, current
->tree
.gravity
);
445 if (IsHorizontal(new) != IsHorizontal(current
)) {
446 if (new->tree
.vpad
== current
->tree
.vpad
&&
447 new->tree
.hpad
== current
->tree
.hpad
) {
448 new->tree
.vpad
= current
->tree
.hpad
;
449 new->tree
.hpad
= current
->tree
.vpad
;
453 if (new->tree
.vpad
!= current
->tree
.vpad
||
454 new->tree
.hpad
!= current
->tree
.hpad
||
455 new->tree
.gravity
!= current
->tree
.gravity
) {
456 layout_tree (new, TRUE
);
464 static Boolean
ConstraintSetValues (current
, request
, new, args
, num_args
)
465 Widget current
, request
, new;
469 TreeConstraints newc
= TREE_CONSTRAINT(new);
470 TreeConstraints curc
= TREE_CONSTRAINT(current
);
471 TreeWidget tw
= (TreeWidget
) new->core
.parent
;
474 * If the parent field has changed, remove the widget
475 * from the old widget's children list and add it to the
478 if (curc
->tree
.parent
!= newc
->tree
.parent
){
479 if (curc
->tree
.parent
)
480 delete_node (curc
->tree
.parent
, new);
481 if (newc
->tree
.parent
)
482 insert_node(newc
->tree
.parent
, new);
485 * If the Tree widget has been realized,
486 * compute new layout.
488 if (XtIsRealized((Widget
)tw
))
489 layout_tree (tw
, FALSE
);
495 static void ConstraintDestroy (w
)
498 TreeConstraints tc
= TREE_CONSTRAINT(w
);
499 TreeWidget tw
= (TreeWidget
) XtParent(w
);
503 * Remove the widget from its parent's sub-nodes list and
504 * make all this widget's sub-nodes sub-nodes of the parent.
507 if (tw
->tree
.tree_root
== w
) {
508 if (tc
->tree
.n_children
> 0)
509 tw
->tree
.tree_root
= tc
->tree
.children
[0];
511 tw
->tree
.tree_root
= NULL
;
514 delete_node (tc
->tree
.parent
, (Widget
) w
);
515 for (i
= 0; i
< tc
->tree
.n_children
; i
++)
516 insert_node (tc
->tree
.parent
, tc
->tree
.children
[i
]);
518 layout_tree ((TreeWidget
) (w
->core
.parent
), FALSE
);
522 static XtGeometryResult
GeometryManager (w
, request
, reply
)
524 XtWidgetGeometry
*request
;
525 XtWidgetGeometry
*reply
;
528 TreeWidget tw
= (TreeWidget
) w
->core
.parent
;
531 * No position changes allowed!.
533 if ((request
->request_mode
& CWX
&& request
->x
!=w
->core
.x
)
534 ||(request
->request_mode
& CWY
&& request
->y
!=w
->core
.y
))
535 return (XtGeometryNo
);
538 * Allow all resize requests.
541 if (request
->request_mode
& CWWidth
)
542 w
->core
.width
= request
->width
;
543 if (request
->request_mode
& CWHeight
)
544 w
->core
.height
= request
->height
;
545 if (request
->request_mode
& CWBorderWidth
)
546 w
->core
.border_width
= request
->border_width
;
548 if (tw
->tree
.auto_reconfigure
) layout_tree (tw
, FALSE
);
549 return (XtGeometryYes
);
552 static void ChangeManaged (gw
)
555 layout_tree ((TreeWidget
) gw
, FALSE
);
559 static void Destroy (gw
)
562 TreeWidget w
= (TreeWidget
) gw
;
564 XtReleaseGC (gw
, w
->tree
.gc
);
565 if (w
->tree
.largest
) XtFree ((char *) w
->tree
.largest
);
570 static void Redisplay (gw
, event
, region
)
575 TreeWidget tw
= (TreeWidget
) gw
;
578 * If the Tree widget is visible, visit each managed child.
580 if (tw
->core
.visible
) {
582 Display
*dpy
= XtDisplay (tw
);
583 Window w
= XtWindow (tw
);
585 for (i
= 0; i
< tw
->composite
.num_children
; i
++) {
586 Widget child
= tw
->composite
.children
[i
];
587 TreeConstraints tc
= TREE_CONSTRAINT(child
);
590 * Don't draw lines from the fake tree_root.
592 if (child
!= tw
->tree
.tree_root
&& tc
->tree
.n_children
) {
593 int srcx
= child
->core
.x
+ child
->core
.border_width
;
594 int srcy
= child
->core
.y
+ child
->core
.border_width
;
596 switch (tw
->tree
.gravity
) {
598 srcx
+= child
->core
.width
+ child
->core
.border_width
;
601 srcy
+= child
->core
.height
/ 2;
605 srcy
+= child
->core
.height
+ child
->core
.border_width
;
608 srcx
+= child
->core
.width
/ 2;
612 for (j
= 0; j
< tc
->tree
.n_children
; j
++) {
613 Widget k
= tc
->tree
.children
[j
];
614 GC gc
= (tc
->tree
.gc
? tc
->tree
.gc
: tw
->tree
.gc
);
616 switch (tw
->tree
.gravity
) {
619 * right center to left center
621 XDrawLine (dpy
, w
, gc
, srcx
, srcy
,
623 (k
->core
.y
+ ((int) k
->core
.border_width
) +
624 ((int) k
->core
.height
) / 2));
629 * bottom center to top center
631 XDrawLine (dpy
, w
, gc
, srcx
, srcy
,
632 (k
->core
.x
+ ((int) k
->core
.border_width
) +
633 ((int) k
->core
.width
) / 2),
639 * left center to right center
641 XDrawLine (dpy
, w
, gc
, srcx
, srcy
,
643 (((int) k
->core
.border_width
) << 1) +
644 (int) k
->core
.width
),
645 (k
->core
.y
+ ((int) k
->core
.border_width
) +
646 ((int) k
->core
.height
) / 2));
651 * top center to bottom center
653 XDrawLine (dpy
, w
, gc
, srcx
, srcy
,
654 (k
->core
.x
+ ((int) k
->core
.border_width
) +
655 ((int) k
->core
.width
) / 2),
657 (((int) k
->core
.border_width
) << 1) +
658 (int) k
->core
.height
));
667 static XtGeometryResult
QueryGeometry (w
, intended
, preferred
)
669 XtWidgetGeometry
*intended
, *preferred
;
671 TreeWidget tw
= (TreeWidget
) w
;
673 preferred
->request_mode
= (CWWidth
| CWHeight
);
674 preferred
->width
= tw
->tree
.maxwidth
;
675 preferred
->height
= tw
->tree
.maxheight
;
677 if (((intended
->request_mode
& (CWWidth
| CWHeight
)) ==
678 (CWWidth
| CWHeight
)) &&
679 intended
->width
== preferred
->width
&&
680 intended
->height
== preferred
->height
)
681 return XtGeometryYes
;
682 else if (preferred
->width
== w
->core
.width
&&
683 preferred
->height
== w
->core
.height
)
686 return XtGeometryAlmost
;
690 /*****************************************************************************
692 * tree layout algorithm *
694 * Each node in the tree is "shrink-wrapped" with a minimal bounding *
695 * rectangle, laid next to its siblings (with a small about of padding in *
696 * between) and then wrapped with their parent. Parents are centered about *
697 * their children (or vice versa if the parent is larger than the children). *
699 *****************************************************************************/
701 static void compute_bounding_box_subtree (tree
, w
, depth
)
706 TreeConstraints tc
= TREE_CONSTRAINT(w
); /* info attached to all kids */
708 Bool horiz
= IsHorizontal (tree
);
709 Dimension newwidth
, newheight
;
710 Dimension bw2
= w
->core
.border_width
* 2;
713 * Set the max-size per level.
715 if (depth
>= tree
->tree
.n_largest
) {
716 initialize_dimensions (&tree
->tree
.largest
,
717 &tree
->tree
.n_largest
, depth
+ 1);
719 newwidth
= ((horiz
? w
->core
.width
: w
->core
.height
) + bw2
);
720 if (tree
->tree
.largest
[depth
] < newwidth
)
721 tree
->tree
.largest
[depth
] = newwidth
;
727 tc
->tree
.bbwidth
= w
->core
.width
+ bw2
;
728 tc
->tree
.bbheight
= w
->core
.height
+ bw2
;
730 if (tc
->tree
.n_children
== 0) return;
733 * Figure the size of the opposite dimension (vertical if tree is
734 * horizontal, else vice versa). The other dimension will be set
735 * in the second pass once we know the maximum dimensions.
739 for (i
= 0; i
< tc
->tree
.n_children
; i
++) {
740 Widget child
= tc
->tree
.children
[i
];
741 TreeConstraints cc
= TREE_CONSTRAINT(child
);
743 compute_bounding_box_subtree (tree
, child
, depth
+ 1);
746 if (newwidth
< cc
->tree
.bbwidth
) newwidth
= cc
->tree
.bbwidth
;
747 newheight
+= tree
->tree
.vpad
+ cc
->tree
.bbheight
;
749 if (newheight
< cc
->tree
.bbheight
) newheight
= cc
->tree
.bbheight
;
750 newwidth
+= tree
->tree
.hpad
+ cc
->tree
.bbwidth
;
755 tc
->tree
.bbsubwidth
= newwidth
;
756 tc
->tree
.bbsubheight
= newheight
;
759 * Now fit parent onto side (or top) of bounding box and correct for
760 * extra padding. Be careful of unsigned arithmetic.
763 tc
->tree
.bbwidth
+= tree
->tree
.hpad
+ newwidth
;
764 newheight
-= tree
->tree
.vpad
;
765 if (newheight
> tc
->tree
.bbheight
) tc
->tree
.bbheight
= newheight
;
767 tc
->tree
.bbheight
+= tree
->tree
.vpad
+ newheight
;
768 newwidth
-= tree
->tree
.hpad
;
769 if (newwidth
> tc
->tree
.bbwidth
) tc
->tree
.bbwidth
= newwidth
;
774 static void set_positions (tw
, w
, level
)
782 TreeConstraints tc
= TREE_CONSTRAINT(w
);
786 * mirror if necessary
788 switch (tw
->tree
.gravity
) {
790 tc
->tree
.x
= (((Position
) tw
->tree
.maxwidth
) -
791 ((Position
) w
->core
.width
) - tc
->tree
.x
);
795 tc
->tree
.y
= (((Position
) tw
->tree
.maxheight
) -
796 ((Position
) w
->core
.height
) - tc
->tree
.y
);
801 * Move the widget into position.
803 XtMoveWidget (w
, tc
->tree
.x
, tc
->tree
.y
);
807 * Set the positions of all children.
809 for (i
= 0; i
< tc
->tree
.n_children
; i
++)
810 set_positions (tw
, tc
->tree
.children
[i
], level
+ 1);
815 static void arrange_subtree (tree
, w
, depth
, x
, y
)
821 TreeConstraints tc
= TREE_CONSTRAINT(w
); /* info attached to all kids */
822 TreeConstraints firstcc
, lastcc
;
825 Bool horiz
= IsHorizontal (tree
);
828 Dimension bw2
= w
->core
.border_width
* 2;
829 Bool relayout
= True
;
833 * If no children, then just lay out where requested.
839 int myh
= (w
->core
.height
+ bw2
);
841 if (myh
> (int)tc
->tree
.bbsubheight
) {
842 y
+= (myh
- (int)tc
->tree
.bbsubheight
) / 2;
846 int myw
= (w
->core
.width
+ bw2
);
848 if (myw
> (int)tc
->tree
.bbsubwidth
) {
849 x
+= (myw
- (int)tc
->tree
.bbsubwidth
) / 2;
854 if ((tmp
= ((Dimension
) x
) + tc
->tree
.bbwidth
) > tree
->tree
.maxwidth
)
855 tree
->tree
.maxwidth
= tmp
;
856 if ((tmp
= ((Dimension
) y
) + tc
->tree
.bbheight
) > tree
->tree
.maxheight
)
857 tree
->tree
.maxheight
= tmp
;
859 if (tc
->tree
.n_children
== 0) return;
863 * Have children, so walk down tree laying out children, then laying
867 newx
= x
+ tree
->tree
.largest
[depth
];
868 if (depth
> 0) newx
+= tree
->tree
.hpad
;
872 newy
= y
+ tree
->tree
.largest
[depth
];
873 if (depth
> 0) newy
+= tree
->tree
.vpad
;
876 for (i
= 0; i
< tc
->tree
.n_children
; i
++) {
879 child
= tc
->tree
.children
[i
]; /* last value is used outside loop */
880 cc
= TREE_CONSTRAINT(child
);
882 arrange_subtree (tree
, child
, depth
+ 1, newx
, newy
);
884 newy
+= tree
->tree
.vpad
+ cc
->tree
.bbheight
;
886 newx
+= tree
->tree
.hpad
+ cc
->tree
.bbwidth
;
891 * now layout parent between first and last children
895 firstcc
= TREE_CONSTRAINT (tc
->tree
.children
[0]);
896 lastcc
= TREE_CONSTRAINT (child
);
898 /* Adjustments are disallowed if they result in a position above
899 * or to the left of the originally requested position, because
900 * this could collide with the position of the previous sibling.
904 adjusted
= firstcc
->tree
.y
+
905 ((lastcc
->tree
.y
+ (Position
) child
->core
.height
+
906 (Position
) child
->core
.border_width
* 2 -
907 firstcc
->tree
.y
- (Position
) w
->core
.height
-
908 (Position
) w
->core
.border_width
* 2 + 1) / 2);
909 if (adjusted
> tc
->tree
.y
) tc
->tree
.y
= adjusted
;
911 adjusted
= firstcc
->tree
.x
+
912 ((lastcc
->tree
.x
+ (Position
) child
->core
.width
+
913 (Position
) child
->core
.border_width
* 2 -
914 firstcc
->tree
.x
- (Position
) w
->core
.width
-
915 (Position
) w
->core
.border_width
* 2 + 1) / 2);
916 if (adjusted
> tc
->tree
.x
) tc
->tree
.x
= adjusted
;
922 static void set_tree_size (tw
, insetvalues
, width
, height
)
925 Dimension width
, height
;
928 tw
->core
.width
= width
;
929 tw
->core
.height
= height
;
931 Dimension replyWidth
= 0, replyHeight
= 0;
932 XtGeometryResult result
= XtMakeResizeRequest ((Widget
) tw
,
937 * Accept any compromise.
939 if (result
== XtGeometryAlmost
)
940 XtMakeResizeRequest ((Widget
) tw
, replyWidth
, replyHeight
,
941 (Dimension
*) NULL
, (Dimension
*) NULL
);
946 static void layout_tree (tw
, insetvalues
)
954 * Do a depth-first search computing the width and height of the bounding
955 * box for the tree at that position (and below). Then, walk again using
956 * this information to layout the children at each level.
959 if (tw
->tree
.tree_root
== NULL
)
962 tw
->tree
.maxwidth
= tw
->tree
.maxheight
= 0;
963 for (i
= 0, dp
= tw
->tree
.largest
; i
< tw
->tree
.n_largest
; i
++, dp
++)
965 initialize_dimensions (&tw
->tree
.largest
, &tw
->tree
.n_largest
,
967 compute_bounding_box_subtree (tw
, tw
->tree
.tree_root
, 0);
970 * Second pass to do final layout. Each child's bounding box is stacked
971 * on top of (if horizontal, else next to) on top of its siblings. The
972 * parent is centered between the first and last children.
974 arrange_subtree (tw
, tw
->tree
.tree_root
, 0, 0, 0);
977 * Move each widget into place.
979 set_tree_size (tw
, insetvalues
, tw
->tree
.maxwidth
, tw
->tree
.maxheight
);
980 set_positions (tw
, tw
->tree
.tree_root
, 0);
985 if (XtIsRealized ((Widget
) tw
)) {
986 XClearArea (XtDisplay(tw
), XtWindow((Widget
)tw
), 0, 0, 0, 0, True
);
992 /*****************************************************************************
996 *****************************************************************************/
999 #if NeedFunctionPrototypes
1000 XawTreeForceLayout (Widget tree
)
1002 XawTreeForceLayout (tree
)
1006 layout_tree ((TreeWidget
) tree
, FALSE
);