filters

XPDFTree.cpp
1//========================================================================
2//
3// XPDFTree.cpp
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
17struct _XPDFTreeEntry {
18 Widget widget;
19 XPDFTreeEntry *children;
20 XPDFTreeEntry *next;
21};
22
23//------------------------------------------------------------------------
24
25static void classPartInitialize(WidgetClass widgetClass);
26static void initialize(Widget requestWidget, Widget newWidget,
27 ArgList args, Cardinal *numArgs);
28static void destroy(Widget widget);
29static void destroySubtree(XPDFTreeEntry *e);
30static void resize(Widget widget);
31static void redisplay(Widget widget, XEvent *event, Region region);
32static void redisplaySubtree(XPDFTreeWidget w, XPDFTreeEntry *e,
33 XEvent *event, Region region);
34static void drawExpandedIcon(XPDFTreeWidget w, Position x, Position y);
35static void drawCollapsedIcon(XPDFTreeWidget w, Position x, Position y);
36static Boolean setValues(Widget oldWidget, Widget requestWidget,
37 Widget newWidget, ArgList args, Cardinal *numArgs);
38static void setValuesAlmost(Widget oldWidget, Widget newWidget,
39 XtWidgetGeometry *request,
40 XtWidgetGeometry *reply);
41static XtGeometryResult queryGeometry(Widget widget,
42 XtWidgetGeometry *request,
43 XtWidgetGeometry *reply);
44static XtGeometryResult geometryManager(Widget widget,
45 XtWidgetGeometry *request,
46 XtWidgetGeometry *reply);
47static void changeManaged(Widget widget);
48static void initConstraint(Widget requestWidget, Widget newWidget,
49 ArgList args, Cardinal *numArgs);
50static void destroyConstraint(Widget widget);
51static void deleteSubtree(Widget widget);
52static Boolean constraintSetValues(Widget oldWidget, Widget requestWidget,
53 Widget newWidget,
54 ArgList args, Cardinal *numArgs);
55static void insertChildOnList(XPDFTreeEntry *e, XPDFTreeEntry **listHead);
56static void deleteChildFromList(XPDFTreeEntry *e, XPDFTreeEntry **listHead);
57static void createGC(Widget widget);
58static void destroyGC(Widget widget);
59static void layout(Widget widget, Widget instigator);
60static int layoutSubtree(XPDFTreeWidget w, Widget instigator,
61 XPDFTreeEntry *e, Position x, Position y,
62 Boolean visible);
63static void calcSize(Widget widget, Widget instigator,
64 Dimension *totalWidth,
65 Dimension *totalHeight);
66static void calcSubtreeSize(XPDFTreeWidget w, Widget instigator,
67 XPDFTreeEntry *e,
68 Dimension *width, Dimension *height);
69static Boolean needRelayout(Widget oldWidget, Widget newWidget);
70static void click(Widget widget, XEvent *event,
71 String *params, Cardinal *numParams);
72static Boolean findPosition(XPDFTreeWidget w, int x, int y,
73 XPDFTreeEntry **e, Boolean *onExpandIcon);
74static Boolean findPositionInSubtree(XPDFTreeWidget w, int x, int y,
75 XPDFTreeEntry **e,
76 Boolean *onExpandIcon);
77
78//------------------------------------------------------------------------
79
80static 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
92static 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
111static 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
123static char defaultTranslations[] =
124 "<Btn1Down>: XPDFTreeClick()";
125
126static XtActionsRec actions[] = {
127 { "XPDFTreeClick", click }
128};
129
130externaldef(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
205externaldef(xpdftreewidgetclass) WidgetClass xpdfTreeWidgetClass =
206 (WidgetClass)&xpdfTreeClassRec;
207
208//------------------------------------------------------------------------
209
210static 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
232static 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
246static 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
261static void destroySubtree(XPDFTreeEntry *e) {
262 if (e->children) {
263 destroySubtree(e->children);
264 }
265 if (e->next) {
266 destroySubtree(e->next);
267 }
268}
269
270static 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
280static 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
294static 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
322static 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
333static 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
344static 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
391static 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
408static 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
443static 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
509static 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
547static 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
564static void destroyConstraint(Widget widget) {
565 deleteSubtree(widget);
566}
567
568static 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
588static 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
648static 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
665static 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
677static 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
697static void destroyGC(Widget widget) {
698 XPDFTreeWidget w = (XPDFTreeWidget)widget;
699
700 XtReleaseGC(widget, w->tree.plainGC);
701 XtReleaseGC(widget, w->tree.dottedGC);
702}
703
704static 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
716static 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
758static 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
786static 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
839static 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
850static 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
879static 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.
894static 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
926Widget XPDFCreateTree(Widget parent, char *name,
927 ArgList argList, Cardinal numArgs) {
928 return XtCreateWidget(name, xpdfTreeWidgetClass, parent, argList, numArgs);
929}