Bringing apdf from vendor into main branch.
[AROS-Contrib.git] / apdf / xpdf / XPDFTree.cc
blob720197f9988dbc0a7e5944c23f9dde28dc421760
1 //========================================================================
2 //
3 // XPDFTree.cc
4 //
5 // Copyright 2002-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
9 #include <stdlib.h>
10 #include "gmem.h"
11 #include "XPDFTreeP.h"
13 //------------------------------------------------------------------------
15 #define xpdfTreeIndent 16
17 //------------------------------------------------------------------------
19 struct _XPDFTreeEntry {
20 Widget widget;
21 XPDFTreeEntry *children;
22 XPDFTreeEntry *next;
25 //------------------------------------------------------------------------
27 static void classPartInitialize(WidgetClass widgetClass);
28 static void initialize(Widget requestWidget, Widget newWidget,
29 ArgList args, Cardinal *numArgs);
30 static void destroy(Widget widget);
31 static void destroySubtree(XPDFTreeEntry *e);
32 static void resize(Widget widget);
33 static void redisplay(Widget widget, XEvent *event, Region region);
34 static void redisplaySubtree(XPDFTreeWidget w, XPDFTreeEntry *e,
35 XEvent *event, Region region);
36 static void drawExpandedIcon(XPDFTreeWidget w, Position x, Position y);
37 static void drawCollapsedIcon(XPDFTreeWidget w, Position x, Position y);
38 static Boolean setValues(Widget oldWidget, Widget requestWidget,
39 Widget newWidget, ArgList args, Cardinal *numArgs);
40 static void setValuesAlmost(Widget oldWidget, Widget newWidget,
41 XtWidgetGeometry *request,
42 XtWidgetGeometry *reply);
43 static XtGeometryResult queryGeometry(Widget widget,
44 XtWidgetGeometry *request,
45 XtWidgetGeometry *reply);
46 static XtGeometryResult geometryManager(Widget widget,
47 XtWidgetGeometry *request,
48 XtWidgetGeometry *reply);
49 static void changeManaged(Widget widget);
50 static void initConstraint(Widget requestWidget, Widget newWidget,
51 ArgList args, Cardinal *numArgs);
52 static void destroyConstraint(Widget widget);
53 static void deleteSubtree(Widget widget);
54 static Boolean constraintSetValues(Widget oldWidget, Widget requestWidget,
55 Widget newWidget,
56 ArgList args, Cardinal *numArgs);
57 static void insertChildOnList(XPDFTreeEntry *e, XPDFTreeEntry **listHead);
58 static void deleteChildFromList(XPDFTreeEntry *e, XPDFTreeEntry **listHead);
59 static void createGC(Widget widget);
60 static void destroyGC(Widget widget);
61 static void layout(Widget widget, Widget instigator);
62 static int layoutSubtree(XPDFTreeWidget w, Widget instigator,
63 XPDFTreeEntry *e, Position x, Position y,
64 Boolean visible);
65 static void calcSize(Widget widget, Widget instigator,
66 Dimension *totalWidth,
67 Dimension *totalHeight);
68 static void calcSubtreeSize(XPDFTreeWidget w, Widget instigator,
69 XPDFTreeEntry *e,
70 Dimension *width, Dimension *height);
71 static Boolean needRelayout(Widget oldWidget, Widget newWidget);
72 static void click(Widget widget, XEvent *event,
73 String *params, Cardinal *numParams);
74 static Boolean findPosition(XPDFTreeWidget w, int x, int y,
75 XPDFTreeEntry **e, Boolean *onExpandIcon);
76 static Boolean findPositionInSubtree(XPDFTreeWidget w, int x, int y,
77 XPDFTreeEntry **e,
78 Boolean *onExpandIcon);
80 //------------------------------------------------------------------------
82 static XtResource resources[] = {
83 { XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension,
84 sizeof(Dimension), XtOffsetOf(XPDFTreeRec, tree.marginWidth),
85 XmRImmediate, (XtPointer)0 },
86 { XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension,
87 sizeof(Dimension), XtOffsetOf(XPDFTreeRec, tree.marginHeight),
88 XmRImmediate, (XtPointer)0 },
89 { XPDFNselectionCallback, XmCCallback, XmRCallback,
90 sizeof(XtCallbackList), XtOffsetOf(XPDFTreeRec, tree.selectCallback),
91 XmRImmediate, (XtPointer)NULL }
94 static XmSyntheticResource synResources[] = {
95 { XmNmarginWidth, sizeof(Dimension),
96 XtOffsetOf(XPDFTreeRec, tree.marginWidth),
97 #if XmVERSION > 1
98 XmeFromHorizontalPixels, XmeToHorizontalPixels
99 #else
100 _XmFromHorizontalPixels, _XmToHorizontalPixels
101 #endif
103 { XmNmarginHeight, sizeof(Dimension),
104 XtOffsetOf(XPDFTreeRec, tree.marginHeight),
105 #if XmVERSION > 1
106 XmeFromVerticalPixels, XmeToVerticalPixels
107 #else
108 _XmFromVerticalPixels, _XmToVerticalPixels
109 #endif
113 static XtResource constraints[] = {
114 { XPDFNentryParent, XPDFCentryParent, XmRWidget,
115 sizeof(Widget), XtOffsetOf(XPDFTreeConstraintRec, tree.entryParent),
116 XmRImmediate, (XtPointer)NULL },
117 { XPDFNentryExpanded, XPDFCentryExpanded, XmRBoolean,
118 sizeof(Boolean), XtOffsetOf(XPDFTreeConstraintRec, tree.entryExpanded),
119 XmRImmediate, (XtPointer)False },
120 { XPDFNentryPosition, XPDFCentryPosition, XmRInt,
121 sizeof(int), XtOffsetOf(XPDFTreeConstraintRec, tree.entryPosition),
122 XmRImmediate, (XtPointer)0 }
125 static char defaultTranslations[] =
126 "<Btn1Down>: XPDFTreeClick()";
128 static XtActionsRec actions[] = {
129 { "XPDFTreeClick", click }
132 externaldef(xpdftreeclassrec) XPDFTreeClassRec xpdfTreeClassRec = {
133 { // Core
134 (WidgetClass)&xmManagerClassRec, // superclass
135 "XPDFTree", // class_name
136 sizeof(XPDFTreeRec), // widget_size
137 NULL, // class_initialize
138 &classPartInitialize, // class_part_initialize
139 FALSE, // class_inited
140 &initialize, // initialize
141 NULL, // initialize_hook
142 XtInheritRealize, // realize
143 actions, // actions
144 XtNumber(actions), // num_actions
145 resources, // resources
146 XtNumber(resources), // num_resources
147 NULLQUARK, // xrm_class
148 TRUE, // compress_motion
149 XtExposeCompressMaximal, // compress_exposure
150 TRUE, // compress_enterleave
151 FALSE, // visible_interest
152 &destroy, // destroy
153 &resize, // resize
154 &redisplay, // expose
155 &setValues, // set_values
156 NULL, // set_values_hook
157 &setValuesAlmost, // set_values_almost
158 NULL, // get_values_hook
159 NULL, // accept_focus
160 XtVersion, // version
161 NULL, // callback_private
162 defaultTranslations, // tm_table
163 &queryGeometry, // query_geometry
164 NULL, // display_accelerator
165 NULL // extension
167 { // Composite
168 &geometryManager, // geometry_manager
169 &changeManaged, // change_managed
170 XtInheritInsertChild, // insert_child
171 XtInheritDeleteChild, // delete_child
172 NULL // extension
174 { // Constraint
175 constraints, // constraint_resources
176 XtNumber(constraints), // constraint_num_resources
177 sizeof(XPDFTreeConstraintRec), // constraint_size
178 &initConstraint, // constraint_initialize
179 &destroyConstraint, // constraint_destroy
180 &constraintSetValues, // constraint_set_values
181 NULL // extension
183 { // XmManager
184 XtInheritTranslations, // translations
185 #if XmVERSION > 1
186 synResources, // syn_resources
187 XtNumber(synResources), // num_syn_resources
188 #else
189 NULL, // syn_resources
190 0, // num_syn_resources
191 #endif
192 NULL, // syn_constraint_resources
193 0, // num_syn_constraint_res's
194 XmInheritParentProcess, // parent_process
195 NULL // extension
197 { // XPDFTree
198 &createGC, // createGC
199 &destroyGC, // destroyGC
200 &layout, // layout
201 &calcSize, // calcSize
202 &needRelayout, // needRelayout
203 NULL // extension
207 externaldef(xpdftreewidgetclass) WidgetClass xpdfTreeWidgetClass =
208 (WidgetClass)&xpdfTreeClassRec;
210 //------------------------------------------------------------------------
212 static void classPartInitialize(WidgetClass widgetCls) {
213 XPDFTreeWidgetClass wc = (XPDFTreeWidgetClass)widgetCls;
214 XPDFTreeWidgetClass sc = (XPDFTreeWidgetClass)wc->coreClass.superclass;
216 // method inheritance
217 if (wc->treeClass.createGC == XPDFInheritCreateGC) {
218 wc->treeClass.createGC = sc->treeClass.createGC;
220 if (wc->treeClass.destroyGC == XPDFInheritDestroyGC) {
221 wc->treeClass.destroyGC = sc->treeClass.destroyGC;
223 if (wc->treeClass.layout == XPDFInheritLayout) {
224 wc->treeClass.layout = sc->treeClass.layout;
226 if (wc->treeClass.calcSize == XPDFInheritCalcSize) {
227 wc->treeClass.calcSize = sc->treeClass.calcSize;
229 if (wc->treeClass.needRelayout == XPDFInheritNeedRelayout) {
230 wc->treeClass.needRelayout = sc->treeClass.needRelayout;
234 static void initialize(Widget requestWidget, Widget newWidget,
235 ArgList args, Cardinal *numArgs) {
236 XPDFTreeWidget nw = (XPDFTreeWidget)newWidget;
237 XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(newWidget);
239 nw->tree.root = NULL;
240 nw->tree.redrawY = -1;
241 if (cls->treeClass.createGC) {
242 (*cls->treeClass.createGC)(newWidget);
243 } else {
244 createGC(newWidget);
248 static void destroy(Widget widget) {
249 XPDFTreeWidget w = (XPDFTreeWidget)widget;
250 XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
252 if (w->tree.root) {
253 destroySubtree(w->tree.root);
254 w->tree.root = NULL;
256 if (cls->treeClass.destroyGC) {
257 (*cls->treeClass.destroyGC)(widget);
258 } else {
259 destroyGC(widget);
263 static void destroySubtree(XPDFTreeEntry *e) {
264 if (e->children) {
265 destroySubtree(e->children);
267 if (e->next) {
268 destroySubtree(e->next);
272 static void resize(Widget widget) {
273 XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
275 if (cls->treeClass.layout) {
276 (*cls->treeClass.layout)(widget, NULL);
277 } else {
278 layout(widget, NULL);
282 static void redisplay(Widget widget, XEvent *event, Region region) {
283 XPDFTreeWidget w = (XPDFTreeWidget)widget;
284 XPDFTreeEntry *e;
286 if (w->tree.redrawY >= 0) {
287 XClearArea(XtDisplay((Widget)w), XtWindow((Widget)w),
288 0, w->tree.redrawY, w->core.width, w->core.height, False);
289 w->tree.redrawY = -1;
291 for (e = w->tree.root; e; e = e->next) {
292 redisplaySubtree(w, e, event, region);
296 static void redisplaySubtree(XPDFTreeWidget w, XPDFTreeEntry *e,
297 XEvent *event, Region region) {
298 XPDFTreeConstraint c;
299 Position x, y, y2;
300 XPDFTreeEntry *child;
302 (*XtClass(e->widget)->core_class.expose)(e->widget, event, region);
303 c = XPDFTreeCPart(e->widget);
304 x = e->widget->core.x;
305 y = e->widget->core.y + e->widget->core.height / 2;
306 if (e->children) {
307 if (c->entryExpanded) {
308 drawExpandedIcon(w, x - 8, y);
309 y2 = y; // make gcc happy
310 for (child = e->children; child; child = child->next) {
311 y2 = child->widget->core.y + child->widget->core.height / 2;
312 XDrawLine(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.dottedGC,
313 x - 8, y2, x + 6, y2);
314 redisplaySubtree(w, child, event, region);
316 XDrawLine(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.dottedGC,
317 x - 8, y + 2, x - 8, y2);
318 } else {
319 drawCollapsedIcon(w, x - 8, y);
324 static void drawExpandedIcon(XPDFTreeWidget w, Position x, Position y) {
325 XPoint pts[4];
327 pts[0].x = x - 4; pts[0].y = y - 2;
328 pts[1].x = x + 4; pts[1].y = y - 2;
329 pts[2].x = x; pts[2].y = y + 2;
330 pts[3].x = x - 4; pts[3].y = y - 2;
331 XDrawLines(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.plainGC,
332 pts, 4, CoordModeOrigin);
335 static void drawCollapsedIcon(XPDFTreeWidget w, Position x, Position y) {
336 XPoint pts[4];
338 pts[0].x = x - 2; pts[0].y = y - 4;
339 pts[1].x = x - 2; pts[1].y = y + 4;
340 pts[2].x = x + 2; pts[2].y = y;
341 pts[3].x = x - 2; pts[3].y = y - 4;
342 XDrawLines(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.plainGC,
343 pts, 4, CoordModeOrigin);
346 static Boolean setValues(Widget oldWidget, Widget requestWidget,
347 Widget newWidget, ArgList args, Cardinal *numArgs) {
348 XPDFTreeWidget ow = (XPDFTreeWidget)oldWidget;
349 XPDFTreeWidget nw = (XPDFTreeWidget)newWidget;
350 XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(nw);
351 Boolean relayout, redisp;
353 // check to see if layout-affecting resources have changed
354 if (cls->treeClass.needRelayout) {
355 relayout = (*cls->treeClass.needRelayout)((Widget)ow, (Widget)nw);
356 } else {
357 relayout = needRelayout((Widget)ow, (Widget)nw);
359 redisp = False;
360 if (relayout) {
362 // calculate a new ideal size (reset the widget size first so
363 // calcSize will compute a new one)
364 if (nw->core.width == ow->core.width) {
365 nw->core.width = 0;
367 if (nw->core.height == ow->core.height) {
368 nw->core.height = 0;
370 if (cls->treeClass.calcSize) {
371 (*cls->treeClass.calcSize)((Widget)nw, NULL,
372 &nw->core.width, &nw->core.height);
373 } else {
374 calcSize((Widget)nw, NULL, &nw->core.width, &nw->core.height);
377 // if resources have changed but size hasn't, layout manually
378 // (because Xt just looks at the size)
379 if (nw->core.width == ow->core.width &&
380 nw->core.height == ow->core.height) {
381 if (cls->treeClass.layout) {
382 (*cls->treeClass.layout)((Widget)nw, NULL);
383 } else {
384 layout((Widget)nw, NULL);
386 redisp = True;
390 return redisp;
393 static void setValuesAlmost(Widget oldWidget, Widget newWidget,
394 XtWidgetGeometry *request,
395 XtWidgetGeometry *reply) {
396 XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(newWidget);
398 // our parent rejected a geometry request, so accept the compromise
399 // and relayout
400 if (!reply->request_mode) {
401 if (cls->treeClass.layout) {
402 (*cls->treeClass.layout)(newWidget, NULL);
403 } else {
404 layout(newWidget, NULL);
407 *request = *reply;
410 static XtGeometryResult queryGeometry(Widget widget,
411 XtWidgetGeometry *request,
412 XtWidgetGeometry *reply) {
413 XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
415 if (!XtIsRealized(widget)) {
416 reply->width = XtWidth(widget);
417 reply->height = XtHeight(widget);
418 } else {
419 reply->width = 0;
420 reply->height = 0;
422 if (cls->treeClass.calcSize) {
423 (*cls->treeClass.calcSize)(widget, NULL, &reply->width, &reply->height);
424 } else {
425 calcSize(widget, NULL, &reply->width, &reply->height);
427 #if XmVERSION > 1
428 return XmeReplyToQueryGeometry(widget, request, reply);
429 #else
430 if ((request->request_mode & CWWidth) &&
431 (request->request_mode & CWHeight) &&
432 request->width == reply->width &&
433 request->height == reply->height) {
434 return XtGeometryYes;
436 if (reply->width == XtWidth(widget) &&
437 reply->height == XtHeight(widget)) {
438 return XtGeometryNo;
440 reply->request_mode = CWWidth | CWHeight;
441 return XtGeometryAlmost;
442 #endif
445 static XtGeometryResult geometryManager(Widget widget,
446 XtWidgetGeometry *request,
447 XtWidgetGeometry *reply) {
448 XPDFTreeWidget w = (XPDFTreeWidget)XtParent(widget);
449 XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(w);
450 Dimension curWidth, curHeight, curBW;
451 XtWidgetGeometry parentReq;
452 XtGeometryResult result;
454 // deny any requests for a new position
455 if ((request->request_mode & CWX) || (request->request_mode & CWY)) {
456 return XtGeometryNo;
459 // save the current geometry
460 curWidth = w->core.width;
461 curHeight = w->core.height;
462 curBW = w->core.border_width;
464 // make the requested changes
465 if (request->request_mode & CWWidth) {
466 w->core.width = request->width;
468 if (request->request_mode & CWHeight) {
469 w->core.height = request->height;
471 if (request->request_mode & CWBorderWidth) {
472 w->core.border_width = request->border_width;
475 // calculate the new ideal size
476 parentReq.width = 0;
477 parentReq.height = 0;
478 if (cls->treeClass.calcSize) {
479 (*cls->treeClass.calcSize)((Widget)w, widget,
480 &parentReq.width, &reply->height);
481 } else {
482 calcSize((Widget)w, widget, &parentReq.width, &reply->height);
485 // send geometry request to our parent
486 parentReq.request_mode = CWWidth | CWHeight;
487 if (request->request_mode & XtCWQueryOnly) {
488 parentReq.request_mode |= XtCWQueryOnly;
490 result = XtMakeGeometryRequest((Widget)w, &parentReq, NULL);
491 if (result == XtGeometryAlmost) {
492 result = XtGeometryNo;
495 if (result == XtGeometryNo || (request->request_mode & XtCWQueryOnly)) {
496 // restore the original geometry
497 w->core.width = curWidth;
498 w->core.height = curHeight;
499 w->core.border_width = curBW;
500 } else {
501 if (cls->treeClass.layout) {
502 (*cls->treeClass.layout)((Widget)w, widget);
503 } else {
504 layout((Widget)w, widget);
508 return result;
511 static void changeManaged(Widget widget) {
512 Dimension width, height;
513 XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
515 // compute the ideal size
516 if (!XtIsRealized(widget)) {
517 width = XtWidth(widget);
518 height = XtHeight(widget);
519 } else {
520 width = 0;
521 height = 0;
523 if (cls->treeClass.calcSize) {
524 (*cls->treeClass.calcSize)(widget, NULL, &width, &height);
525 } else {
526 calcSize(widget, NULL, &width, &height);
529 // make resize request to parent -- keep asking until we get a yes
530 // or no
531 while (XtMakeResizeRequest(widget, width, height, &width, &height)
532 == XtGeometryAlmost) ;
534 // relayout
535 if (cls->treeClass.layout) {
536 (*cls->treeClass.layout)(widget, NULL);
537 } else {
538 layout(widget, NULL);
541 #if XmVERSION > 1
542 // update keyboard traversal
543 XmeNavigChangeManaged(widget);
544 #else
545 _XmNavigChangeManaged(widget);
546 #endif
549 static void initConstraint(Widget requestWidget, Widget newWidget,
550 ArgList args, Cardinal *numArgs) {
551 XPDFTreeWidget w = (XPDFTreeWidget)XtParent(newWidget);
552 XPDFTreeConstraint c;
554 c = XPDFTreeCPart(newWidget);
555 c->e = (XPDFTreeEntry *)gmalloc(sizeof(XPDFTreeEntry));
556 c->e->widget = newWidget;
557 c->e->children = NULL;
558 c->e->next = NULL;
559 if (c->entryParent) {
560 insertChildOnList(c->e, &XPDFTreeCPart(c->entryParent)->e->children);
561 } else {
562 insertChildOnList(c->e, &w->tree.root);
566 static void destroyConstraint(Widget widget) {
567 deleteSubtree(widget);
570 static void deleteSubtree(Widget widget) {
571 XPDFTreeWidget w = (XPDFTreeWidget)XtParent(widget);
572 XPDFTreeConstraint c;
574 c = XPDFTreeCPart(widget);
575 if (!c->e) {
576 return;
578 while (c->e->children) {
579 deleteSubtree(c->e->children->widget);
581 if (c->entryParent) {
582 deleteChildFromList(c->e, &XPDFTreeCPart(c->entryParent)->e->children);
583 } else {
584 deleteChildFromList(c->e, &w->tree.root);
586 gfree(c->e);
587 c->e = NULL;
590 static Boolean constraintSetValues(Widget oldWidget, Widget requestWidget,
591 Widget newWidget,
592 ArgList args, Cardinal *numArgs) {
593 XPDFTreeWidget w = (XPDFTreeWidget)XtParent(newWidget);
594 XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass((Widget)w);
595 XPDFTreeConstraint oc, nc;
596 Boolean relayout;
597 Dimension width, height;
599 if (!XtIsManaged(newWidget)) {
600 return False;
602 oc = XPDFTreeCPart(oldWidget);
603 nc = XPDFTreeCPart(newWidget);
604 relayout = False;
605 if (nc->entryParent != oc->entryParent ||
606 nc->entryPosition != oc->entryPosition) {
607 if (oc->entryParent) {
608 deleteChildFromList(oc->e, &XPDFTreeCPart(oc->entryParent)->e->children);
609 } else {
610 deleteChildFromList(oc->e, &w->tree.root);
612 if (nc->entryParent) {
613 insertChildOnList(nc->e, &XPDFTreeCPart(nc->entryParent)->e->children);
614 } else {
615 insertChildOnList(nc->e, &w->tree.root);
617 relayout = True;
618 } else if (nc->entryExpanded != oc->entryExpanded) {
619 relayout = True;
622 if (relayout) {
624 // calculate a new ideal size (reset the widget size first so
625 // calcSize will compute a new one)
626 width = 0;
627 height = 0;
628 if (cls->treeClass.calcSize) {
629 (*cls->treeClass.calcSize)((Widget)w, NULL, &width, &height);
630 } else {
631 calcSize((Widget)w, NULL, &width, &height);
634 // make resize request to parent -- keep asking until we get a yes
635 // or no
636 while (XtMakeResizeRequest((Widget)w, width, height, &width, &height)
637 == XtGeometryAlmost) ;
639 // relayout the widget
640 if (cls->treeClass.layout) {
641 (*cls->treeClass.layout)((Widget)w, NULL);
642 } else {
643 layout((Widget)w, NULL);
647 return relayout;
650 static void insertChildOnList(XPDFTreeEntry *e, XPDFTreeEntry **listHead) {
651 int pos;
652 XPDFTreeEntry *e2;
654 pos = XPDFTreeCPart(e->widget)->entryPosition;
655 if (!*listHead || pos < XPDFTreeCPart((*listHead)->widget)->entryPosition) {
656 e->next = *listHead;
657 *listHead = e;
658 } else {
659 for (e2 = *listHead;
660 e2->next && pos >= XPDFTreeCPart(e2->next->widget)->entryPosition;
661 e2 = e2->next) ;
662 e->next = e2->next;
663 e2->next = e;
667 static void deleteChildFromList(XPDFTreeEntry *e, XPDFTreeEntry **listHead) {
668 XPDFTreeEntry **p;
670 for (p = listHead; *p; p = &(*p)->next) {
671 if (*p == e) {
672 *p = e->next;
673 e->next = NULL;
674 return;
679 static void createGC(Widget widget) {
680 XPDFTreeWidget w = (XPDFTreeWidget)widget;
681 XGCValues gcValues;
683 gcValues.foreground = w->manager.foreground;
684 gcValues.line_width = 0;
685 gcValues.line_style = LineSolid;
686 w->tree.plainGC = XtGetGC(widget,
687 GCForeground | GCLineWidth | GCLineStyle,
688 &gcValues);
690 gcValues.line_style = LineOnOffDash;
691 gcValues.dashes = 1;
692 gcValues.dash_offset = 0;
693 w->tree.dottedGC = XtGetGC(widget,
694 GCForeground | GCLineWidth | GCLineStyle |
695 GCDashList | GCDashOffset,
696 &gcValues);
699 static void destroyGC(Widget widget) {
700 XPDFTreeWidget w = (XPDFTreeWidget)widget;
702 XtReleaseGC(widget, w->tree.plainGC);
703 XtReleaseGC(widget, w->tree.dottedGC);
706 static void layout(Widget widget, Widget instigator) {
707 XPDFTreeWidget w = (XPDFTreeWidget)widget;
708 XPDFTreeEntry *e;
709 Position x, y;
711 x = w->tree.marginWidth + xpdfTreeIndent;
712 y = w->tree.marginHeight;
713 for (e = w->tree.root; e; e = e->next) {
714 y = layoutSubtree(w, instigator, e, x, y, True);
718 static int layoutSubtree(XPDFTreeWidget w, Widget instigator,
719 XPDFTreeEntry *e, Position x, Position y,
720 Boolean visible) {
721 Widget ew;
722 XPDFTreeEntry *child;
723 XPDFTreeConstraint c;
725 ew = e->widget;
726 if (!XtIsManaged(ew)) {
727 return y;
729 c = XPDFTreeCPart(ew);
731 // place this entry
732 if (ew) {
733 if (visible) {
734 if (ew == instigator) {
735 ew->core.x = x;
736 ew->core.y = y;
737 } else {
738 #if XmVERSION > 1
739 XmeConfigureObject(ew, x, y, ew->core.width, ew->core.height,
740 ew->core.border_width);
741 #else
742 _XmConfigureObject(ew, x, y, ew->core.width, ew->core.height,
743 ew->core.border_width);
744 #endif
746 y += ew->core.height + 2 * ew->core.border_width;
750 // place this entry's children
751 x += xpdfTreeIndent;
752 for (child = e->children; child; child = child->next) {
753 y = layoutSubtree(w, instigator, child, x, y,
754 visible && (!c || c->entryExpanded));
757 return y;
760 static void calcSize(Widget widget, Widget instigator,
761 Dimension *totalWidth,
762 Dimension *totalHeight) {
763 XPDFTreeWidget w = (XPDFTreeWidget)widget;
764 XPDFTreeEntry *e;
765 Dimension w1, h1, w2, h2;
767 w1 = h1 = 0;
768 for (e = w->tree.root; e; e = e->next) {
769 calcSubtreeSize(w, instigator, e, &w2, &h2);
770 if (w2 > w1) {
771 w1 = w2;
773 h1 += h2;
775 w1 += xpdfTreeIndent + 2 * w->tree.marginWidth;
776 h1 += 2 * w->tree.marginHeight;
777 if (h1 == 0) {
778 h1 = 1;
780 if (!*totalWidth) {
781 *totalWidth = w1;
783 if (!*totalHeight) {
784 *totalHeight = h1;
788 static void calcSubtreeSize(XPDFTreeWidget w, Widget instigator,
789 XPDFTreeEntry *e,
790 Dimension *width, Dimension *height) {
791 Widget ew;
792 XPDFTreeEntry *child;
793 XPDFTreeConstraint c;
794 XtWidgetGeometry geom;
795 Dimension w1, h1, w2, h2;
797 ew = e->widget;
798 if (!XtIsManaged(ew)) {
799 *width = *height = 0;
800 return;
802 c = XPDFTreeCPart(ew);
804 // get size of this entry
805 if (ew) {
806 if (!XtIsManaged(ew)) {
807 *width = *height = 0;
808 return;
810 if (ew == instigator) {
811 w1 = ew->core.width;
812 h1 = ew->core.height;
813 } else {
814 XtQueryGeometry(ew, NULL, &geom);
815 w1 = (geom.request_mode & CWWidth) ? geom.width : ew->core.width;
816 h1 = (geom.request_mode & CWHeight) ? geom.height : ew->core.height;
818 h1 += 2 * ew->core.border_width;
819 } else {
820 // root of tree
821 w1 = 0;
822 h1 = 0;
825 // if this entry is expanded, get size of all of its children
826 if (c->entryExpanded) {
827 for (child = e->children; child; child = child->next) {
828 calcSubtreeSize(w, instigator, child, &w2, &h2);
829 w2 += xpdfTreeIndent;
830 if (w2 > w1) {
831 w1 = w2;
833 h1 += h2;
837 *width = w1;
838 *height = h1;
841 static Boolean needRelayout(Widget oldWidget, Widget newWidget) {
842 XPDFTreeWidget ow = (XPDFTreeWidget)oldWidget;
843 XPDFTreeWidget nw = (XPDFTreeWidget)newWidget;
845 if (nw->tree.marginWidth != ow->tree.marginWidth ||
846 nw->tree.marginHeight != ow->tree.marginHeight) {
847 return True;
849 return False;
852 static void click(Widget widget, XEvent *event,
853 String *params, Cardinal *numParams) {
854 XPDFTreeWidget w = (XPDFTreeWidget)widget;
855 XButtonPressedEvent *bpe;
856 XPDFTreeEntry *e;
857 Boolean onExpandIcon;
858 XPDFTreeConstraint c;
859 XPDFTreeSelectCallbackStruct cbs;
861 if (event->type != ButtonPress) {
862 return;
864 bpe = (XButtonPressedEvent *)event;
865 if (findPosition(w, bpe->x, bpe->y, &e, &onExpandIcon)) {
866 if (onExpandIcon) {
867 c = XPDFTreeCPart(e->widget);
868 w->tree.redrawY = e->widget->core.y;
869 XtVaSetValues(e->widget, XPDFNentryExpanded, !c->entryExpanded, NULL);
870 } else {
871 XmProcessTraversal(e->widget, XmTRAVERSE_CURRENT);
872 XtCallActionProc(widget, "ManagerGadgetActivate", event, NULL, 0);
873 cbs.reason = XmCR_ACTIVATE;
874 cbs.event = event;
875 cbs.selectedItem = e->widget;
876 XtCallCallbackList(widget, w->tree.selectCallback, &cbs);
881 static Boolean findPosition(XPDFTreeWidget w, int x, int y,
882 XPDFTreeEntry **e, Boolean *onExpandIcon) {
883 XPDFTreeEntry *e2;
885 for (e2 = w->tree.root; e2; e2 = e2->next) {
886 *e = e2;
887 if (findPositionInSubtree(w, x, y, e, onExpandIcon)) {
888 return True;
891 return False;
894 // If (x,y) falls on either an expand/collapse icon or a label gadget,
895 // set *<e> and *<onExpandIcon> and return true.
896 static Boolean findPositionInSubtree(XPDFTreeWidget w, int x, int y,
897 XPDFTreeEntry **e,
898 Boolean *onExpandIcon) {
899 Widget child;
900 XPDFTreeConstraint c;
901 XPDFTreeEntry *e2;
902 int y1;
904 child = (*e)->widget;
905 y1 = child->core.y + child->core.height / 2;
906 if (x >= child->core.x && x < child->core.x + child->core.width &&
907 y >= child->core.y && y < child->core.y + child->core.height) {
908 *onExpandIcon = False;
909 return True;
910 } else if (x >= child->core.x - 16 && x < child->core.x - 4 &&
911 y >= y1 - 6 && y < y1 + 6 &&
912 (*e)->children) {
913 *onExpandIcon = True;
914 return True;
916 c = XPDFTreeCPart(child);
917 if (!c || c->entryExpanded) {
918 for (e2 = (*e)->children; e2; e2 = e2->next) {
919 *e = e2;
920 if (findPositionInSubtree(w, x, y, e, onExpandIcon)) {
921 return True;
925 return False;
928 Widget XPDFCreateTree(Widget parent, char *name,
929 ArgList argList, Cardinal numArgs) {
930 return XtCreateWidget(name, xpdfTreeWidgetClass, parent, argList, numArgs);