#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/XawInit.h>
#include <X11/Xaw/Cardinals.h>
#include <X11/Xaw/TreeP.h>
#include "Private.h"
#define IsHorizontal(tw) ((tw)->tree.gravity == WestGravity || \
(tw)->tree.gravity == EastGravity)
static void XawTreeChangeManaged(Widget);
static void XawTreeClassInitialize(void);
static void XawTreeConstraintDestroy(Widget);
static void XawTreeConstraintInitialize(Widget, Widget, ArgList, Cardinal*);
static Boolean XawTreeConstraintSetValues(Widget, Widget, Widget,
ArgList, Cardinal*);
static void XawTreeDestroy(Widget);
static XtGeometryResult XawTreeGeometryManager(Widget, XtWidgetGeometry*,
XtWidgetGeometry*);
static void XawTreeInitialize(Widget, Widget, ArgList, Cardinal*);
static XtGeometryResult XawTreeQueryGeometry(Widget, XtWidgetGeometry*,
XtWidgetGeometry*);
static void XawTreeRedisplay(Widget, XEvent*, Region);
static Boolean XawTreeSetValues(Widget, Widget, Widget, ArgList, Cardinal*);
static void arrange_subtree(TreeWidget, Widget, int, int, int);
static void check_gravity(TreeWidget, XtGravity);
static void compute_bounding_box_subtree(TreeWidget, Widget, int);
static void delete_node(Widget, Widget);
static GC get_tree_gc(TreeWidget);
static void initialize_dimensions(Dimension**, int*, int);
static void insert_node(Widget, Widget);
static void layout_tree(TreeWidget, Bool);
static void set_positions(TreeWidget, Widget, int);
static void set_tree_size(TreeWidget, Bool, unsigned int, unsigned int);
static XtResource resources[] = {
{ XtNautoReconfigure, XtCAutoReconfigure, XtRBoolean, sizeof (Boolean),
XtOffsetOf(TreeRec, tree.auto_reconfigure), XtRImmediate,
(XtPointer) FALSE },
{ XtNhSpace, XtCHSpace, XtRDimension, sizeof (Dimension),
XtOffsetOf(TreeRec, tree.hpad), XtRImmediate, (XtPointer) 0 },
{ XtNvSpace, XtCVSpace, XtRDimension, sizeof (Dimension),
XtOffsetOf(TreeRec, tree.vpad), XtRImmediate, (XtPointer) 0 },
{ XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
XtOffsetOf(TreeRec, tree.foreground), XtRString,
XtDefaultForeground},
{ XtNlineWidth, XtCLineWidth, XtRDimension, sizeof (Dimension),
XtOffsetOf(TreeRec, tree.line_width), XtRImmediate, (XtPointer) 0 },
{ XtNgravity, XtCGravity, XtRGravity, sizeof (XtGravity),
XtOffsetOf(TreeRec, tree.gravity), XtRImmediate,
(XtPointer) WestGravity },
#ifndef OLDXAW
{ XawNdisplayList, XawCDisplayList, XawRDisplayList, sizeof(XawDisplayList*),
XtOffsetOf(TreeRec, tree.display_list), XtRImmediate,
NULL },
#endif
};
static XtResource treeConstraintResources[] = {
{ XtNtreeParent, XtCTreeParent, XtRWidget, sizeof (Widget),
XtOffsetOf(TreeConstraintsRec, tree.parent), XtRImmediate, NULL },
{ XtNtreeGC, XtCTreeGC, XtRGC, sizeof(GC),
XtOffsetOf(TreeConstraintsRec, tree.gc), XtRImmediate, NULL },
};
TreeClassRec treeClassRec = {
{
(WidgetClass) &constraintClassRec,
"Tree",
sizeof(TreeRec),
XawTreeClassInitialize,
NULL,
FALSE,
XawTreeInitialize,
NULL,
XtInheritRealize,
NULL,
0,
resources,
XtNumber(resources),
NULLQUARK,
TRUE,
TRUE,
TRUE,
TRUE,
XawTreeDestroy,
NULL,
XawTreeRedisplay,
XawTreeSetValues,
NULL,
XtInheritSetValuesAlmost,
NULL,
NULL,
XtVersion,
NULL,
NULL,
XawTreeQueryGeometry,
NULL,
NULL,
},
{
XawTreeGeometryManager,
XawTreeChangeManaged,
XtInheritInsertChild,
XtInheritDeleteChild,
NULL,
},
{
treeConstraintResources,
XtNumber(treeConstraintResources),
sizeof(TreeConstraintsRec),
XawTreeConstraintInitialize,
XawTreeConstraintDestroy,
XawTreeConstraintSetValues,
NULL,
},
{
NULL,
}
};
WidgetClass treeWidgetClass = (WidgetClass) &treeClassRec;
static void
initialize_dimensions(Dimension **listp, int *sizep, int n)
{
int i;
Dimension *l;
if (!*listp) {
*listp = (Dimension *) XtCalloc ((unsigned int) n,
(unsigned int) sizeof(Dimension));
*sizep = ((*listp) ? n : 0);
return;
}
if (n > *sizep) {
*listp = (Dimension *) XtRealloc((char *) *listp,
(unsigned int) (n*sizeof(Dimension)));
if (!*listp) {
*sizep = 0;
return;
}
for (i = *sizep, l = (*listp) + i; i < n; i++, l++) *l = 0;
*sizep = n;
}
return;
}
static GC
get_tree_gc(TreeWidget w)
{
XtGCMask valuemask = GCBackground | GCForeground;
XGCValues values;
values.background = w->core.background_pixel;
values.foreground = w->tree.foreground;
if (w->tree.line_width != 0) {
valuemask |= GCLineWidth;
values.line_width = w->tree.line_width;
}
return XtGetGC ((Widget) w, valuemask, &values);
}
static void
insert_node(Widget parent, Widget node)
{
TreeConstraints pc;
TreeConstraints nc = TREE_CONSTRAINT(node);
int nindex;
nc->tree.parent = parent;
if (parent == NULL) return;
pc = TREE_CONSTRAINT(parent);
nindex = pc->tree.n_children;
if (pc->tree.n_children == pc->tree.max_children) {
pc->tree.max_children += (pc->tree.max_children / 2) + 2;
pc->tree.children = (WidgetList) XtRealloc ((char *)pc->tree.children,
(unsigned int)
((pc->tree.max_children) *
sizeof(Widget)));
}
pc->tree.children[nindex] = node;
pc->tree.n_children++;
}
static void
delete_node(Widget parent, Widget node)
{
TreeConstraints pc;
int pos, i;
if (!parent) return;
pc = TREE_CONSTRAINT(parent);
for (pos = 0; pos < pc->tree.n_children; pos++)
if (pc->tree.children[pos] == node) break;
if (pos == pc->tree.n_children) return;
pc->tree.n_children--;
for (i = pos; i < pc->tree.n_children; i++)
pc->tree.children[i] = pc->tree.children[i+1];
pc->tree.children[pc->tree.n_children] = NULL;
}
static void
check_gravity(TreeWidget tw, XtGravity grav)
{
switch (tw->tree.gravity) {
case WestGravity: case NorthGravity: case EastGravity: case SouthGravity:
break;
default:
tw->tree.gravity = grav;
break;
}
}
static void
XawTreeClassInitialize(void)
{
XawInitializeWidgetSet();
XtAddConverter(XtRString, XtRGravity, XmuCvtStringToGravity, NULL, 0);
XtSetTypeConverter(XtRGravity, XtRString, XmuCvtGravityToString,
NULL, 0, XtCacheNone, NULL);
}
static void
XawTreeInitialize(Widget grequest, Widget gnew,
ArgList args, Cardinal *num_args)
{
TreeWidget request = (TreeWidget) grequest, cnew = (TreeWidget) gnew;
Arg arglist[2];
if (request->core.width <= 0) cnew->core.width = 5;
if (request->core.height <= 0) cnew->core.height = 5;
if (request->tree.hpad == 0 && request->tree.vpad == 0) {
if (IsHorizontal (request)) {
cnew->tree.hpad = TREE_HORIZONTAL_DEFAULT_SPACING;
cnew->tree.vpad = TREE_VERTICAL_DEFAULT_SPACING;
} else {
cnew->tree.hpad = TREE_VERTICAL_DEFAULT_SPACING;
cnew->tree.vpad = TREE_HORIZONTAL_DEFAULT_SPACING;
}
}
cnew->tree.gc = get_tree_gc (cnew);
cnew->tree.tree_root = (Widget) NULL;
XtSetArg(arglist[0], XtNwidth, 1);
XtSetArg(arglist[1], XtNheight, 1);
cnew->tree.tree_root = XtCreateWidget ("root", widgetClass, gnew,
arglist,TWO);
cnew->tree.largest = NULL;
cnew->tree.n_largest = 0;
initialize_dimensions (&cnew->tree.largest, &cnew->tree.n_largest,
TREE_INITIAL_DEPTH);
check_gravity (cnew, WestGravity);
}
static void
XawTreeConstraintInitialize(Widget request, Widget cnew,
ArgList args, Cardinal *num_args)
{
TreeConstraints tc = TREE_CONSTRAINT(cnew);
TreeWidget tw = (TreeWidget) cnew->core.parent;
tc->tree.n_children = 0;
tc->tree.max_children = 0;
tc->tree.children = (Widget *) NULL;
tc->tree.x = tc->tree.y = 0;
tc->tree.bbsubwidth = 0;
tc->tree.bbsubheight = 0;
if (tc->tree.parent)
insert_node (tc->tree.parent, cnew);
else if (tw->tree.tree_root)
insert_node (tw->tree.tree_root, cnew);
}
static Boolean
XawTreeSetValues(Widget gcurrent, Widget grequest, Widget gnew,
ArgList args, Cardinal *num_args)
{
TreeWidget current = (TreeWidget) gcurrent, cnew = (TreeWidget) gnew;
Boolean redraw = FALSE;
if (cnew->tree.foreground != current->tree.foreground ||
cnew->core.background_pixel != current->core.background_pixel ||
cnew->tree.line_width != current->tree.line_width) {
XtReleaseGC (gnew, cnew->tree.gc);
cnew->tree.gc = get_tree_gc (cnew);
redraw = TRUE;
}
if (cnew->tree.gravity != current->tree.gravity) {
check_gravity (cnew, current->tree.gravity);
}
if (IsHorizontal(cnew) != IsHorizontal(current)) {
if (cnew->tree.vpad == current->tree.vpad &&
cnew->tree.hpad == current->tree.hpad) {
cnew->tree.vpad = current->tree.hpad;
cnew->tree.hpad = current->tree.vpad;
}
}
if (cnew->tree.vpad != current->tree.vpad ||
cnew->tree.hpad != current->tree.hpad ||
cnew->tree.gravity != current->tree.gravity) {
layout_tree (cnew, TRUE);
redraw = FALSE;
}
return redraw;
}
static Boolean
XawTreeConstraintSetValues(Widget current, Widget request, Widget cnew,
ArgList args, Cardinal *num_args)
{
TreeConstraints newc = TREE_CONSTRAINT(cnew);
TreeConstraints curc = TREE_CONSTRAINT(current);
TreeWidget tw = (TreeWidget) cnew->core.parent;
if (curc->tree.parent != newc->tree.parent){
if (curc->tree.parent)
delete_node (curc->tree.parent, cnew);
if (newc->tree.parent)
insert_node(newc->tree.parent, cnew);
if (XtIsRealized((Widget)tw))
layout_tree (tw, FALSE);
}
return False;
}
static void
XawTreeConstraintDestroy(Widget w)
{
TreeConstraints tc = TREE_CONSTRAINT(w);
TreeWidget tw = (TreeWidget) XtParent(w);
int i;
if (tw->tree.tree_root == w) {
if (tc->tree.n_children > 0)
tw->tree.tree_root = tc->tree.children[0];
else
tw->tree.tree_root = NULL;
}
delete_node (tc->tree.parent, (Widget) w);
for (i = 0; i< tc->tree.n_children; i++)
insert_node (tc->tree.parent, tc->tree.children[i]);
layout_tree ((TreeWidget) (w->core.parent), FALSE);
}
static XtGeometryResult
XawTreeGeometryManager(Widget w, XtWidgetGeometry *request,
XtWidgetGeometry *reply)
{
TreeWidget tw = (TreeWidget) w->core.parent;
if ((request->request_mode & CWX && request->x!=w->core.x)
||(request->request_mode & CWY && request->y!=w->core.y))
return (XtGeometryNo);
if (request->request_mode & CWWidth)
w->core.width = request->width;
if (request->request_mode & CWHeight)
w->core.height = request->height;
if (request->request_mode & CWBorderWidth)
w->core.border_width = request->border_width;
if (tw->tree.auto_reconfigure) layout_tree (tw, FALSE);
return (XtGeometryYes);
}
static void
XawTreeChangeManaged(Widget gw)
{
layout_tree ((TreeWidget) gw, FALSE);
}
static void
XawTreeDestroy(Widget gw)
{
TreeWidget w = (TreeWidget) gw;
XtReleaseGC (gw, w->tree.gc);
if (w->tree.largest) XtFree ((char *) w->tree.largest);
}
static void
XawTreeRedisplay(Widget gw, XEvent *event, Region region)
{
TreeWidget tw = (TreeWidget) gw;
#ifndef OLDXAW
if (tw->tree.display_list)
XawRunDisplayList(gw, tw->tree.display_list, event, region);
#endif
if (tw->core.visible) {
Cardinal i;
int j;
Display *dpy = XtDisplay (tw);
Window w = XtWindow (tw);
for (i = 0; i < tw->composite.num_children; i++) {
Widget child = tw->composite.children[i];
TreeConstraints tc = TREE_CONSTRAINT(child);
if (child != tw->tree.tree_root && tc->tree.n_children) {
int srcx = child->core.x + child->core.border_width;
int srcy = child->core.y + child->core.border_width;
switch (tw->tree.gravity) {
case WestGravity:
srcx += child->core.width + child->core.border_width;
case EastGravity:
srcy += child->core.height / 2;
break;
case NorthGravity:
srcy += child->core.height + child->core.border_width;
case SouthGravity:
srcx += child->core.width / 2;
break;
}
for (j = 0; j < tc->tree.n_children; j++) {
Widget k = tc->tree.children[j];
GC gc = (tc->tree.gc ? tc->tree.gc : tw->tree.gc);
switch (tw->tree.gravity) {
case WestGravity:
XDrawLine (dpy, w, gc, srcx, srcy,
(int) k->core.x,
(k->core.y + ((int) k->core.border_width) +
((int) k->core.height) / 2));
break;
case NorthGravity:
XDrawLine (dpy, w, gc, srcx, srcy,
(k->core.x + ((int) k->core.border_width) +
((int) k->core.width) / 2),
(int) k->core.y);
break;
case EastGravity:
XDrawLine (dpy, w, gc, srcx, srcy,
(k->core.x +
(((int) k->core.border_width) << 1) +
(int) k->core.width),
(k->core.y + ((int) k->core.border_width) +
((int) k->core.height) / 2));
break;
case SouthGravity:
XDrawLine (dpy, w, gc, srcx, srcy,
(k->core.x + ((int) k->core.border_width) +
((int) k->core.width) / 2),
(k->core.y +
(((int) k->core.border_width) << 1) +
(int) k->core.height));
break;
}
}
}
}
}
}
static XtGeometryResult
XawTreeQueryGeometry(Widget w, XtWidgetGeometry *intended,
XtWidgetGeometry *preferred)
{
TreeWidget tw = (TreeWidget) w;
preferred->request_mode = (CWWidth | CWHeight);
preferred->width = tw->tree.maxwidth;
preferred->height = tw->tree.maxheight;
if (((intended->request_mode & (CWWidth | CWHeight)) ==
(CWWidth | CWHeight)) &&
intended->width == preferred->width &&
intended->height == preferred->height)
return XtGeometryYes;
else if (preferred->width == w->core.width &&
preferred->height == w->core.height)
return XtGeometryNo;
else
return XtGeometryAlmost;
}
static void
compute_bounding_box_subtree(TreeWidget tree, Widget w, int depth)
{
TreeConstraints tc = TREE_CONSTRAINT(w);
int i;
Bool horiz = IsHorizontal (tree);
Dimension newwidth, newheight;
Dimension bw2 = w->core.border_width * 2;
if (depth >= tree->tree.n_largest) {
initialize_dimensions (&tree->tree.largest,
&tree->tree.n_largest, depth + 1);
}
newwidth = ((horiz ? w->core.width : w->core.height) + bw2);
if (tree->tree.largest[depth] < newwidth)
tree->tree.largest[depth] = newwidth;
tc->tree.bbwidth = w->core.width + bw2;
tc->tree.bbheight = w->core.height + bw2;
if (tc->tree.n_children == 0) return;
newwidth = 0;
newheight = 0;
for (i = 0; i < tc->tree.n_children; i++) {
Widget child = tc->tree.children[i];
TreeConstraints cc = TREE_CONSTRAINT(child);
compute_bounding_box_subtree (tree, child, depth + 1);
if (horiz) {
if (newwidth < cc->tree.bbwidth) newwidth = cc->tree.bbwidth;
newheight += tree->tree.vpad + cc->tree.bbheight;
} else {
if (newheight < cc->tree.bbheight) newheight = cc->tree.bbheight;
newwidth += tree->tree.hpad + cc->tree.bbwidth;
}
}
tc->tree.bbsubwidth = newwidth;
tc->tree.bbsubheight = newheight;
if (horiz) {
tc->tree.bbwidth += tree->tree.hpad + newwidth;
newheight -= tree->tree.vpad;
if (newheight > tc->tree.bbheight) tc->tree.bbheight = newheight;
} else {
tc->tree.bbheight += tree->tree.vpad + newheight;
newwidth -= tree->tree.hpad;
if (newwidth > tc->tree.bbwidth) tc->tree.bbwidth = newwidth;
}
}
static void
set_positions(TreeWidget tw, Widget w, int level)
{
int i;
if (w) {
TreeConstraints tc = TREE_CONSTRAINT(w);
if (level > 0) {
switch (tw->tree.gravity) {
case EastGravity:
tc->tree.x = (((Position) tw->tree.maxwidth) -
((Position) w->core.width) - tc->tree.x);
break;
case SouthGravity:
tc->tree.y = (((Position) tw->tree.maxheight) -
((Position) w->core.height) - tc->tree.y);
break;
}
XtMoveWidget (w, tc->tree.x, tc->tree.y);
}
for (i = 0; i < tc->tree.n_children; i++)
set_positions (tw, tc->tree.children[i], level + 1);
}
}
static void
arrange_subtree(TreeWidget tree, Widget w, int depth, int x, int y)
{
TreeConstraints tc = TREE_CONSTRAINT(w);
TreeConstraints firstcc, lastcc;
int i;
int newx, newy;
Bool horiz = IsHorizontal (tree);
Widget child = NULL;
Dimension tmp;
Dimension bw2 = w->core.border_width * 2;
Bool relayout = True;
tc->tree.x = x;
tc->tree.y = y;
if (horiz) {
int myh = (w->core.height + bw2);
if (myh > (int)tc->tree.bbsubheight) {
y += (myh - (int)tc->tree.bbsubheight) / 2;
relayout = False;
}
} else {
int myw = (w->core.width + bw2);
if (myw > (int)tc->tree.bbsubwidth) {
x += (myw - (int)tc->tree.bbsubwidth) / 2;
relayout = False;
}
}
if ((tmp = ((Dimension) x) + tc->tree.bbwidth) > tree->tree.maxwidth)
tree->tree.maxwidth = tmp;
if ((tmp = ((Dimension) y) + tc->tree.bbheight) > tree->tree.maxheight)
tree->tree.maxheight = tmp;
if (tc->tree.n_children == 0) return;
if (horiz) {
newx = x + tree->tree.largest[depth];
if (depth > 0) newx += tree->tree.hpad;
newy = y;
} else {
newx = x;
newy = y + tree->tree.largest[depth];
if (depth > 0) newy += tree->tree.vpad;
}
for (i = 0; i < tc->tree.n_children; i++) {
TreeConstraints cc;
child = tc->tree.children[i];
cc = TREE_CONSTRAINT(child);
arrange_subtree (tree, child, depth + 1, newx, newy);
if (horiz) {
newy += tree->tree.vpad + cc->tree.bbheight;
} else {
newx += tree->tree.hpad + cc->tree.bbwidth;
}
}
if (relayout) {
Position adjusted;
firstcc = TREE_CONSTRAINT (tc->tree.children[0]);
lastcc = TREE_CONSTRAINT (child);
if (horiz) {
tc->tree.x = x;
adjusted = firstcc->tree.y +
((lastcc->tree.y + (Position) child->core.height +
(Position) child->core.border_width * 2 -
firstcc->tree.y - (Position) w->core.height -
(Position) w->core.border_width * 2 + 1) / 2);
if (adjusted > tc->tree.y) tc->tree.y = adjusted;
} else {
adjusted = firstcc->tree.x +
((lastcc->tree.x + (Position) child->core.width +
(Position) child->core.border_width * 2 -
firstcc->tree.x - (Position) w->core.width -
(Position) w->core.border_width * 2 + 1) / 2);
if (adjusted > tc->tree.x) tc->tree.x = adjusted;
tc->tree.y = y;
}
}
}
static void
set_tree_size(TreeWidget tw, Bool insetvalues,
unsigned int width, unsigned int height)
{
if (insetvalues) {
tw->core.width = width;
tw->core.height = height;
} else {
Dimension replyWidth = 0, replyHeight = 0;
XtGeometryResult result = XtMakeResizeRequest ((Widget) tw,
width, height,
&replyWidth,
&replyHeight);
if (result == XtGeometryAlmost)
XtMakeResizeRequest ((Widget) tw, replyWidth, replyHeight,
(Dimension *) NULL, (Dimension *) NULL);
}
return;
}
static void
layout_tree(TreeWidget tw, Bool insetvalues)
{
int i;
Dimension *dp;
if (tw->tree.tree_root == NULL)
return;
tw->tree.maxwidth = tw->tree.maxheight = 0;
for (i = 0, dp = tw->tree.largest; i < tw->tree.n_largest; i++, dp++)
*dp = 0;
initialize_dimensions (&tw->tree.largest, &tw->tree.n_largest,
tw->tree.n_largest);
compute_bounding_box_subtree (tw, tw->tree.tree_root, 0);
arrange_subtree (tw, tw->tree.tree_root, 0, 0, 0);
set_tree_size (tw, insetvalues, tw->tree.maxwidth, tw->tree.maxheight);
set_positions (tw, tw->tree.tree_root, 0);
if (XtIsRealized ((Widget) tw)) {
XClearArea (XtDisplay(tw), XtWindow((Widget)tw), 0, 0, 0, 0, True);
}
}
void
XawTreeForceLayout(Widget tree)
{
layout_tree ((TreeWidget) tree, FALSE);
}