You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
931 lines
26 KiB
931 lines
26 KiB
//========================================================================
|
|
//
|
|
// XPDFTree.cc
|
|
//
|
|
// Copyright 2002-2003 Glyph & Cog, LLC
|
|
//
|
|
//========================================================================
|
|
|
|
#include <stdlib.h>
|
|
#include "gmem.h"
|
|
#include "XPDFTreeP.h"
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
#define xpdfTreeIndent 16
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
struct _XPDFTreeEntry {
|
|
Widget widget;
|
|
XPDFTreeEntry *children;
|
|
XPDFTreeEntry *next;
|
|
};
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
static void classPartInitialize(WidgetClass widgetClass);
|
|
static void initialize(Widget requestWidget, Widget newWidget,
|
|
ArgList args, Cardinal *numArgs);
|
|
static void destroy(Widget widget);
|
|
static void destroySubtree(XPDFTreeEntry *e);
|
|
static void resize(Widget widget);
|
|
static void redisplay(Widget widget, XEvent *event, Region region);
|
|
static void redisplaySubtree(XPDFTreeWidget w, XPDFTreeEntry *e,
|
|
XEvent *event, Region region);
|
|
static void drawExpandedIcon(XPDFTreeWidget w, Position x, Position y);
|
|
static void drawCollapsedIcon(XPDFTreeWidget w, Position x, Position y);
|
|
static Boolean setValues(Widget oldWidget, Widget requestWidget,
|
|
Widget newWidget, ArgList args, Cardinal *numArgs);
|
|
static void setValuesAlmost(Widget oldWidget, Widget newWidget,
|
|
XtWidgetGeometry *request,
|
|
XtWidgetGeometry *reply);
|
|
static XtGeometryResult queryGeometry(Widget widget,
|
|
XtWidgetGeometry *request,
|
|
XtWidgetGeometry *reply);
|
|
static XtGeometryResult geometryManager(Widget widget,
|
|
XtWidgetGeometry *request,
|
|
XtWidgetGeometry *reply);
|
|
static void changeManaged(Widget widget);
|
|
static void initConstraint(Widget requestWidget, Widget newWidget,
|
|
ArgList args, Cardinal *numArgs);
|
|
static void destroyConstraint(Widget widget);
|
|
static void deleteSubtree(Widget widget);
|
|
static Boolean constraintSetValues(Widget oldWidget, Widget requestWidget,
|
|
Widget newWidget,
|
|
ArgList args, Cardinal *numArgs);
|
|
static void insertChildOnList(XPDFTreeEntry *e, XPDFTreeEntry **listHead);
|
|
static void deleteChildFromList(XPDFTreeEntry *e, XPDFTreeEntry **listHead);
|
|
static void createGC(Widget widget);
|
|
static void destroyGC(Widget widget);
|
|
static void layout(Widget widget, Widget instigator);
|
|
static int layoutSubtree(XPDFTreeWidget w, Widget instigator,
|
|
XPDFTreeEntry *e, Position x, Position y,
|
|
Boolean visible);
|
|
static void calcSize(Widget widget, Widget instigator,
|
|
Dimension *totalWidth,
|
|
Dimension *totalHeight);
|
|
static void calcSubtreeSize(XPDFTreeWidget w, Widget instigator,
|
|
XPDFTreeEntry *e,
|
|
Dimension *width, Dimension *height);
|
|
static Boolean needRelayout(Widget oldWidget, Widget newWidget);
|
|
static void click(Widget widget, XEvent *event,
|
|
String *params, Cardinal *numParams);
|
|
static Boolean findPosition(XPDFTreeWidget w, int x, int y,
|
|
XPDFTreeEntry **e, Boolean *onExpandIcon);
|
|
static Boolean findPositionInSubtree(XPDFTreeWidget w, int x, int y,
|
|
XPDFTreeEntry **e,
|
|
Boolean *onExpandIcon);
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
static XtResource resources[] = {
|
|
{ XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension,
|
|
sizeof(Dimension), XtOffsetOf(XPDFTreeRec, tree.marginWidth),
|
|
XmRImmediate, (XtPointer)0 },
|
|
{ XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension,
|
|
sizeof(Dimension), XtOffsetOf(XPDFTreeRec, tree.marginHeight),
|
|
XmRImmediate, (XtPointer)0 },
|
|
{ XPDFNselectionCallback, XmCCallback, XmRCallback,
|
|
sizeof(XtCallbackList), XtOffsetOf(XPDFTreeRec, tree.selectCallback),
|
|
XmRImmediate, (XtPointer)NULL }
|
|
};
|
|
|
|
static XmSyntheticResource synResources[] = {
|
|
{ XmNmarginWidth, sizeof(Dimension),
|
|
XtOffsetOf(XPDFTreeRec, tree.marginWidth),
|
|
#if XmVERSION > 1
|
|
XmeFromHorizontalPixels, XmeToHorizontalPixels
|
|
#else
|
|
_XmFromHorizontalPixels, _XmToHorizontalPixels
|
|
#endif
|
|
},
|
|
{ XmNmarginHeight, sizeof(Dimension),
|
|
XtOffsetOf(XPDFTreeRec, tree.marginHeight),
|
|
#if XmVERSION > 1
|
|
XmeFromVerticalPixels, XmeToVerticalPixels
|
|
#else
|
|
_XmFromVerticalPixels, _XmToVerticalPixels
|
|
#endif
|
|
}
|
|
};
|
|
|
|
static XtResource constraints[] = {
|
|
{ XPDFNentryParent, XPDFCentryParent, XmRWidget,
|
|
sizeof(Widget), XtOffsetOf(XPDFTreeConstraintRec, tree.entryParent),
|
|
XmRImmediate, (XtPointer)NULL },
|
|
{ XPDFNentryExpanded, XPDFCentryExpanded, XmRBoolean,
|
|
sizeof(Boolean), XtOffsetOf(XPDFTreeConstraintRec, tree.entryExpanded),
|
|
XmRImmediate, (XtPointer)False },
|
|
{ XPDFNentryPosition, XPDFCentryPosition, XmRInt,
|
|
sizeof(int), XtOffsetOf(XPDFTreeConstraintRec, tree.entryPosition),
|
|
XmRImmediate, (XtPointer)0 }
|
|
};
|
|
|
|
static char defaultTranslations[] =
|
|
"<Btn1Down>: XPDFTreeClick()";
|
|
|
|
static XtActionsRec actions[] = {
|
|
{ "XPDFTreeClick", click }
|
|
};
|
|
|
|
externaldef(xpdftreeclassrec) XPDFTreeClassRec xpdfTreeClassRec = {
|
|
{ // Core
|
|
(WidgetClass)&xmManagerClassRec, // superclass
|
|
"XPDFTree", // class_name
|
|
sizeof(XPDFTreeRec), // widget_size
|
|
NULL, // class_initialize
|
|
&classPartInitialize, // class_part_initialize
|
|
FALSE, // class_inited
|
|
&initialize, // initialize
|
|
NULL, // initialize_hook
|
|
XtInheritRealize, // realize
|
|
actions, // actions
|
|
XtNumber(actions), // num_actions
|
|
resources, // resources
|
|
XtNumber(resources), // num_resources
|
|
NULLQUARK, // xrm_class
|
|
TRUE, // compress_motion
|
|
XtExposeCompressMaximal, // compress_exposure
|
|
TRUE, // compress_enterleave
|
|
FALSE, // visible_interest
|
|
&destroy, // destroy
|
|
&resize, // resize
|
|
&redisplay, // expose
|
|
&setValues, // set_values
|
|
NULL, // set_values_hook
|
|
&setValuesAlmost, // set_values_almost
|
|
NULL, // get_values_hook
|
|
NULL, // accept_focus
|
|
XtVersion, // version
|
|
NULL, // callback_private
|
|
defaultTranslations, // tm_table
|
|
&queryGeometry, // query_geometry
|
|
NULL, // display_accelerator
|
|
NULL // extension
|
|
},
|
|
{ // Composite
|
|
&geometryManager, // geometry_manager
|
|
&changeManaged, // change_managed
|
|
XtInheritInsertChild, // insert_child
|
|
XtInheritDeleteChild, // delete_child
|
|
NULL // extension
|
|
},
|
|
{ // Constraint
|
|
constraints, // constraint_resources
|
|
XtNumber(constraints), // constraint_num_resources
|
|
sizeof(XPDFTreeConstraintRec), // constraint_size
|
|
&initConstraint, // constraint_initialize
|
|
&destroyConstraint, // constraint_destroy
|
|
&constraintSetValues, // constraint_set_values
|
|
NULL // extension
|
|
},
|
|
{ // XmManager
|
|
XtInheritTranslations, // translations
|
|
#if XmVERSION > 1
|
|
synResources, // syn_resources
|
|
XtNumber(synResources), // num_syn_resources
|
|
#else
|
|
NULL, // syn_resources
|
|
0, // num_syn_resources
|
|
#endif
|
|
NULL, // syn_constraint_resources
|
|
0, // num_syn_constraint_res's
|
|
XmInheritParentProcess, // parent_process
|
|
NULL // extension
|
|
},
|
|
{ // XPDFTree
|
|
&createGC, // createGC
|
|
&destroyGC, // destroyGC
|
|
&layout, // layout
|
|
&calcSize, // calcSize
|
|
&needRelayout, // needRelayout
|
|
NULL // extension
|
|
}
|
|
};
|
|
|
|
externaldef(xpdftreewidgetclass) WidgetClass xpdfTreeWidgetClass =
|
|
(WidgetClass)&xpdfTreeClassRec;
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
static void classPartInitialize(WidgetClass widgetCls) {
|
|
XPDFTreeWidgetClass wc = (XPDFTreeWidgetClass)widgetCls;
|
|
XPDFTreeWidgetClass sc = (XPDFTreeWidgetClass)wc->coreClass.superclass;
|
|
|
|
// method inheritance
|
|
if (wc->treeClass.createGC == XPDFInheritCreateGC) {
|
|
wc->treeClass.createGC = sc->treeClass.createGC;
|
|
}
|
|
if (wc->treeClass.destroyGC == XPDFInheritDestroyGC) {
|
|
wc->treeClass.destroyGC = sc->treeClass.destroyGC;
|
|
}
|
|
if (wc->treeClass.layout == XPDFInheritLayout) {
|
|
wc->treeClass.layout = sc->treeClass.layout;
|
|
}
|
|
if (wc->treeClass.calcSize == XPDFInheritCalcSize) {
|
|
wc->treeClass.calcSize = sc->treeClass.calcSize;
|
|
}
|
|
if (wc->treeClass.needRelayout == XPDFInheritNeedRelayout) {
|
|
wc->treeClass.needRelayout = sc->treeClass.needRelayout;
|
|
}
|
|
}
|
|
|
|
static void initialize(Widget requestWidget, Widget newWidget,
|
|
ArgList args, Cardinal *numArgs) {
|
|
XPDFTreeWidget nw = (XPDFTreeWidget)newWidget;
|
|
XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(newWidget);
|
|
|
|
nw->tree.root = NULL;
|
|
nw->tree.redrawY = -1;
|
|
if (cls->treeClass.createGC) {
|
|
(*cls->treeClass.createGC)(newWidget);
|
|
} else {
|
|
createGC(newWidget);
|
|
}
|
|
}
|
|
|
|
static void destroy(Widget widget) {
|
|
XPDFTreeWidget w = (XPDFTreeWidget)widget;
|
|
XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
|
|
|
|
if (w->tree.root) {
|
|
destroySubtree(w->tree.root);
|
|
w->tree.root = NULL;
|
|
}
|
|
if (cls->treeClass.destroyGC) {
|
|
(*cls->treeClass.destroyGC)(widget);
|
|
} else {
|
|
destroyGC(widget);
|
|
}
|
|
}
|
|
|
|
static void destroySubtree(XPDFTreeEntry *e) {
|
|
if (e->children) {
|
|
destroySubtree(e->children);
|
|
}
|
|
if (e->next) {
|
|
destroySubtree(e->next);
|
|
}
|
|
}
|
|
|
|
static void resize(Widget widget) {
|
|
XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
|
|
|
|
if (cls->treeClass.layout) {
|
|
(*cls->treeClass.layout)(widget, NULL);
|
|
} else {
|
|
layout(widget, NULL);
|
|
}
|
|
}
|
|
|
|
static void redisplay(Widget widget, XEvent *event, Region region) {
|
|
XPDFTreeWidget w = (XPDFTreeWidget)widget;
|
|
XPDFTreeEntry *e;
|
|
|
|
if (w->tree.redrawY >= 0) {
|
|
XClearArea(XtDisplay((Widget)w), XtWindow((Widget)w),
|
|
0, w->tree.redrawY, w->core.width, w->core.height, False);
|
|
w->tree.redrawY = -1;
|
|
}
|
|
for (e = w->tree.root; e; e = e->next) {
|
|
redisplaySubtree(w, e, event, region);
|
|
}
|
|
}
|
|
|
|
static void redisplaySubtree(XPDFTreeWidget w, XPDFTreeEntry *e,
|
|
XEvent *event, Region region) {
|
|
XPDFTreeConstraint c;
|
|
Position x, y, y2;
|
|
XPDFTreeEntry *child;
|
|
|
|
(*XtClass(e->widget)->core_class.expose)(e->widget, event, region);
|
|
c = XPDFTreeCPart(e->widget);
|
|
x = e->widget->core.x;
|
|
y = e->widget->core.y + e->widget->core.height / 2;
|
|
if (e->children) {
|
|
if (c->entryExpanded) {
|
|
drawExpandedIcon(w, x - 8, y);
|
|
y2 = y; // make gcc happy
|
|
for (child = e->children; child; child = child->next) {
|
|
y2 = child->widget->core.y + child->widget->core.height / 2;
|
|
XDrawLine(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.dottedGC,
|
|
x - 8, y2, x + 6, y2);
|
|
redisplaySubtree(w, child, event, region);
|
|
}
|
|
XDrawLine(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.dottedGC,
|
|
x - 8, y + 2, x - 8, y2);
|
|
} else {
|
|
drawCollapsedIcon(w, x - 8, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void drawExpandedIcon(XPDFTreeWidget w, Position x, Position y) {
|
|
XPoint pts[4];
|
|
|
|
pts[0].x = x - 4; pts[0].y = y - 2;
|
|
pts[1].x = x + 4; pts[1].y = y - 2;
|
|
pts[2].x = x; pts[2].y = y + 2;
|
|
pts[3].x = x - 4; pts[3].y = y - 2;
|
|
XDrawLines(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.plainGC,
|
|
pts, 4, CoordModeOrigin);
|
|
}
|
|
|
|
static void drawCollapsedIcon(XPDFTreeWidget w, Position x, Position y) {
|
|
XPoint pts[4];
|
|
|
|
pts[0].x = x - 2; pts[0].y = y - 4;
|
|
pts[1].x = x - 2; pts[1].y = y + 4;
|
|
pts[2].x = x + 2; pts[2].y = y;
|
|
pts[3].x = x - 2; pts[3].y = y - 4;
|
|
XDrawLines(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.plainGC,
|
|
pts, 4, CoordModeOrigin);
|
|
}
|
|
|
|
static Boolean setValues(Widget oldWidget, Widget requestWidget,
|
|
Widget newWidget, ArgList args, Cardinal *numArgs) {
|
|
XPDFTreeWidget ow = (XPDFTreeWidget)oldWidget;
|
|
XPDFTreeWidget nw = (XPDFTreeWidget)newWidget;
|
|
XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(nw);
|
|
Boolean relayout, redisp;
|
|
|
|
// check to see if layout-affecting resources have changed
|
|
if (cls->treeClass.needRelayout) {
|
|
relayout = (*cls->treeClass.needRelayout)((Widget)ow, (Widget)nw);
|
|
} else {
|
|
relayout = needRelayout((Widget)ow, (Widget)nw);
|
|
}
|
|
redisp = False;
|
|
if (relayout) {
|
|
|
|
// calculate a new ideal size (reset the widget size first so
|
|
// calcSize will compute a new one)
|
|
if (nw->core.width == ow->core.width) {
|
|
nw->core.width = 0;
|
|
}
|
|
if (nw->core.height == ow->core.height) {
|
|
nw->core.height = 0;
|
|
}
|
|
if (cls->treeClass.calcSize) {
|
|
(*cls->treeClass.calcSize)((Widget)nw, NULL,
|
|
&nw->core.width, &nw->core.height);
|
|
} else {
|
|
calcSize((Widget)nw, NULL, &nw->core.width, &nw->core.height);
|
|
}
|
|
|
|
// if resources have changed but size hasn't, layout manually
|
|
// (because Xt just looks at the size)
|
|
if (nw->core.width == ow->core.width &&
|
|
nw->core.height == ow->core.height) {
|
|
if (cls->treeClass.layout) {
|
|
(*cls->treeClass.layout)((Widget)nw, NULL);
|
|
} else {
|
|
layout((Widget)nw, NULL);
|
|
}
|
|
redisp = True;
|
|
}
|
|
}
|
|
|
|
return redisp;
|
|
}
|
|
|
|
static void setValuesAlmost(Widget oldWidget, Widget newWidget,
|
|
XtWidgetGeometry *request,
|
|
XtWidgetGeometry *reply) {
|
|
XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(newWidget);
|
|
|
|
// our parent rejected a geometry request, so accept the compromise
|
|
// and relayout
|
|
if (!reply->request_mode) {
|
|
if (cls->treeClass.layout) {
|
|
(*cls->treeClass.layout)(newWidget, NULL);
|
|
} else {
|
|
layout(newWidget, NULL);
|
|
}
|
|
}
|
|
*request = *reply;
|
|
}
|
|
|
|
static XtGeometryResult queryGeometry(Widget widget,
|
|
XtWidgetGeometry *request,
|
|
XtWidgetGeometry *reply) {
|
|
XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
|
|
|
|
if (!XtIsRealized(widget)) {
|
|
reply->width = XtWidth(widget);
|
|
reply->height = XtHeight(widget);
|
|
} else {
|
|
reply->width = 0;
|
|
reply->height = 0;
|
|
}
|
|
if (cls->treeClass.calcSize) {
|
|
(*cls->treeClass.calcSize)(widget, NULL, &reply->width, &reply->height);
|
|
} else {
|
|
calcSize(widget, NULL, &reply->width, &reply->height);
|
|
}
|
|
#if XmVERSION > 1
|
|
return XmeReplyToQueryGeometry(widget, request, reply);
|
|
#else
|
|
if ((request->request_mode & CWWidth) &&
|
|
(request->request_mode & CWHeight) &&
|
|
request->width == reply->width &&
|
|
request->height == reply->height) {
|
|
return XtGeometryYes;
|
|
}
|
|
if (reply->width == XtWidth(widget) &&
|
|
reply->height == XtHeight(widget)) {
|
|
return XtGeometryNo;
|
|
}
|
|
reply->request_mode = CWWidth | CWHeight;
|
|
return XtGeometryAlmost;
|
|
#endif
|
|
}
|
|
|
|
static XtGeometryResult geometryManager(Widget widget,
|
|
XtWidgetGeometry *request,
|
|
XtWidgetGeometry *reply) {
|
|
XPDFTreeWidget w = (XPDFTreeWidget)XtParent(widget);
|
|
XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(w);
|
|
Dimension curWidth, curHeight, curBW;
|
|
XtWidgetGeometry parentReq;
|
|
XtGeometryResult result;
|
|
|
|
// deny any requests for a new position
|
|
if ((request->request_mode & CWX) || (request->request_mode & CWY)) {
|
|
return XtGeometryNo;
|
|
}
|
|
|
|
// save the current geometry
|
|
curWidth = w->core.width;
|
|
curHeight = w->core.height;
|
|
curBW = w->core.border_width;
|
|
|
|
// make the requested changes
|
|
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;
|
|
}
|
|
|
|
// calculate the new ideal size
|
|
parentReq.width = 0;
|
|
parentReq.height = 0;
|
|
if (cls->treeClass.calcSize) {
|
|
(*cls->treeClass.calcSize)((Widget)w, widget,
|
|
&parentReq.width, &reply->height);
|
|
} else {
|
|
calcSize((Widget)w, widget, &parentReq.width, &reply->height);
|
|
}
|
|
|
|
// send geometry request to our parent
|
|
parentReq.request_mode = CWWidth | CWHeight;
|
|
if (request->request_mode & XtCWQueryOnly) {
|
|
parentReq.request_mode |= XtCWQueryOnly;
|
|
}
|
|
result = XtMakeGeometryRequest((Widget)w, &parentReq, NULL);
|
|
if (result == XtGeometryAlmost) {
|
|
result = XtGeometryNo;
|
|
}
|
|
|
|
if (result == XtGeometryNo || (request->request_mode & XtCWQueryOnly)) {
|
|
// restore the original geometry
|
|
w->core.width = curWidth;
|
|
w->core.height = curHeight;
|
|
w->core.border_width = curBW;
|
|
} else {
|
|
if (cls->treeClass.layout) {
|
|
(*cls->treeClass.layout)((Widget)w, widget);
|
|
} else {
|
|
layout((Widget)w, widget);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void changeManaged(Widget widget) {
|
|
Dimension width, height;
|
|
XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
|
|
|
|
// compute the ideal size
|
|
if (!XtIsRealized(widget)) {
|
|
width = XtWidth(widget);
|
|
height = XtHeight(widget);
|
|
} else {
|
|
width = 0;
|
|
height = 0;
|
|
}
|
|
if (cls->treeClass.calcSize) {
|
|
(*cls->treeClass.calcSize)(widget, NULL, &width, &height);
|
|
} else {
|
|
calcSize(widget, NULL, &width, &height);
|
|
}
|
|
|
|
// make resize request to parent -- keep asking until we get a yes
|
|
// or no
|
|
while (XtMakeResizeRequest(widget, width, height, &width, &height)
|
|
== XtGeometryAlmost) ;
|
|
|
|
// relayout
|
|
if (cls->treeClass.layout) {
|
|
(*cls->treeClass.layout)(widget, NULL);
|
|
} else {
|
|
layout(widget, NULL);
|
|
}
|
|
|
|
#if XmVERSION > 1
|
|
// update keyboard traversal
|
|
XmeNavigChangeManaged(widget);
|
|
#else
|
|
_XmNavigChangeManaged(widget);
|
|
#endif
|
|
}
|
|
|
|
static void initConstraint(Widget requestWidget, Widget newWidget,
|
|
ArgList args, Cardinal *numArgs) {
|
|
XPDFTreeWidget w = (XPDFTreeWidget)XtParent(newWidget);
|
|
XPDFTreeConstraint c;
|
|
|
|
c = XPDFTreeCPart(newWidget);
|
|
c->e = (XPDFTreeEntry *)gmalloc(sizeof(XPDFTreeEntry));
|
|
c->e->widget = newWidget;
|
|
c->e->children = NULL;
|
|
c->e->next = NULL;
|
|
if (c->entryParent) {
|
|
insertChildOnList(c->e, &XPDFTreeCPart(c->entryParent)->e->children);
|
|
} else {
|
|
insertChildOnList(c->e, &w->tree.root);
|
|
}
|
|
}
|
|
|
|
static void destroyConstraint(Widget widget) {
|
|
deleteSubtree(widget);
|
|
}
|
|
|
|
static void deleteSubtree(Widget widget) {
|
|
XPDFTreeWidget w = (XPDFTreeWidget)XtParent(widget);
|
|
XPDFTreeConstraint c;
|
|
|
|
c = XPDFTreeCPart(widget);
|
|
if (!c->e) {
|
|
return;
|
|
}
|
|
while (c->e->children) {
|
|
deleteSubtree(c->e->children->widget);
|
|
}
|
|
if (c->entryParent) {
|
|
deleteChildFromList(c->e, &XPDFTreeCPart(c->entryParent)->e->children);
|
|
} else {
|
|
deleteChildFromList(c->e, &w->tree.root);
|
|
}
|
|
gfree(c->e);
|
|
c->e = NULL;
|
|
}
|
|
|
|
static Boolean constraintSetValues(Widget oldWidget, Widget requestWidget,
|
|
Widget newWidget,
|
|
ArgList args, Cardinal *numArgs) {
|
|
XPDFTreeWidget w = (XPDFTreeWidget)XtParent(newWidget);
|
|
XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass((Widget)w);
|
|
XPDFTreeConstraint oc, nc;
|
|
Boolean relayout;
|
|
Dimension width, height;
|
|
|
|
if (!XtIsManaged(newWidget)) {
|
|
return False;
|
|
}
|
|
oc = XPDFTreeCPart(oldWidget);
|
|
nc = XPDFTreeCPart(newWidget);
|
|
relayout = False;
|
|
if (nc->entryParent != oc->entryParent ||
|
|
nc->entryPosition != oc->entryPosition) {
|
|
if (oc->entryParent) {
|
|
deleteChildFromList(oc->e, &XPDFTreeCPart(oc->entryParent)->e->children);
|
|
} else {
|
|
deleteChildFromList(oc->e, &w->tree.root);
|
|
}
|
|
if (nc->entryParent) {
|
|
insertChildOnList(nc->e, &XPDFTreeCPart(nc->entryParent)->e->children);
|
|
} else {
|
|
insertChildOnList(nc->e, &w->tree.root);
|
|
}
|
|
relayout = True;
|
|
} else if (nc->entryExpanded != oc->entryExpanded) {
|
|
relayout = True;
|
|
}
|
|
|
|
if (relayout) {
|
|
|
|
// calculate a new ideal size (reset the widget size first so
|
|
// calcSize will compute a new one)
|
|
width = 0;
|
|
height = 0;
|
|
if (cls->treeClass.calcSize) {
|
|
(*cls->treeClass.calcSize)((Widget)w, NULL, &width, &height);
|
|
} else {
|
|
calcSize((Widget)w, NULL, &width, &height);
|
|
}
|
|
|
|
// make resize request to parent -- keep asking until we get a yes
|
|
// or no
|
|
while (XtMakeResizeRequest((Widget)w, width, height, &width, &height)
|
|
== XtGeometryAlmost) ;
|
|
|
|
// relayout the widget
|
|
if (cls->treeClass.layout) {
|
|
(*cls->treeClass.layout)((Widget)w, NULL);
|
|
} else {
|
|
layout((Widget)w, NULL);
|
|
}
|
|
}
|
|
|
|
return relayout;
|
|
}
|
|
|
|
static void insertChildOnList(XPDFTreeEntry *e, XPDFTreeEntry **listHead) {
|
|
int pos;
|
|
XPDFTreeEntry *e2;
|
|
|
|
pos = XPDFTreeCPart(e->widget)->entryPosition;
|
|
if (!*listHead || pos < XPDFTreeCPart((*listHead)->widget)->entryPosition) {
|
|
e->next = *listHead;
|
|
*listHead = e;
|
|
} else {
|
|
for (e2 = *listHead;
|
|
e2->next && pos >= XPDFTreeCPart(e2->next->widget)->entryPosition;
|
|
e2 = e2->next) ;
|
|
e->next = e2->next;
|
|
e2->next = e;
|
|
}
|
|
}
|
|
|
|
static void deleteChildFromList(XPDFTreeEntry *e, XPDFTreeEntry **listHead) {
|
|
XPDFTreeEntry **p;
|
|
|
|
for (p = listHead; *p; p = &(*p)->next) {
|
|
if (*p == e) {
|
|
*p = e->next;
|
|
e->next = NULL;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void createGC(Widget widget) {
|
|
XPDFTreeWidget w = (XPDFTreeWidget)widget;
|
|
XGCValues gcValues;
|
|
|
|
gcValues.foreground = w->manager.foreground;
|
|
gcValues.line_width = 0;
|
|
gcValues.line_style = LineSolid;
|
|
w->tree.plainGC = XtGetGC(widget,
|
|
GCForeground | GCLineWidth | GCLineStyle,
|
|
&gcValues);
|
|
|
|
gcValues.line_style = LineOnOffDash;
|
|
gcValues.dashes = 1;
|
|
gcValues.dash_offset = 0;
|
|
w->tree.dottedGC = XtGetGC(widget,
|
|
GCForeground | GCLineWidth | GCLineStyle |
|
|
GCDashList | GCDashOffset,
|
|
&gcValues);
|
|
}
|
|
|
|
static void destroyGC(Widget widget) {
|
|
XPDFTreeWidget w = (XPDFTreeWidget)widget;
|
|
|
|
XtReleaseGC(widget, w->tree.plainGC);
|
|
XtReleaseGC(widget, w->tree.dottedGC);
|
|
}
|
|
|
|
static void layout(Widget widget, Widget instigator) {
|
|
XPDFTreeWidget w = (XPDFTreeWidget)widget;
|
|
XPDFTreeEntry *e;
|
|
Position x, y;
|
|
|
|
x = w->tree.marginWidth + xpdfTreeIndent;
|
|
y = w->tree.marginHeight;
|
|
for (e = w->tree.root; e; e = e->next) {
|
|
y = layoutSubtree(w, instigator, e, x, y, True);
|
|
}
|
|
}
|
|
|
|
static int layoutSubtree(XPDFTreeWidget w, Widget instigator,
|
|
XPDFTreeEntry *e, Position x, Position y,
|
|
Boolean visible) {
|
|
Widget ew;
|
|
XPDFTreeEntry *child;
|
|
XPDFTreeConstraint c;
|
|
|
|
ew = e->widget;
|
|
if (!XtIsManaged(ew)) {
|
|
return y;
|
|
}
|
|
c = XPDFTreeCPart(ew);
|
|
|
|
// place this entry
|
|
if (ew) {
|
|
if (visible) {
|
|
if (ew == instigator) {
|
|
ew->core.x = x;
|
|
ew->core.y = y;
|
|
} else {
|
|
#if XmVERSION > 1
|
|
XmeConfigureObject(ew, x, y, ew->core.width, ew->core.height,
|
|
ew->core.border_width);
|
|
#else
|
|
_XmConfigureObject(ew, x, y, ew->core.width, ew->core.height,
|
|
ew->core.border_width);
|
|
#endif
|
|
}
|
|
y += ew->core.height + 2 * ew->core.border_width;
|
|
}
|
|
}
|
|
|
|
// place this entry's children
|
|
x += xpdfTreeIndent;
|
|
for (child = e->children; child; child = child->next) {
|
|
y = layoutSubtree(w, instigator, child, x, y,
|
|
visible && (!c || c->entryExpanded));
|
|
}
|
|
|
|
return y;
|
|
}
|
|
|
|
static void calcSize(Widget widget, Widget instigator,
|
|
Dimension *totalWidth,
|
|
Dimension *totalHeight) {
|
|
XPDFTreeWidget w = (XPDFTreeWidget)widget;
|
|
XPDFTreeEntry *e;
|
|
Dimension w1, h1, w2, h2;
|
|
|
|
w1 = h1 = 0;
|
|
for (e = w->tree.root; e; e = e->next) {
|
|
calcSubtreeSize(w, instigator, e, &w2, &h2);
|
|
if (w2 > w1) {
|
|
w1 = w2;
|
|
}
|
|
h1 += h2;
|
|
}
|
|
w1 += xpdfTreeIndent + 2 * w->tree.marginWidth;
|
|
h1 += 2 * w->tree.marginHeight;
|
|
if (h1 == 0) {
|
|
h1 = 1;
|
|
}
|
|
if (!*totalWidth) {
|
|
*totalWidth = w1;
|
|
}
|
|
if (!*totalHeight) {
|
|
*totalHeight = h1;
|
|
}
|
|
}
|
|
|
|
static void calcSubtreeSize(XPDFTreeWidget w, Widget instigator,
|
|
XPDFTreeEntry *e,
|
|
Dimension *width, Dimension *height) {
|
|
Widget ew;
|
|
XPDFTreeEntry *child;
|
|
XPDFTreeConstraint c;
|
|
XtWidgetGeometry geom;
|
|
Dimension w1, h1, w2, h2;
|
|
|
|
ew = e->widget;
|
|
if (!XtIsManaged(ew)) {
|
|
*width = *height = 0;
|
|
return;
|
|
}
|
|
c = XPDFTreeCPart(ew);
|
|
|
|
// get size of this entry
|
|
if (ew) {
|
|
if (!XtIsManaged(ew)) {
|
|
*width = *height = 0;
|
|
return;
|
|
}
|
|
if (ew == instigator) {
|
|
w1 = ew->core.width;
|
|
h1 = ew->core.height;
|
|
} else {
|
|
XtQueryGeometry(ew, NULL, &geom);
|
|
w1 = (geom.request_mode & CWWidth) ? geom.width : ew->core.width;
|
|
h1 = (geom.request_mode & CWHeight) ? geom.height : ew->core.height;
|
|
}
|
|
h1 += 2 * ew->core.border_width;
|
|
} else {
|
|
// root of tree
|
|
w1 = 0;
|
|
h1 = 0;
|
|
}
|
|
|
|
// if this entry is expanded, get size of all of its children
|
|
if (c->entryExpanded) {
|
|
for (child = e->children; child; child = child->next) {
|
|
calcSubtreeSize(w, instigator, child, &w2, &h2);
|
|
w2 += xpdfTreeIndent;
|
|
if (w2 > w1) {
|
|
w1 = w2;
|
|
}
|
|
h1 += h2;
|
|
}
|
|
}
|
|
|
|
*width = w1;
|
|
*height = h1;
|
|
}
|
|
|
|
static Boolean needRelayout(Widget oldWidget, Widget newWidget) {
|
|
XPDFTreeWidget ow = (XPDFTreeWidget)oldWidget;
|
|
XPDFTreeWidget nw = (XPDFTreeWidget)newWidget;
|
|
|
|
if (nw->tree.marginWidth != ow->tree.marginWidth ||
|
|
nw->tree.marginHeight != ow->tree.marginHeight) {
|
|
return True;
|
|
}
|
|
return False;
|
|
}
|
|
|
|
static void click(Widget widget, XEvent *event,
|
|
String *params, Cardinal *numParams) {
|
|
XPDFTreeWidget w = (XPDFTreeWidget)widget;
|
|
XButtonPressedEvent *bpe;
|
|
XPDFTreeEntry *e;
|
|
Boolean onExpandIcon;
|
|
XPDFTreeConstraint c;
|
|
XPDFTreeSelectCallbackStruct cbs;
|
|
|
|
if (event->type != ButtonPress) {
|
|
return;
|
|
}
|
|
bpe = (XButtonPressedEvent *)event;
|
|
if (findPosition(w, bpe->x, bpe->y, &e, &onExpandIcon)) {
|
|
if (onExpandIcon) {
|
|
c = XPDFTreeCPart(e->widget);
|
|
w->tree.redrawY = e->widget->core.y;
|
|
XtVaSetValues(e->widget, XPDFNentryExpanded, !c->entryExpanded, NULL);
|
|
} else {
|
|
XmProcessTraversal(e->widget, XmTRAVERSE_CURRENT);
|
|
XtCallActionProc(widget, "ManagerGadgetActivate", event, NULL, 0);
|
|
cbs.reason = XmCR_ACTIVATE;
|
|
cbs.event = event;
|
|
cbs.selectedItem = e->widget;
|
|
XtCallCallbackList(widget, w->tree.selectCallback, &cbs);
|
|
}
|
|
}
|
|
}
|
|
|
|
static Boolean findPosition(XPDFTreeWidget w, int x, int y,
|
|
XPDFTreeEntry **e, Boolean *onExpandIcon) {
|
|
XPDFTreeEntry *e2;
|
|
|
|
for (e2 = w->tree.root; e2; e2 = e2->next) {
|
|
*e = e2;
|
|
if (findPositionInSubtree(w, x, y, e, onExpandIcon)) {
|
|
return True;
|
|
}
|
|
}
|
|
return False;
|
|
}
|
|
|
|
// If (x,y) falls on either an expand/collapse icon or a label gadget,
|
|
// set *<e> and *<onExpandIcon> and return true.
|
|
static Boolean findPositionInSubtree(XPDFTreeWidget w, int x, int y,
|
|
XPDFTreeEntry **e,
|
|
Boolean *onExpandIcon) {
|
|
Widget child;
|
|
XPDFTreeConstraint c;
|
|
XPDFTreeEntry *e2;
|
|
int y1;
|
|
|
|
child = (*e)->widget;
|
|
y1 = child->core.y + child->core.height / 2;
|
|
if (x >= child->core.x && x < child->core.x + child->core.width &&
|
|
y >= child->core.y && y < child->core.y + child->core.height) {
|
|
*onExpandIcon = False;
|
|
return True;
|
|
} else if (x >= child->core.x - 16 && x < child->core.x - 4 &&
|
|
y >= y1 - 6 && y < y1 + 6 &&
|
|
(*e)->children) {
|
|
*onExpandIcon = True;
|
|
return True;
|
|
}
|
|
c = XPDFTreeCPart(child);
|
|
if (!c || c->entryExpanded) {
|
|
for (e2 = (*e)->children; e2; e2 = e2->next) {
|
|
*e = e2;
|
|
if (findPositionInSubtree(w, x, y, e, onExpandIcon)) {
|
|
return True;
|
|
}
|
|
}
|
|
}
|
|
return False;
|
|
}
|
|
|
|
Widget XPDFCreateTree(Widget parent, char *name,
|
|
ArgList argList, Cardinal numArgs) {
|
|
return XtCreateWidget(name, xpdfTreeWidgetClass, parent, argList, numArgs);
|
|
}
|
|
|