Tree.c revision efbcb2bf
1/*
2
3Copyright 1990, 1994, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25 * Copyright 1989 Prentice Hall
26 *
27 * Permission to use, copy, modify, and distribute this software for any
28 * purpose and without fee is hereby granted, provided that the above
29 * copyright notice appear in all copies and that both the copyright notice
30 * and this permission notice appear in supporting documentation.
31 *
32 * Prentice Hall and the authors disclaim all warranties with regard
33 * to this software, including all implied warranties of merchantability and
34 * fitness.  In no event shall Prentice Hall or the authors be liable
35 * for any special, indirect or cosequential damages or any damages whatsoever
36 * resulting from loss of use, data or profits, whether in an action of
37 * contract, negligence or other tortious action, arising out of or in
38 * connection with the use or performance of this software.
39 *
40 * Authors:  Jim Fulton, MIT X Consortium,
41 *           based on a version by Douglas Young, Prentice Hall
42 *
43 * This widget is based on the Tree widget described on pages 397-419 of
44 * Douglas Young's book "The X Window System, Programming and Applications
45 * with Xt OSF/Motif Edition."  The layout code has been rewritten to use
46 * additional blank space to make the structure of the graph easier to see
47 * as well as to support vertical trees.
48 */
49
50#ifdef HAVE_CONFIG_H
51#include <config.h>
52#endif
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>
58#include "Private.h"
59
60#define IsHorizontal(tw) ((tw)->tree.gravity == WestGravity || \
61			  (tw)->tree.gravity == EastGravity)
62
63/*
64 * Class Methods
65 */
66static void XawTreeChangeManaged(Widget);
67static void XawTreeClassInitialize(void);
68static void XawTreeConstraintDestroy(Widget);
69static void XawTreeConstraintInitialize(Widget, Widget, ArgList, Cardinal*);
70static Boolean XawTreeConstraintSetValues(Widget, Widget, Widget,
71					  ArgList, Cardinal*);
72static void XawTreeDestroy(Widget);
73static XtGeometryResult XawTreeGeometryManager(Widget, XtWidgetGeometry*,
74					       XtWidgetGeometry*);
75static void XawTreeInitialize(Widget, Widget, ArgList, Cardinal*);
76static XtGeometryResult XawTreeQueryGeometry(Widget, XtWidgetGeometry*,
77					     XtWidgetGeometry*);
78static void XawTreeRedisplay(Widget, XEvent*, Region);
79static Boolean XawTreeSetValues(Widget, Widget, Widget, ArgList, Cardinal*);
80
81/*
82 * Prototypes
83 */
84static void arrange_subtree(TreeWidget, Widget, int, int, int);
85static void check_gravity(TreeWidget, XtGravity);
86static void compute_bounding_box_subtree(TreeWidget, Widget, int);
87static void delete_node(Widget, Widget);
88static GC get_tree_gc(TreeWidget);
89static void initialize_dimensions(Dimension**, int*, int);
90static void insert_node(Widget, Widget);
91static void layout_tree(TreeWidget, Bool);
92static void set_positions(TreeWidget, Widget, int);
93static void set_tree_size(TreeWidget, Bool, unsigned int, unsigned int);
94
95/*
96 * Initialization
97 */
98
99/*
100 * resources of the tree itself
101 */
102static XtResource resources[] = {
103    { XtNautoReconfigure, XtCAutoReconfigure, XtRBoolean, sizeof (Boolean),
104	XtOffsetOf(TreeRec, tree.auto_reconfigure), XtRImmediate,
105	(XtPointer) FALSE },
106    { XtNhSpace, XtCHSpace, XtRDimension, sizeof (Dimension),
107	XtOffsetOf(TreeRec, tree.hpad), XtRImmediate, (XtPointer) 0 },
108    { XtNvSpace, XtCVSpace, XtRDimension, sizeof (Dimension),
109	XtOffsetOf(TreeRec, tree.vpad), XtRImmediate, (XtPointer) 0 },
110    { XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
111	XtOffsetOf(TreeRec, tree.foreground), XtRString,
112	(XtPointer) XtDefaultForeground},
113    { XtNlineWidth, XtCLineWidth, XtRDimension, sizeof (Dimension),
114	XtOffsetOf(TreeRec, tree.line_width), XtRImmediate, (XtPointer) 0 },
115    { XtNgravity, XtCGravity, XtRGravity, sizeof (XtGravity),
116	XtOffsetOf(TreeRec, tree.gravity), XtRImmediate,
117	(XtPointer) WestGravity },
118#ifndef OLDXAW
119    { XawNdisplayList, XawCDisplayList, XawRDisplayList, sizeof(XawDisplayList*),
120	XtOffsetOf(TreeRec, tree.display_list), XtRImmediate,
121	NULL },
122#endif
123};
124
125
126/*
127 * resources that are attached to all children of the tree
128 */
129static XtResource treeConstraintResources[] = {
130    { XtNtreeParent, XtCTreeParent, XtRWidget, sizeof (Widget),
131	XtOffsetOf(TreeConstraintsRec, tree.parent), XtRImmediate, NULL },
132    { XtNtreeGC, XtCTreeGC, XtRGC, sizeof(GC),
133	XtOffsetOf(TreeConstraintsRec, tree.gc), XtRImmediate, NULL },
134};
135
136
137TreeClassRec treeClassRec = {
138  {
139					/* core_class fields  */
140    (WidgetClass) &constraintClassRec,	/* superclass         */
141    "Tree",				/* class_name         */
142    sizeof(TreeRec),			/* widget_size        */
143    XawTreeClassInitialize,		/* class_init         */
144    NULL,				/* class_part_init    */
145    FALSE,				/* class_inited       */
146    XawTreeInitialize,			/* initialize         */
147    NULL,				/* initialize_hook    */
148    XtInheritRealize,			/* realize            */
149    NULL,				/* actions            */
150    0,					/* num_actions        */
151    resources,				/* resources          */
152    XtNumber(resources),		/* num_resources      */
153    NULLQUARK,				/* xrm_class          */
154    TRUE,				/* compress_motion    */
155    TRUE,				/* compress_exposure  */
156    TRUE,				/* compress_enterleave*/
157    TRUE,				/* visible_interest   */
158    XawTreeDestroy,			/* destroy            */
159    NULL,				/* resize             */
160    XawTreeRedisplay,			/* expose             */
161    XawTreeSetValues,			/* set_values         */
162    NULL,				/* set_values_hook    */
163    XtInheritSetValuesAlmost,		/* set_values_almost  */
164    NULL,				/* get_values_hook    */
165    NULL,				/* accept_focus       */
166    XtVersion,				/* version            */
167    NULL,				/* callback_private   */
168    NULL,				/* tm_table           */
169    XawTreeQueryGeometry,		/* query_geometry     */
170    NULL,				/* display_accelerator*/
171    NULL,				/* extension          */
172  },
173  {
174					/* composite_class fields */
175    XawTreeGeometryManager,		/* geometry_manager    */
176    XawTreeChangeManaged,		/* change_managed      */
177    XtInheritInsertChild,		/* insert_child        */
178    XtInheritDeleteChild,		/* delete_child        */
179    NULL,				/* extension           */
180  },
181  {
182					/* constraint_class fields */
183   treeConstraintResources,		/* subresources        */
184   XtNumber(treeConstraintResources),	/* subresource_count   */
185   sizeof(TreeConstraintsRec),		/* constraint_size     */
186   XawTreeConstraintInitialize,		/* initialize          */
187   XawTreeConstraintDestroy,		/* destroy             */
188   XawTreeConstraintSetValues,		/* set_values          */
189   NULL,				/* extension           */
190   },
191  {
192					/* Tree class fields */
193    NULL,				/* ignore              */
194  }
195};
196
197WidgetClass treeWidgetClass = (WidgetClass) &treeClassRec;
198
199
200/*****************************************************************************
201 *                                                                           *
202 *			     tree utility routines                           *
203 *                                                                           *
204 *****************************************************************************/
205
206static void
207initialize_dimensions(Dimension **listp, int *sizep, int n)
208{
209    if (!*listp) {
210	*listp = (Dimension *) XtCalloc ((unsigned int) n,
211					 (unsigned int) sizeof(Dimension));
212	*sizep = ((*listp) ? n : 0);
213	return;
214    }
215    if (n > *sizep) {
216	*listp = (Dimension *) XtRealloc((char *) *listp,
217					 (Cardinal) ((size_t)n*sizeof(Dimension)));
218	if (!*listp) {
219	    *sizep = 0;
220	    return;
221	} else {
222	    int i;
223	    Dimension *l;
224
225	    for (i = *sizep, l = (*listp) + i; i < n; i++, l++)
226		*l = 0;
227	    *sizep = n;
228	}
229    }
230    return;
231}
232
233static GC
234get_tree_gc(TreeWidget w)
235{
236    XtGCMask valuemask = GCBackground | GCForeground;
237    XGCValues values;
238
239    values.background = w->core.background_pixel;
240    values.foreground = w->tree.foreground;
241    if (w->tree.line_width != 0) {
242	valuemask |= GCLineWidth;
243	values.line_width = w->tree.line_width;
244    }
245
246    return XtGetGC ((Widget) w, valuemask, &values);
247}
248
249static void
250insert_node(Widget parent, Widget node)
251{
252    TreeConstraints pc;
253    TreeConstraints nc = TREE_CONSTRAINT(node);
254    int nindex;
255
256    nc->tree.parent = parent;
257
258    if (parent == NULL) return;
259
260    /*
261     * If there isn't more room in the children array,
262     * allocate additional space.
263     */
264    pc = TREE_CONSTRAINT(parent);
265    nindex = pc->tree.n_children;
266
267    if (pc->tree.n_children == pc->tree.max_children) {
268	pc->tree.max_children += (pc->tree.max_children / 2) + 2;
269	pc->tree.children = (WidgetList) XtRealloc ((char *)pc->tree.children,
270						    (Cardinal)
271						    ((size_t)pc->tree.max_children *
272						    sizeof(Widget)));
273    }
274
275    /*
276     * Add the sub_node in the next available slot and
277     * increment the counter.
278     */
279    pc->tree.children[nindex] = node;
280    pc->tree.n_children++;
281}
282
283static void
284delete_node(Widget parent, Widget node)
285{
286    TreeConstraints pc;
287    int pos;
288    int i;
289
290    /*
291     * Make sure the parent exists.
292     */
293    if (!parent) return;
294
295    pc = TREE_CONSTRAINT(parent);
296
297    /*
298     * Find the sub_node on its parent's list.
299     */
300    for (pos = 0; pos < pc->tree.n_children; pos++)
301      if (pc->tree.children[pos] == node) break;
302
303    if (pos == pc->tree.n_children) return;
304
305    /*
306     * Decrement the number of children
307     */
308    pc->tree.n_children--;
309
310    /*
311     * Fill in the gap left by the sub_node.
312     * Zero the last slot for good luck.
313     */
314    for (i = pos; i < pc->tree.n_children; i++)
315      pc->tree.children[i] = pc->tree.children[i+1];
316
317    pc->tree.children[pc->tree.n_children] = NULL;
318}
319
320static void
321check_gravity(TreeWidget tw, XtGravity grav)
322{
323    switch (tw->tree.gravity) {
324      case WestGravity: case NorthGravity: case EastGravity: case SouthGravity:
325	break;
326      default:
327	tw->tree.gravity = grav;
328	break;
329    }
330}
331
332
333/*****************************************************************************
334 *                                                                           *
335 *			      tree class methods                             *
336 *                                                                           *
337 *****************************************************************************/
338
339static void
340XawTreeClassInitialize(void)
341{
342    XawInitializeWidgetSet();
343  XtAddConverter(XtRString, XtRGravity, XmuCvtStringToGravity, NULL, 0);
344  XtSetTypeConverter(XtRGravity, XtRString, XmuCvtGravityToString,
345		     NULL, 0, XtCacheNone, NULL);
346}
347
348
349/*ARGSUSED*/
350static void
351XawTreeInitialize(Widget grequest, Widget gnew,
352		  ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
353{
354    TreeWidget request = (TreeWidget) grequest, cnew = (TreeWidget) gnew;
355    Arg arglist[2];
356
357    /*
358     * Make sure the widget's width and height are
359     * greater than zero.
360     */
361    if (request->core.width <= 0) cnew->core.width = 5;
362    if (request->core.height <= 0) cnew->core.height = 5;
363
364    /*
365     * Set the padding according to the orientation
366     */
367    if (request->tree.hpad == 0 && request->tree.vpad == 0) {
368	if (IsHorizontal (request)) {
369	    cnew->tree.hpad = TREE_HORIZONTAL_DEFAULT_SPACING;
370	    cnew->tree.vpad = TREE_VERTICAL_DEFAULT_SPACING;
371	} else {
372	    cnew->tree.hpad = TREE_VERTICAL_DEFAULT_SPACING;
373	    cnew->tree.vpad = TREE_HORIZONTAL_DEFAULT_SPACING;
374	}
375    }
376
377    /*
378     * Create a graphics context for the connecting lines.
379     */
380    cnew->tree.gc = get_tree_gc (cnew);
381
382    /*
383     * Create the hidden root widget.
384     */
385    cnew->tree.tree_root = (Widget) NULL;
386    XtSetArg(arglist[0], XtNwidth, 1);
387    XtSetArg(arglist[1], XtNheight, 1);
388    cnew->tree.tree_root = XtCreateWidget ("root", widgetClass, gnew,
389					  arglist,TWO);
390
391    /*
392     * Allocate the array used to hold the widest values per depth
393     */
394    cnew->tree.largest = NULL;
395    cnew->tree.n_largest = 0;
396    initialize_dimensions (&cnew->tree.largest, &cnew->tree.n_largest,
397			   TREE_INITIAL_DEPTH);
398
399    /*
400     * make sure that our gravity is one of the acceptable values
401     */
402    check_gravity (cnew, WestGravity);
403}
404
405
406/* ARGSUSED */
407static void
408XawTreeConstraintInitialize(Widget request _X_UNUSED, Widget cnew,
409			    ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
410{
411    TreeConstraints tc = TREE_CONSTRAINT(cnew);
412    TreeWidget tw = (TreeWidget) cnew->core.parent;
413
414    /*
415     * Initialize the widget to have no sub-nodes.
416     */
417    tc->tree.n_children = 0;
418    tc->tree.max_children = 0;
419    tc->tree.children = (Widget *) NULL;
420    tc->tree.x = tc->tree.y = 0;
421    tc->tree.bbsubwidth = 0;
422    tc->tree.bbsubheight = 0;
423
424
425    /*
426     * If this widget has a super-node, add it to that
427     * widget' sub-nodes list. Otherwise make it a sub-node of
428     * the tree_root widget.
429     */
430    if (tc->tree.parent)
431      insert_node (tc->tree.parent, cnew);
432    else if (tw->tree.tree_root)
433      insert_node (tw->tree.tree_root, cnew);
434}
435
436
437/* ARGSUSED */
438static Boolean
439XawTreeSetValues(Widget gcurrent, Widget grequest _X_UNUSED, Widget gnew,
440		 ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
441{
442    TreeWidget current = (TreeWidget) gcurrent, cnew = (TreeWidget) gnew;
443    Boolean redraw = FALSE;
444
445    /*
446     * If the foreground color has changed, redo the GC's
447     * and indicate a redraw.
448     */
449    if (cnew->tree.foreground != current->tree.foreground ||
450	cnew->core.background_pixel != current->core.background_pixel ||
451	cnew->tree.line_width != current->tree.line_width) {
452	XtReleaseGC (gnew, cnew->tree.gc);
453	cnew->tree.gc = get_tree_gc (cnew);
454	redraw = TRUE;
455    }
456
457    /*
458     * If the minimum spacing has changed, recalculate the
459     * tree layout. layout_tree() does a redraw, so we don't
460     * need SetValues to do another one.
461     */
462    if (cnew->tree.gravity != current->tree.gravity) {
463	check_gravity (cnew, current->tree.gravity);
464    }
465
466    if (IsHorizontal(cnew) != IsHorizontal(current)) {
467	if (cnew->tree.vpad == current->tree.vpad &&
468	    cnew->tree.hpad == current->tree.hpad) {
469	    cnew->tree.vpad = current->tree.hpad;
470	    cnew->tree.hpad = current->tree.vpad;
471	}
472    }
473
474    if (cnew->tree.vpad != current->tree.vpad ||
475	cnew->tree.hpad != current->tree.hpad ||
476	cnew->tree.gravity != current->tree.gravity) {
477	layout_tree (cnew, TRUE);
478	redraw = FALSE;
479    }
480    return redraw;
481}
482
483
484/* ARGSUSED */
485static Boolean
486XawTreeConstraintSetValues(Widget current, Widget request _X_UNUSED, Widget cnew,
487			   ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
488{
489    TreeConstraints newc = TREE_CONSTRAINT(cnew);
490    TreeConstraints curc = TREE_CONSTRAINT(current);
491    TreeWidget tw = (TreeWidget) cnew->core.parent;
492
493    /*
494     * If the parent field has changed, remove the widget
495     * from the old widget's children list and add it to the
496     * new one.
497     */
498    if (curc->tree.parent != newc->tree.parent){
499	if (curc->tree.parent)
500	  delete_node (curc->tree.parent, cnew);
501	if (newc->tree.parent)
502	  insert_node(newc->tree.parent, cnew);
503
504	/*
505	 * If the Tree widget has been realized,
506	 * compute new layout.
507	 */
508	if (XtIsRealized((Widget)tw))
509	  layout_tree (tw, FALSE);
510    }
511    return False;
512}
513
514
515static void
516XawTreeConstraintDestroy(Widget w)
517{
518    TreeConstraints tc = TREE_CONSTRAINT(w);
519    TreeWidget tw = (TreeWidget) XtParent(w);
520    int i;
521
522    /*
523     * Remove the widget from its parent's sub-nodes list and
524     * make all this widget's sub-nodes sub-nodes of the parent.
525     */
526
527    if (tw->tree.tree_root == w) {
528	if (tc->tree.n_children > 0)
529	  tw->tree.tree_root = tc->tree.children[0];
530	else
531	  tw->tree.tree_root = NULL;
532    }
533
534    delete_node (tc->tree.parent, (Widget) w);
535    for (i = 0; i< tc->tree.n_children; i++)
536      insert_node (tc->tree.parent, tc->tree.children[i]);
537
538    layout_tree ((TreeWidget) (w->core.parent), FALSE);
539}
540
541/* ARGSUSED */
542static XtGeometryResult
543XawTreeGeometryManager(Widget w, XtWidgetGeometry *request,
544		       XtWidgetGeometry *reply _X_UNUSED)
545{
546
547    TreeWidget tw = (TreeWidget) w->core.parent;
548
549    /*
550     * No position changes allowed!.
551     */
552    if ((request->request_mode & CWX && request->x!=w->core.x)
553	||(request->request_mode & CWY && request->y!=w->core.y))
554      return (XtGeometryNo);
555
556    /*
557     * Allow all resize requests.
558     */
559
560    if (request->request_mode & CWWidth)
561      w->core.width = request->width;
562    if (request->request_mode & CWHeight)
563      w->core.height = request->height;
564    if (request->request_mode & CWBorderWidth)
565      w->core.border_width = request->border_width;
566
567    if (tw->tree.auto_reconfigure) layout_tree (tw, FALSE);
568    return (XtGeometryYes);
569}
570
571static void
572XawTreeChangeManaged(Widget gw)
573{
574    layout_tree ((TreeWidget) gw, FALSE);
575}
576
577
578static void
579XawTreeDestroy(Widget gw)
580{
581    TreeWidget w = (TreeWidget) gw;
582
583    XtReleaseGC (gw, w->tree.gc);
584    if (w->tree.largest) XtFree ((char *) w->tree.largest);
585}
586
587
588/* ARGSUSED */
589static void
590XawTreeRedisplay(Widget gw, XEvent *event _X_UNUSED, Region region _X_UNUSED)
591{
592    TreeWidget tw = (TreeWidget) gw;
593    Cardinal i;
594    int j;
595
596#ifndef OLDXAW
597    if (tw->tree.display_list)
598	XawRunDisplayList(gw, tw->tree.display_list, event, region);
599#endif
600
601    /*
602     * If the Tree widget is visible, visit each managed child.
603     */
604    if (tw->core.visible) {
605	Display *dpy = XtDisplay (tw);
606	Window w = XtWindow (tw);
607
608	for (i = 0; i < tw->composite.num_children; i++) {
609	    Widget child = tw->composite.children[i];
610	    TreeConstraints tc = TREE_CONSTRAINT(child);
611
612	    /*
613	     * Don't draw lines from the fake tree_root.
614	     */
615	    if (child != tw->tree.tree_root && tc->tree.n_children) {
616		int srcx = child->core.x + child->core.border_width;
617		int srcy = child->core.y + child->core.border_width;
618
619		switch (tw->tree.gravity) {
620		  case WestGravity:
621		    srcx += child->core.width + child->core.border_width;
622		    /* fall through */
623		  case EastGravity:
624		    srcy += child->core.height / 2;
625		    break;
626
627		  case NorthGravity:
628		    srcy += child->core.height + child->core.border_width;
629		    /* fall through */
630		  case SouthGravity:
631		    srcx += child->core.width / 2;
632		    break;
633		}
634
635		for (j = 0; j < tc->tree.n_children; j++) {
636		    Widget k = tc->tree.children[j];
637		    GC gc = (tc->tree.gc ? tc->tree.gc : tw->tree.gc);
638
639		    switch (tw->tree.gravity) {
640		      case WestGravity:
641			/*
642			 * right center to left center
643			 */
644			XDrawLine (dpy, w, gc, srcx, srcy,
645				   (int) k->core.x,
646				   (k->core.y + ((int) k->core.border_width) +
647				    ((int) k->core.height) / 2));
648			break;
649
650		      case NorthGravity:
651			/*
652			 * bottom center to top center
653			 */
654			XDrawLine (dpy, w, gc, srcx, srcy,
655				   (k->core.x + ((int) k->core.border_width) +
656				    ((int) k->core.width) / 2),
657				   (int) k->core.y);
658			break;
659
660		      case EastGravity:
661			/*
662			 * left center to right center
663			 */
664			XDrawLine (dpy, w, gc, srcx, srcy,
665				   (k->core.x +
666				    (((int) k->core.border_width) << 1) +
667				    (int) k->core.width),
668				   (k->core.y + ((int) k->core.border_width) +
669				    ((int) k->core.height) / 2));
670			break;
671
672		      case SouthGravity:
673			/*
674			 * top center to bottom center
675			 */
676			XDrawLine (dpy, w, gc, srcx, srcy,
677				   (k->core.x + ((int) k->core.border_width) +
678				    ((int) k->core.width) / 2),
679				   (k->core.y +
680				    (((int) k->core.border_width) << 1) +
681				    (int) k->core.height));
682			break;
683		    }
684		}
685	    }
686	}
687    }
688}
689
690static XtGeometryResult
691XawTreeQueryGeometry(Widget w, XtWidgetGeometry *intended,
692		     XtWidgetGeometry *preferred)
693{
694    TreeWidget tw = (TreeWidget) w;
695
696    preferred->request_mode = (CWWidth | CWHeight);
697    preferred->width = tw->tree.maxwidth;
698    preferred->height = tw->tree.maxheight;
699
700    if (((intended->request_mode & (CWWidth | CWHeight)) ==
701	 (CWWidth | CWHeight)) &&
702	intended->width == preferred->width &&
703	intended->height == preferred->height)
704      return XtGeometryYes;
705    else if (preferred->width == w->core.width &&
706             preferred->height == w->core.height)
707      return XtGeometryNo;
708    else
709      return XtGeometryAlmost;
710}
711
712
713/*****************************************************************************
714 *                                                                           *
715 *			     tree layout algorithm                           *
716 *                                                                           *
717 * Each node in the tree is "shrink-wrapped" with a minimal bounding         *
718 * rectangle, laid next to its siblings (with a small about of padding in    *
719 * between) and then wrapped with their parent.  Parents are centered about  *
720 * their children (or vice versa if the parent is larger than the children). *
721 *                                                                           *
722 *****************************************************************************/
723
724static void
725compute_bounding_box_subtree(TreeWidget tree, Widget w, int depth)
726{
727    TreeConstraints tc = TREE_CONSTRAINT(w);  /* info attached to all kids */
728    Bool horiz = IsHorizontal (tree);
729    Dimension newwidth, newheight;
730    Dimension bw2 = (Dimension)(w->core.border_width * 2);
731    int i;
732
733    /*
734     * Set the max-size per level.
735     */
736    if (depth >= tree->tree.n_largest) {
737	initialize_dimensions (&tree->tree.largest,
738			       &tree->tree.n_largest, depth + 1);
739    }
740    newwidth = (Dimension)((horiz ? w->core.width : w->core.height) + bw2);
741    if (tree->tree.largest[depth] < newwidth)
742      tree->tree.largest[depth] = newwidth;
743
744
745    /*
746     * initialize
747     */
748    tc->tree.bbwidth = (Dimension)(w->core.width + bw2);
749    tc->tree.bbheight = (Dimension)(w->core.height + bw2);
750
751    if (tc->tree.n_children == 0) return;
752
753    /*
754     * Figure the size of the opposite dimension (vertical if tree is
755     * horizontal, else vice versa).  The other dimension will be set
756     * in the second pass once we know the maximum dimensions.
757     */
758    newwidth = 0;
759    newheight = 0;
760    for (i = 0; i < tc->tree.n_children; i++) {
761	Widget child = tc->tree.children[i];
762	TreeConstraints cc = TREE_CONSTRAINT(child);
763
764	compute_bounding_box_subtree (tree, child, depth + 1);
765
766	if (horiz) {
767	    if (newwidth < cc->tree.bbwidth) newwidth = cc->tree.bbwidth;
768	    newheight = (Dimension)(newheight + (tree->tree.vpad + cc->tree.bbheight));
769	} else {
770	    if (newheight < cc->tree.bbheight) newheight = cc->tree.bbheight;
771	    newwidth = (Dimension)(newwidth + (tree->tree.hpad + cc->tree.bbwidth));
772	}
773    }
774
775
776    tc->tree.bbsubwidth = newwidth;
777    tc->tree.bbsubheight = newheight;
778
779    /*
780     * Now fit parent onto side (or top) of bounding box and correct for
781     * extra padding.  Be careful of unsigned arithmetic.
782     */
783    if (horiz) {
784	tc->tree.bbwidth = (Dimension)(tc->tree.bbwidth + (tree->tree.hpad + newwidth));
785	newheight = (Dimension)(newheight - tree->tree.vpad);
786	if (newheight > tc->tree.bbheight) tc->tree.bbheight = newheight;
787    } else {
788	tc->tree.bbheight = (Dimension)(tc->tree.bbheight + (tree->tree.vpad + newheight));
789	newwidth = (Dimension)(newwidth - tree->tree.hpad);
790	if (newwidth > tc->tree.bbwidth) tc->tree.bbwidth = newwidth;
791    }
792}
793
794
795static void
796set_positions(TreeWidget tw, Widget w, int level)
797{
798    if (w) {
799	TreeConstraints tc = TREE_CONSTRAINT(w);
800	int i;
801
802	if (level > 0) {
803	    /*
804	     * mirror if necessary
805	     */
806	    switch (tw->tree.gravity) {
807	      case EastGravity:
808		tc->tree.x = (Position) (tw->tree.maxwidth -
809			      w->core.width - tc->tree.x);
810		break;
811
812	      case SouthGravity:
813		tc->tree.y = (Position) (tw->tree.maxheight -
814			      w->core.height - tc->tree.y);
815		break;
816	    }
817
818	    /*
819	     * Move the widget into position.
820	     */
821	    XtMoveWidget (w, tc->tree.x, tc->tree.y);
822	}
823
824	/*
825	 * Set the positions of all children.
826	 */
827	for (i = 0; i < tc->tree.n_children; i++)
828	  set_positions (tw, tc->tree.children[i], level + 1);
829    }
830}
831
832
833static void
834arrange_subtree(TreeWidget tree, Widget w, int depth, int x, int y)
835{
836    TreeConstraints tc = TREE_CONSTRAINT(w);  /* info attached to all kids */
837    int newx, newy;
838    Bool horiz = IsHorizontal (tree);
839    Widget child = NULL;
840    Dimension tmp;
841    Dimension bw2 = (Dimension)(w->core.border_width * 2);
842    Bool relayout = True;
843    int i;
844
845    /*
846     * If no children, then just lay out where requested.
847     */
848    tc->tree.x = (Position)x;
849    tc->tree.y = (Position)y;
850
851    if (horiz) {
852	int myh = (w->core.height + bw2);
853
854	if (myh > (int)tc->tree.bbsubheight) {
855	    y += (myh - (int)tc->tree.bbsubheight) / 2;
856	    relayout = False;
857	}
858    } else {
859	int myw = (w->core.width + bw2);
860
861	if (myw > (int)tc->tree.bbsubwidth) {
862	    x += (myw - (int)tc->tree.bbsubwidth) / 2;
863	    relayout = False;
864	}
865    }
866
867    if ((tmp = (Dimension)(x + tc->tree.bbwidth)) > tree->tree.maxwidth)
868      tree->tree.maxwidth = tmp;
869    if ((tmp = (Dimension)(y + tc->tree.bbheight)) > tree->tree.maxheight)
870      tree->tree.maxheight = tmp;
871
872    if (tc->tree.n_children == 0) return;
873
874
875    /*
876     * Have children, so walk down tree laying out children, then laying
877     * out parents.
878     */
879    if (horiz) {
880	newx = x + tree->tree.largest[depth];
881	if (depth > 0) newx += tree->tree.hpad;
882	newy = y;
883    } else {
884	newx = x;
885	newy = y + tree->tree.largest[depth];
886	if (depth > 0) newy += tree->tree.vpad;
887    }
888
889    for (i = 0; i < tc->tree.n_children; i++) {
890	TreeConstraints cc;
891
892	child = tc->tree.children[i];	/* last value is used outside loop */
893	cc = TREE_CONSTRAINT(child);
894
895	arrange_subtree (tree, child, depth + 1, newx, newy);
896	if (horiz) {
897	    newy += tree->tree.vpad + cc->tree.bbheight;
898	} else {
899	    newx += tree->tree.hpad + cc->tree.bbwidth;
900	}
901    }
902
903    /*
904     * now layout parent between first and last children
905     */
906    if (relayout && (child != NULL)) {
907	Position adjusted;
908	TreeConstraints firstcc = TREE_CONSTRAINT (tc->tree.children[0]);
909	TreeConstraints lastcc = TREE_CONSTRAINT (child);
910
911	/* Adjustments are disallowed if they result in a position above
912         * or to the left of the originally requested position, because
913	 * this could collide with the position of the previous sibling.
914	 */
915	if (horiz) {
916	    tc->tree.x = (Position)x;
917	    adjusted = (Position)(firstcc->tree.y +
918	      ((lastcc->tree.y + (Position) child->core.height +
919		(Position) child->core.border_width * 2 -
920		firstcc->tree.y - (Position) w->core.height -
921		(Position) w->core.border_width * 2 + 1) / 2));
922	    if (adjusted > tc->tree.y) tc->tree.y = adjusted;
923	} else {
924	    adjusted = (Position)(firstcc->tree.x +
925	      ((lastcc->tree.x + (Position) child->core.width +
926		(Position) child->core.border_width * 2 -
927		firstcc->tree.x - (Position) w->core.width -
928		(Position) w->core.border_width * 2 + 1) / 2));
929	    if (adjusted > tc->tree.x) tc->tree.x = adjusted;
930	    tc->tree.y = (Position)y;
931	}
932    }
933}
934
935static void
936set_tree_size(TreeWidget tw, Bool insetvalues,
937	      unsigned int width, unsigned int height)
938{
939    if (insetvalues) {
940	tw->core.width = (Dimension)width;
941	tw->core.height = (Dimension)height;
942    } else {
943	Dimension replyWidth = 0, replyHeight = 0;
944	XtGeometryResult result = XtMakeResizeRequest ((Widget) tw,
945						       (Dimension)width,
946						       (Dimension)height,
947						       &replyWidth,
948						       &replyHeight);
949	/*
950	 * Accept any compromise.
951	 */
952	if (result == XtGeometryAlmost)
953	  XtMakeResizeRequest ((Widget) tw, replyWidth, replyHeight,
954			       (Dimension *) NULL, (Dimension *) NULL);
955    }
956    return;
957}
958
959static void
960layout_tree(TreeWidget tw, Bool insetvalues)
961{
962    int i;
963    Dimension *dp;
964
965    /*
966     * Do a depth-first search computing the width and height of the bounding
967     * box for the tree at that position (and below).  Then, walk again using
968     * this information to layout the children at each level.
969     */
970
971    if (tw->tree.tree_root == NULL)
972	return;
973
974    tw->tree.maxwidth = tw->tree.maxheight = 0;
975    for (i = 0, dp = tw->tree.largest; i < tw->tree.n_largest; i++, dp++)
976      *dp = 0;
977    initialize_dimensions (&tw->tree.largest, &tw->tree.n_largest,
978			   tw->tree.n_largest);
979    compute_bounding_box_subtree (tw, tw->tree.tree_root, 0);
980
981   /*
982    * Second pass to do final layout.  Each child's bounding box is stacked
983    * on top of (if horizontal, else next to) on top of its siblings.  The
984    * parent is centered between the first and last children.
985    */
986    arrange_subtree (tw, tw->tree.tree_root, 0, 0, 0);
987
988    /*
989     * Move each widget into place.
990     */
991    set_tree_size (tw, insetvalues, tw->tree.maxwidth, tw->tree.maxheight);
992    set_positions (tw, tw->tree.tree_root, 0);
993
994    /*
995     * And redisplay.
996     */
997    if (XtIsRealized ((Widget) tw)) {
998	XClearArea (XtDisplay(tw), XtWindow((Widget)tw), 0, 0, 0, 0, True);
999    }
1000}
1001
1002
1003
1004/*****************************************************************************
1005 *                                                                           *
1006 *				Public Routines                              *
1007 *                                                                           *
1008 *****************************************************************************/
1009
1010void
1011XawTreeForceLayout(Widget tree)
1012{
1013    layout_tree ((TreeWidget) tree, FALSE);
1014}
1015
1016