]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/XPDFTree.cc
Import of Xpdf 2.02 for merge
[evince.git] / pdf / xpdf / XPDFTree.cc
1 //========================================================================
2 //
3 // XPDFTree.cc
4 //
5 // Copyright 2002-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <stdlib.h>
10 #include "gmem.h"
11 #include "XPDFTreeP.h"
12
13 //------------------------------------------------------------------------
14
15 #define xpdfTreeIndent 16
16
17 //------------------------------------------------------------------------
18
19 struct _XPDFTreeEntry {
20   Widget widget;
21   XPDFTreeEntry *children;
22   XPDFTreeEntry *next;
23 };
24
25 //------------------------------------------------------------------------
26
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);
79
80 //------------------------------------------------------------------------
81
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 }
92 };
93
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
102   },
103   { XmNmarginHeight, sizeof(Dimension),
104     XtOffsetOf(XPDFTreeRec, tree.marginHeight),
105 #if XmVERSION > 1
106     XmeFromVerticalPixels, XmeToVerticalPixels
107 #else
108     _XmFromVerticalPixels, _XmToVerticalPixels
109 #endif
110   }
111 };
112
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 }
123 };
124
125 static char defaultTranslations[] =
126   "<Btn1Down>: XPDFTreeClick()";
127
128 static XtActionsRec actions[] = {
129   { "XPDFTreeClick", click }
130 };
131
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
166   },
167   { // Composite
168     &geometryManager,                           // geometry_manager
169     &changeManaged,                             // change_managed
170     XtInheritInsertChild,                       // insert_child
171     XtInheritDeleteChild,                       // delete_child
172     NULL                                        // extension
173   },
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
182   },
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
196   },
197   { // XPDFTree
198     &createGC,                                  // createGC
199     &destroyGC,                                 // destroyGC
200     &layout,                                    // layout
201     &calcSize,                                  // calcSize
202     &needRelayout,                              // needRelayout
203     NULL                                        // extension
204   }
205 };
206
207 externaldef(xpdftreewidgetclass) WidgetClass xpdfTreeWidgetClass =
208   (WidgetClass)&xpdfTreeClassRec;
209
210 //------------------------------------------------------------------------
211
212 static void classPartInitialize(WidgetClass widgetCls) {
213   XPDFTreeWidgetClass wc = (XPDFTreeWidgetClass)widgetCls;
214   XPDFTreeWidgetClass sc = (XPDFTreeWidgetClass)wc->coreClass.superclass;
215
216   // method inheritance
217   if (wc->treeClass.createGC == XPDFInheritCreateGC) {
218     wc->treeClass.createGC = sc->treeClass.createGC;
219   }
220   if (wc->treeClass.destroyGC == XPDFInheritDestroyGC) {
221     wc->treeClass.destroyGC = sc->treeClass.destroyGC;
222   }
223   if (wc->treeClass.layout == XPDFInheritLayout) {
224     wc->treeClass.layout = sc->treeClass.layout;
225   }
226   if (wc->treeClass.calcSize == XPDFInheritCalcSize) {
227     wc->treeClass.calcSize = sc->treeClass.calcSize;
228   }
229   if (wc->treeClass.needRelayout == XPDFInheritNeedRelayout) {
230     wc->treeClass.needRelayout = sc->treeClass.needRelayout;
231   }
232 }
233
234 static void initialize(Widget requestWidget, Widget newWidget,
235                        ArgList args, Cardinal *numArgs) {
236   XPDFTreeWidget nw = (XPDFTreeWidget)newWidget;
237   XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(newWidget);
238
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);
245   }
246 }
247
248 static void destroy(Widget widget) {
249   XPDFTreeWidget w = (XPDFTreeWidget)widget;
250   XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
251
252   if (w->tree.root) {
253     destroySubtree(w->tree.root);
254     w->tree.root = NULL;
255   }
256   if (cls->treeClass.destroyGC) {
257     (*cls->treeClass.destroyGC)(widget);
258   } else {
259     destroyGC(widget);
260   }
261 }
262
263 static void destroySubtree(XPDFTreeEntry *e) {
264   if (e->children) {
265     destroySubtree(e->children);
266   }
267   if (e->next) {
268     destroySubtree(e->next);
269   }
270 }
271
272 static void resize(Widget widget) {
273   XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
274
275   if (cls->treeClass.layout) {
276     (*cls->treeClass.layout)(widget, NULL);
277   } else {
278     layout(widget, NULL);
279   }
280 }
281
282 static void redisplay(Widget widget, XEvent *event, Region region) {
283   XPDFTreeWidget w = (XPDFTreeWidget)widget;
284   XPDFTreeEntry *e;
285
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;
290   }
291   for (e = w->tree.root; e; e = e->next) {
292     redisplaySubtree(w, e, event, region);
293   }
294 }
295
296 static void redisplaySubtree(XPDFTreeWidget w, XPDFTreeEntry *e,
297                              XEvent *event, Region region) {
298   XPDFTreeConstraint c;
299   Position x, y, y2;
300   XPDFTreeEntry *child;
301
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);
315       }
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);
320     }
321   }
322 }
323
324 static void drawExpandedIcon(XPDFTreeWidget w, Position x, Position y) {
325   XPoint pts[4];
326
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);
333 }
334
335 static void drawCollapsedIcon(XPDFTreeWidget w, Position x, Position y) {
336   XPoint pts[4];
337
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);
344 }
345
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;
352
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);
358   }
359   redisp = False;
360   if (relayout) {
361
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;
366     }
367     if (nw->core.height == ow->core.height) {
368       nw->core.height = 0;
369     }
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);
375     }
376
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);
385       }
386       redisp = True;
387     }
388   }
389
390   return redisp;
391 }
392
393 static void setValuesAlmost(Widget oldWidget, Widget newWidget,
394                             XtWidgetGeometry *request,
395                             XtWidgetGeometry *reply) {
396   XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(newWidget);
397
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);
405     }
406   }
407   *request = *reply;
408 }
409
410 static XtGeometryResult queryGeometry(Widget widget,
411                                       XtWidgetGeometry *request,
412                                       XtWidgetGeometry *reply) {
413   XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
414
415   if (!XtIsRealized(widget)) {
416     reply->width = XtWidth(widget);
417     reply->height = XtHeight(widget);
418   } else {
419     reply->width = 0;
420     reply->height = 0;
421   }
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);
426   }
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;
435   }
436   if (reply->width == XtWidth(widget) &&
437       reply->height == XtHeight(widget)) {
438     return XtGeometryNo;
439   }
440   reply->request_mode = CWWidth | CWHeight;
441   return XtGeometryAlmost;
442 #endif
443 }
444
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;
453
454   // deny any requests for a new position
455   if ((request->request_mode & CWX) || (request->request_mode & CWY)) {
456     return XtGeometryNo;
457   }
458
459   // save the current geometry
460   curWidth = w->core.width;
461   curHeight = w->core.height;
462   curBW = w->core.border_width;
463
464   // make the requested changes
465   if (request->request_mode & CWWidth) {
466     w->core.width = request->width;
467   }
468   if (request->request_mode & CWHeight) {
469     w->core.height = request->height;
470   }
471   if (request->request_mode & CWBorderWidth) {
472     w->core.border_width = request->border_width;
473   }
474
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);
483   }
484
485   // send geometry request to our parent
486   parentReq.request_mode = CWWidth | CWHeight;
487   if (request->request_mode & XtCWQueryOnly) {
488     parentReq.request_mode |= XtCWQueryOnly;
489   }
490   result = XtMakeGeometryRequest((Widget)w, &parentReq, NULL);
491   if (result == XtGeometryAlmost) {
492     result = XtGeometryNo;
493   }
494
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);
505     }
506   }
507
508   return result;
509 }
510
511 static void changeManaged(Widget widget) {
512   Dimension width, height;
513   XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
514
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;
522   }
523   if (cls->treeClass.calcSize) {
524     (*cls->treeClass.calcSize)(widget, NULL, &width, &height);
525   } else {
526     calcSize(widget, NULL, &width, &height);
527   }
528
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) ;
533
534   // relayout
535   if (cls->treeClass.layout) {
536     (*cls->treeClass.layout)(widget, NULL);
537   } else {
538     layout(widget, NULL);
539   }
540
541 #if XmVERSION > 1
542   // update keyboard traversal
543   XmeNavigChangeManaged(widget);
544 #else
545   _XmNavigChangeManaged(widget);
546 #endif
547 }
548
549 static void initConstraint(Widget requestWidget, Widget newWidget,
550                            ArgList args, Cardinal *numArgs) {
551   XPDFTreeWidget w = (XPDFTreeWidget)XtParent(newWidget);
552   XPDFTreeConstraint c;
553
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);
563   }
564 }
565
566 static void destroyConstraint(Widget widget) {
567   deleteSubtree(widget);
568 }
569
570 static void deleteSubtree(Widget widget) {
571   XPDFTreeWidget w = (XPDFTreeWidget)XtParent(widget);
572   XPDFTreeConstraint c;
573
574   c = XPDFTreeCPart(widget);
575   if (!c->e) {
576     return;
577   }
578   while (c->e->children) {
579     deleteSubtree(c->e->children->widget);
580   }
581   if (c->entryParent) {
582     deleteChildFromList(c->e, &XPDFTreeCPart(c->entryParent)->e->children);
583   } else {
584     deleteChildFromList(c->e, &w->tree.root);
585   }
586   gfree(c->e);
587   c->e = NULL;
588 }
589
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;
598
599   if (!XtIsManaged(newWidget)) {
600     return False;
601   }
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);
611     }
612     if (nc->entryParent) {
613       insertChildOnList(nc->e, &XPDFTreeCPart(nc->entryParent)->e->children);
614     } else {
615       insertChildOnList(nc->e, &w->tree.root);
616     }
617     relayout = True;
618   } else if (nc->entryExpanded != oc->entryExpanded) {
619     relayout = True;
620   }
621
622   if (relayout) {
623
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);
632     }
633
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) ;
638
639     // relayout the widget
640     if (cls->treeClass.layout) {
641       (*cls->treeClass.layout)((Widget)w, NULL);
642     } else {
643       layout((Widget)w, NULL);
644     }
645   }
646
647   return relayout;
648 }
649
650 static void insertChildOnList(XPDFTreeEntry *e, XPDFTreeEntry **listHead) {
651   int pos;
652   XPDFTreeEntry *e2;
653
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;
664   }
665 }
666
667 static void deleteChildFromList(XPDFTreeEntry *e, XPDFTreeEntry **listHead) {
668   XPDFTreeEntry **p;
669
670   for (p = listHead; *p; p = &(*p)->next) {
671     if (*p == e) {
672       *p = e->next;
673       e->next = NULL;
674       return;
675     }
676   }
677 }
678
679 static void createGC(Widget widget) {
680   XPDFTreeWidget w = (XPDFTreeWidget)widget;
681   XGCValues gcValues;
682
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);
689
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);
697 }
698
699 static void destroyGC(Widget widget) {
700   XPDFTreeWidget w = (XPDFTreeWidget)widget;
701
702   XtReleaseGC(widget, w->tree.plainGC);
703   XtReleaseGC(widget, w->tree.dottedGC);
704 }
705
706 static void layout(Widget widget, Widget instigator) {
707   XPDFTreeWidget w = (XPDFTreeWidget)widget;
708   XPDFTreeEntry *e;
709   Position x, y;
710
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);
715   }
716 }
717
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;
724
725   ew = e->widget;
726   if (!XtIsManaged(ew)) {
727     return y;
728   }
729   c = XPDFTreeCPart(ew);
730
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
745       }
746       y += ew->core.height + 2 * ew->core.border_width;
747     }
748   }
749
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));
755   }
756
757   return y;
758 }
759
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;
766
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;
772     }
773     h1 += h2;
774   }
775   w1 += xpdfTreeIndent + 2 * w->tree.marginWidth;
776   h1 += 2 * w->tree.marginHeight;
777   if (h1 == 0) {
778     h1 = 1;
779   }
780   if (!*totalWidth) {
781     *totalWidth = w1;
782   }
783   if (!*totalHeight) {
784     *totalHeight = h1;
785   }
786 }
787
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;
796   
797   ew = e->widget;
798   if (!XtIsManaged(ew)) {
799     *width = *height = 0;
800     return;
801   }
802   c = XPDFTreeCPart(ew);
803
804   // get size of this entry
805   if (ew) {
806     if (!XtIsManaged(ew)) {
807       *width = *height = 0;
808       return;
809     }
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;
817     }
818     h1 += 2 * ew->core.border_width;
819   } else {
820     // root of tree
821     w1 = 0;
822     h1 = 0;
823   }
824
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;
832       }
833       h1 += h2;
834     }
835   }
836
837   *width = w1;
838   *height = h1;
839 }
840
841 static Boolean needRelayout(Widget oldWidget, Widget newWidget) {
842   XPDFTreeWidget ow = (XPDFTreeWidget)oldWidget;
843   XPDFTreeWidget nw = (XPDFTreeWidget)newWidget;
844
845   if (nw->tree.marginWidth != ow->tree.marginWidth ||
846       nw->tree.marginHeight != ow->tree.marginHeight) {
847     return True;
848   }
849   return False;
850 }
851
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;
860
861   if (event->type != ButtonPress) {
862     return;
863   }
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);
877     }
878   }
879 }
880
881 static Boolean findPosition(XPDFTreeWidget w, int x, int y,
882                             XPDFTreeEntry **e, Boolean *onExpandIcon) {
883   XPDFTreeEntry *e2;
884
885   for (e2 = w->tree.root; e2; e2 = e2->next) {
886     *e = e2;
887     if (findPositionInSubtree(w, x, y, e, onExpandIcon)) {
888       return True;
889     }
890   }
891   return False;
892 }
893
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;
903
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;
915   }
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;
922       }
923     }
924   }
925   return False;
926 }
927
928 Widget XPDFCreateTree(Widget parent, char *name,
929                       ArgList argList, Cardinal numArgs) {
930   return XtCreateWidget(name, xpdfTreeWidgetClass, parent, argList, numArgs);
931 }