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