filters

XPDFTree.cc

00001 //========================================================================
00002 //
00003 // XPDFTree.cc
00004 //
00005 //========================================================================
00006 
00007 #include <stdlib.h>
00008 #include "gmem.h"
00009 #include "XPDFTreeP.h"
00010 
00011 //------------------------------------------------------------------------
00012 
00013 #define xpdfTreeIndent 16
00014 
00015 //------------------------------------------------------------------------
00016 
00017 struct _XPDFTreeEntry {
00018   Widget widget;
00019   XPDFTreeEntry *children;
00020   XPDFTreeEntry *next;
00021 };
00022 
00023 //------------------------------------------------------------------------
00024 
00025 static void classPartInitialize(WidgetClass widgetClass);
00026 static void initialize(Widget requestWidget, Widget newWidget,
00027                ArgList args, Cardinal *numArgs);
00028 static void destroy(Widget widget);
00029 static void destroySubtree(XPDFTreeEntry *e);
00030 static void resize(Widget widget);
00031 static void redisplay(Widget widget, XEvent *event, Region region);
00032 static void redisplaySubtree(XPDFTreeWidget w, XPDFTreeEntry *e,
00033                  XEvent *event, Region region);
00034 static void drawExpandedIcon(XPDFTreeWidget w, Position x, Position y);
00035 static void drawCollapsedIcon(XPDFTreeWidget w, Position x, Position y);
00036 static Boolean setValues(Widget oldWidget, Widget requestWidget,
00037              Widget newWidget, ArgList args, Cardinal *numArgs);
00038 static void setValuesAlmost(Widget oldWidget, Widget newWidget,
00039                 XtWidgetGeometry *request,
00040                 XtWidgetGeometry *reply);
00041 static XtGeometryResult queryGeometry(Widget widget,
00042                       XtWidgetGeometry *request,
00043                       XtWidgetGeometry *reply);
00044 static XtGeometryResult geometryManager(Widget widget,
00045                     XtWidgetGeometry *request,
00046                     XtWidgetGeometry *reply);
00047 static void changeManaged(Widget widget);
00048 static void initConstraint(Widget requestWidget, Widget newWidget,
00049                ArgList args, Cardinal *numArgs);
00050 static void destroyConstraint(Widget widget);
00051 static void deleteSubtree(Widget widget);
00052 static Boolean constraintSetValues(Widget oldWidget, Widget requestWidget,
00053                    Widget newWidget,
00054                    ArgList args, Cardinal *numArgs);
00055 static void insertChildOnList(XPDFTreeEntry *e, XPDFTreeEntry **listHead);
00056 static void deleteChildFromList(XPDFTreeEntry *e, XPDFTreeEntry **listHead);
00057 static void createGC(Widget widget);
00058 static void destroyGC(Widget widget);
00059 static void layout(Widget widget, Widget instigator);
00060 static int layoutSubtree(XPDFTreeWidget w, Widget instigator,
00061              XPDFTreeEntry *e, Position x, Position y,
00062              Boolean visible);
00063 static void calcSize(Widget widget, Widget instigator,
00064              Dimension *totalWidth,
00065              Dimension *totalHeight);
00066 static void calcSubtreeSize(XPDFTreeWidget w, Widget instigator,
00067                 XPDFTreeEntry *e,
00068                 Dimension *width, Dimension *height);
00069 static Boolean needRelayout(Widget oldWidget, Widget newWidget);
00070 static void click(Widget widget, XEvent *event,
00071           String *params, Cardinal *numParams);
00072 static Boolean findPosition(XPDFTreeWidget w, int x, int y,
00073                 XPDFTreeEntry **e, Boolean *onExpandIcon);
00074 static Boolean findPositionInSubtree(XPDFTreeWidget w, int x, int y,
00075                      XPDFTreeEntry **e,
00076                      Boolean *onExpandIcon);
00077 
00078 //------------------------------------------------------------------------
00079 
00080 static XtResource resources[] = {
00081   { XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension,
00082     sizeof(Dimension), XtOffsetOf(XPDFTreeRec, tree.marginWidth),
00083     XmRImmediate, (XtPointer)0 },
00084   { XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension,
00085     sizeof(Dimension), XtOffsetOf(XPDFTreeRec, tree.marginHeight),
00086     XmRImmediate, (XtPointer)0 },
00087   { XPDFNselectionCallback, XmCCallback, XmRCallback,
00088     sizeof(XtCallbackList), XtOffsetOf(XPDFTreeRec, tree.selectCallback),
00089     XmRImmediate, (XtPointer)NULL }
00090 };
00091 
00092 static XmSyntheticResource synResources[] = {
00093   { XmNmarginWidth, sizeof(Dimension),
00094     XtOffsetOf(XPDFTreeRec, tree.marginWidth),
00095 #if XmVERSION > 1
00096     XmeFromHorizontalPixels, XmeToHorizontalPixels
00097 #else
00098     _XmFromHorizontalPixels, _XmToHorizontalPixels
00099 #endif
00100   },
00101   { XmNmarginHeight, sizeof(Dimension),
00102     XtOffsetOf(XPDFTreeRec, tree.marginHeight),
00103 #if XmVERSION > 1
00104     XmeFromVerticalPixels, XmeToVerticalPixels
00105 #else
00106     _XmFromVerticalPixels, _XmToVerticalPixels
00107 #endif
00108   }
00109 };
00110 
00111 static XtResource constraints[] = {
00112   { XPDFNentryParent, XPDFCentryParent, XmRWidget,
00113     sizeof(Widget), XtOffsetOf(XPDFTreeConstraintRec, tree.entryParent),
00114     XmRImmediate, (XtPointer)NULL },
00115   { XPDFNentryExpanded, XPDFCentryExpanded, XmRBoolean,
00116     sizeof(Boolean), XtOffsetOf(XPDFTreeConstraintRec, tree.entryExpanded),
00117     XmRImmediate, (XtPointer)False },
00118   { XPDFNentryPosition, XPDFCentryPosition, XmRInt,
00119     sizeof(int), XtOffsetOf(XPDFTreeConstraintRec, tree.entryPosition),
00120     XmRImmediate, (XtPointer)0 }
00121 };
00122 
00123 static char defaultTranslations[] =
00124   "<Btn1Down>: XPDFTreeClick()";
00125 
00126 static XtActionsRec actions[] = {
00127   { "XPDFTreeClick", click }
00128 };
00129 
00130 externaldef(xpdftreeclassrec) XPDFTreeClassRec xpdfTreeClassRec = {
00131   { // Core
00132     (WidgetClass)&xmManagerClassRec,        // superclass
00133     "XPDFTree",                 // class_name
00134     sizeof(XPDFTreeRec),            // widget_size
00135     NULL,                   // class_initialize
00136     &classPartInitialize,           // class_part_initialize
00137     FALSE,                  // class_inited
00138     &initialize,                // initialize
00139     NULL,                   // initialize_hook
00140     XtInheritRealize,               // realize
00141     actions,                    // actions
00142     XtNumber(actions),              // num_actions
00143     resources,                  // resources
00144     XtNumber(resources),            // num_resources
00145     NULLQUARK,                  // xrm_class
00146     TRUE,                   // compress_motion
00147     XtExposeCompressMaximal,            // compress_exposure
00148     TRUE,                   // compress_enterleave
00149     FALSE,                  // visible_interest
00150     &destroy,                   // destroy
00151     &resize,                    // resize
00152     &redisplay,                 // expose
00153     &setValues,                 // set_values
00154     NULL,                   // set_values_hook
00155     &setValuesAlmost,               // set_values_almost
00156     NULL,                   // get_values_hook
00157     NULL,                   // accept_focus
00158     XtVersion,                  // version
00159     NULL,                   // callback_private
00160     defaultTranslations,            // tm_table
00161     &queryGeometry,             // query_geometry
00162     NULL,                   // display_accelerator
00163     NULL                    // extension
00164   },
00165   { // Composite
00166     &geometryManager,               // geometry_manager
00167     &changeManaged,             // change_managed
00168     XtInheritInsertChild,           // insert_child
00169     XtInheritDeleteChild,           // delete_child
00170     NULL                    // extension
00171   },
00172   { // Constraint
00173     constraints,                // constraint_resources
00174     XtNumber(constraints),          // constraint_num_resources
00175     sizeof(XPDFTreeConstraintRec),      // constraint_size
00176     &initConstraint,                // constraint_initialize
00177     &destroyConstraint,             // constraint_destroy
00178     &constraintSetValues,           // constraint_set_values
00179     NULL                    // extension
00180   },
00181   { // XmManager
00182     XtInheritTranslations,          // translations
00183 #if XmVERSION > 1
00184     synResources,               // syn_resources
00185     XtNumber(synResources),         // num_syn_resources
00186 #else
00187     NULL,                   // syn_resources
00188     0,                      // num_syn_resources
00189 #endif
00190     NULL,                   // syn_constraint_resources
00191     0,                      // num_syn_constraint_res's
00192     XmInheritParentProcess,         // parent_process
00193     NULL                    // extension
00194   },
00195   { // XPDFTree
00196     &createGC,                  // createGC
00197     &destroyGC,                 // destroyGC
00198     &layout,                    // layout
00199     &calcSize,                  // calcSize
00200     &needRelayout,              // needRelayout
00201     NULL                    // extension
00202   }
00203 };
00204 
00205 externaldef(xpdftreewidgetclass) WidgetClass xpdfTreeWidgetClass =
00206   (WidgetClass)&xpdfTreeClassRec;
00207 
00208 //------------------------------------------------------------------------
00209 
00210 static void classPartInitialize(WidgetClass widgetCls) {
00211   XPDFTreeWidgetClass wc = (XPDFTreeWidgetClass)widgetCls;
00212   XPDFTreeWidgetClass sc = (XPDFTreeWidgetClass)wc->coreClass.superclass;
00213 
00214   // method inheritance
00215   if (wc->treeClass.createGC == XPDFInheritCreateGC) {
00216     wc->treeClass.createGC = sc->treeClass.createGC;
00217   }
00218   if (wc->treeClass.destroyGC == XPDFInheritDestroyGC) {
00219     wc->treeClass.destroyGC = sc->treeClass.destroyGC;
00220   }
00221   if (wc->treeClass.layout == XPDFInheritLayout) {
00222     wc->treeClass.layout = sc->treeClass.layout;
00223   }
00224   if (wc->treeClass.calcSize == XPDFInheritCalcSize) {
00225     wc->treeClass.calcSize = sc->treeClass.calcSize;
00226   }
00227   if (wc->treeClass.needRelayout == XPDFInheritNeedRelayout) {
00228     wc->treeClass.needRelayout = sc->treeClass.needRelayout;
00229   }
00230 }
00231 
00232 static void initialize(Widget requestWidget, Widget newWidget,
00233                ArgList args, Cardinal *numArgs) {
00234   XPDFTreeWidget nw = (XPDFTreeWidget)newWidget;
00235   XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(newWidget);
00236 
00237   nw->tree.root = NULL;
00238   nw->tree.redrawY = -1;
00239   if (cls->treeClass.createGC) {
00240     (*cls->treeClass.createGC)(newWidget);
00241   } else {
00242     createGC(newWidget);
00243   }
00244 }
00245 
00246 static void destroy(Widget widget) {
00247   XPDFTreeWidget w = (XPDFTreeWidget)widget;
00248   XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
00249 
00250   if (w->tree.root) {
00251     destroySubtree(w->tree.root);
00252     w->tree.root = NULL;
00253   }
00254   if (cls->treeClass.destroyGC) {
00255     (*cls->treeClass.destroyGC)(widget);
00256   } else {
00257     destroyGC(widget);
00258   }
00259 }
00260 
00261 static void destroySubtree(XPDFTreeEntry *e) {
00262   if (e->children) {
00263     destroySubtree(e->children);
00264   }
00265   if (e->next) {
00266     destroySubtree(e->next);
00267   }
00268 }
00269 
00270 static void resize(Widget widget) {
00271   XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
00272 
00273   if (cls->treeClass.layout) {
00274     (*cls->treeClass.layout)(widget, NULL);
00275   } else {
00276     layout(widget, NULL);
00277   }
00278 }
00279 
00280 static void redisplay(Widget widget, XEvent *event, Region region) {
00281   XPDFTreeWidget w = (XPDFTreeWidget)widget;
00282   XPDFTreeEntry *e;
00283 
00284   if (w->tree.redrawY >= 0) {
00285     XClearArea(XtDisplay((Widget)w), XtWindow((Widget)w),
00286            0, w->tree.redrawY, w->core.width, w->core.height, False);
00287     w->tree.redrawY = -1;
00288   }
00289   for (e = w->tree.root; e; e = e->next) {
00290     redisplaySubtree(w, e, event, region);
00291   }
00292 }
00293 
00294 static void redisplaySubtree(XPDFTreeWidget w, XPDFTreeEntry *e,
00295                  XEvent *event, Region region) {
00296   XPDFTreeConstraint c;
00297   Position x, y, y2;
00298   XPDFTreeEntry *child;
00299 
00300   (*XtClass(e->widget)->core_class.expose)(e->widget, event, region);
00301   c = XPDFTreeCPart(e->widget);
00302   x = e->widget->core.x;
00303   y = e->widget->core.y + e->widget->core.height / 2;
00304   if (e->children) {
00305     if (c->entryExpanded) {
00306       drawExpandedIcon(w, x - 8, y);
00307       y2 = y; // make gcc happy
00308       for (child = e->children; child; child = child->next) {
00309     y2 = child->widget->core.y + child->widget->core.height / 2;
00310     XDrawLine(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.dottedGC,
00311           x - 8, y2, x + 6, y2);
00312     redisplaySubtree(w, child, event, region);
00313       }
00314       XDrawLine(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.dottedGC,
00315         x - 8, y + 2, x - 8, y2);
00316     } else {
00317       drawCollapsedIcon(w, x - 8, y);
00318     }
00319   }
00320 }
00321 
00322 static void drawExpandedIcon(XPDFTreeWidget w, Position x, Position y) {
00323   XPoint pts[4];
00324 
00325   pts[0].x = x - 4;    pts[0].y = y - 2;
00326   pts[1].x = x + 4;    pts[1].y = y - 2;
00327   pts[2].x = x;        pts[2].y = y + 2;
00328   pts[3].x = x - 4;    pts[3].y = y - 2;
00329   XDrawLines(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.plainGC,
00330          pts, 4, CoordModeOrigin);
00331 }
00332 
00333 static void drawCollapsedIcon(XPDFTreeWidget w, Position x, Position y) {
00334   XPoint pts[4];
00335 
00336   pts[0].x = x - 2;    pts[0].y = y - 4;
00337   pts[1].x = x - 2;    pts[1].y = y + 4;
00338   pts[2].x = x + 2;    pts[2].y = y;
00339   pts[3].x = x - 2;    pts[3].y = y - 4;
00340   XDrawLines(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.plainGC,
00341          pts, 4, CoordModeOrigin);
00342 }
00343 
00344 static Boolean setValues(Widget oldWidget, Widget requestWidget,
00345              Widget newWidget, ArgList args, Cardinal *numArgs) {
00346   XPDFTreeWidget ow = (XPDFTreeWidget)oldWidget;
00347   XPDFTreeWidget nw = (XPDFTreeWidget)newWidget;
00348   XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(nw);
00349   Boolean relayout, redisp;
00350 
00351   // check to see if layout-affecting resources have changed
00352   if (cls->treeClass.needRelayout) {
00353     relayout = (*cls->treeClass.needRelayout)((Widget)ow, (Widget)nw);
00354   } else {
00355     relayout = needRelayout((Widget)ow, (Widget)nw);
00356   }
00357   redisp = False;
00358   if (relayout) {
00359 
00360     // calculate a new ideal size (reset the widget size first so
00361     // calcSize will compute a new one)
00362     if (nw->core.width == ow->core.width) {
00363       nw->core.width = 0;
00364     }
00365     if (nw->core.height == ow->core.height) {
00366       nw->core.height = 0;
00367     }
00368     if (cls->treeClass.calcSize) {
00369       (*cls->treeClass.calcSize)((Widget)nw, NULL,
00370                  &nw->core.width, &nw->core.height);
00371     } else {
00372       calcSize((Widget)nw, NULL, &nw->core.width, &nw->core.height);
00373     }
00374 
00375     // if resources have changed but size hasn't, layout manually
00376     // (because Xt just looks at the size)
00377     if (nw->core.width == ow->core.width &&
00378     nw->core.height == ow->core.height) {
00379       if (cls->treeClass.layout) {
00380     (*cls->treeClass.layout)((Widget)nw, NULL);
00381       } else {
00382     layout((Widget)nw, NULL);
00383       }
00384       redisp = True;
00385     }
00386   }
00387 
00388   return redisp;
00389 }
00390 
00391 static void setValuesAlmost(Widget oldWidget, Widget newWidget,
00392                 XtWidgetGeometry *request,
00393                 XtWidgetGeometry *reply) {
00394   XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(newWidget);
00395 
00396   // our parent rejected a geometry request, so accept the compromise
00397   // and relayout
00398   if (!reply->request_mode) {
00399     if (cls->treeClass.layout) {
00400       (*cls->treeClass.layout)(newWidget, NULL);
00401     } else {
00402       layout(newWidget, NULL);
00403     }
00404   }
00405   *request = *reply;
00406 }
00407 
00408 static XtGeometryResult queryGeometry(Widget widget,
00409                       XtWidgetGeometry *request,
00410                       XtWidgetGeometry *reply) {
00411   XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
00412 
00413   if (!XtIsRealized(widget)) {
00414     reply->width = XtWidth(widget);
00415     reply->height = XtHeight(widget);
00416   } else {
00417     reply->width = 0;
00418     reply->height = 0;
00419   }
00420   if (cls->treeClass.calcSize) {
00421     (*cls->treeClass.calcSize)(widget, NULL, &reply->width, &reply->height);
00422   } else {
00423     calcSize(widget, NULL, &reply->width, &reply->height);
00424   }
00425 #if XmVERSION > 1
00426   return XmeReplyToQueryGeometry(widget, request, reply);
00427 #else
00428   if ((request->request_mode & CWWidth) &&
00429       (request->request_mode & CWHeight) &&
00430       request->width == reply->width &&
00431       request->height == reply->height) {
00432     return XtGeometryYes;
00433   }
00434   if (reply->width == XtWidth(widget) &&
00435       reply->height == XtHeight(widget)) {
00436     return XtGeometryNo;
00437   }
00438   reply->request_mode = CWWidth | CWHeight;
00439   return XtGeometryAlmost;
00440 #endif
00441 }
00442 
00443 static XtGeometryResult geometryManager(Widget widget,
00444                     XtWidgetGeometry *request,
00445                     XtWidgetGeometry *reply) {
00446   XPDFTreeWidget w = (XPDFTreeWidget)XtParent(widget);
00447   XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(w);
00448   Dimension curWidth, curHeight, curBW;
00449   XtWidgetGeometry parentReq;
00450   XtGeometryResult result;
00451 
00452   // deny any requests for a new position
00453   if ((request->request_mode & CWX) || (request->request_mode & CWY)) {
00454     return XtGeometryNo;
00455   }
00456 
00457   // save the current geometry
00458   curWidth = w->core.width;
00459   curHeight = w->core.height;
00460   curBW = w->core.border_width;
00461 
00462   // make the requested changes
00463   if (request->request_mode & CWWidth) {
00464     w->core.width = request->width;
00465   }
00466   if (request->request_mode & CWHeight) {
00467     w->core.height = request->height;
00468   }
00469   if (request->request_mode & CWBorderWidth) {
00470     w->core.border_width = request->border_width;
00471   }
00472 
00473   // calculate the new ideal size
00474   parentReq.width = 0;
00475   parentReq.height = 0;
00476   if (cls->treeClass.calcSize) {
00477     (*cls->treeClass.calcSize)((Widget)w, widget,
00478                    &parentReq.width, &reply->height);
00479   } else {
00480     calcSize((Widget)w, widget, &parentReq.width, &reply->height);
00481   }
00482 
00483   // send geometry request to our parent
00484   parentReq.request_mode = CWWidth | CWHeight;
00485   if (request->request_mode & XtCWQueryOnly) {
00486     parentReq.request_mode |= XtCWQueryOnly;
00487   }
00488   result = XtMakeGeometryRequest((Widget)w, &parentReq, NULL);
00489   if (result == XtGeometryAlmost) {
00490     result = XtGeometryNo;
00491   }
00492 
00493   if (result == XtGeometryNo || (request->request_mode & XtCWQueryOnly)) {
00494     // restore the original geometry
00495     w->core.width = curWidth;
00496     w->core.height = curHeight;
00497     w->core.border_width = curBW;
00498   } else {
00499     if (cls->treeClass.layout) {
00500       (*cls->treeClass.layout)((Widget)w, widget);
00501     } else {
00502       layout((Widget)w, widget);
00503     }
00504   }
00505 
00506   return result;
00507 }
00508 
00509 static void changeManaged(Widget widget) {
00510   Dimension width, height;
00511   XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
00512 
00513   // compute the ideal size
00514   if (!XtIsRealized(widget)) {
00515     width = XtWidth(widget);
00516     height = XtHeight(widget);
00517   } else {
00518     width = 0;
00519     height = 0;
00520   }
00521   if (cls->treeClass.calcSize) {
00522     (*cls->treeClass.calcSize)(widget, NULL, &width, &height);
00523   } else {
00524     calcSize(widget, NULL, &width, &height);
00525   }
00526 
00527   // make resize request to parent -- keep asking until we get a yes
00528   // or no
00529   while (XtMakeResizeRequest(widget, width, height, &width, &height)
00530      == XtGeometryAlmost) ;
00531 
00532   // relayout
00533   if (cls->treeClass.layout) {
00534     (*cls->treeClass.layout)(widget, NULL);
00535   } else {
00536     layout(widget, NULL);
00537   }
00538 
00539 #if XmVERSION > 1
00540   // update keyboard traversal
00541   XmeNavigChangeManaged(widget);
00542 #else
00543   _XmNavigChangeManaged(widget);
00544 #endif
00545 }
00546 
00547 static void initConstraint(Widget requestWidget, Widget newWidget,
00548                ArgList args, Cardinal *numArgs) {
00549   XPDFTreeWidget w = (XPDFTreeWidget)XtParent(newWidget);
00550   XPDFTreeConstraint c;
00551 
00552   c = XPDFTreeCPart(newWidget);
00553   c->e = (XPDFTreeEntry *)gmalloc(sizeof(XPDFTreeEntry));
00554   c->e->widget = newWidget;
00555   c->e->children = NULL;
00556   c->e->next = NULL;
00557   if (c->entryParent) {
00558     insertChildOnList(c->e, &XPDFTreeCPart(c->entryParent)->e->children);
00559   } else {
00560     insertChildOnList(c->e, &w->tree.root);
00561   }
00562 }
00563 
00564 static void destroyConstraint(Widget widget) {
00565   deleteSubtree(widget);
00566 }
00567 
00568 static void deleteSubtree(Widget widget) {
00569   XPDFTreeWidget w = (XPDFTreeWidget)XtParent(widget);
00570   XPDFTreeConstraint c;
00571 
00572   c = XPDFTreeCPart(widget);
00573   if (!c->e) {
00574     return;
00575   }
00576   while (c->e->children) {
00577     deleteSubtree(c->e->children->widget);
00578   }
00579   if (c->entryParent) {
00580     deleteChildFromList(c->e, &XPDFTreeCPart(c->entryParent)->e->children);
00581   } else {
00582     deleteChildFromList(c->e, &w->tree.root);
00583   }
00584   gfree(c->e);
00585   c->e = NULL;
00586 }
00587 
00588 static Boolean constraintSetValues(Widget oldWidget, Widget requestWidget,
00589                    Widget newWidget,
00590                    ArgList args, Cardinal *numArgs) {
00591   XPDFTreeWidget w = (XPDFTreeWidget)XtParent(newWidget);
00592   XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass((Widget)w);
00593   XPDFTreeConstraint oc, nc;
00594   Boolean relayout;
00595   Dimension width, height;
00596 
00597   if (!XtIsManaged(newWidget)) {
00598     return False;
00599   }
00600   oc = XPDFTreeCPart(oldWidget);
00601   nc = XPDFTreeCPart(newWidget);
00602   relayout = False;
00603   if (nc->entryParent != oc->entryParent ||
00604       nc->entryPosition != oc->entryPosition) {
00605     if (oc->entryParent) {
00606       deleteChildFromList(oc->e, &XPDFTreeCPart(oc->entryParent)->e->children);
00607     } else {
00608       deleteChildFromList(oc->e, &w->tree.root);
00609     }
00610     if (nc->entryParent) {
00611       insertChildOnList(nc->e, &XPDFTreeCPart(nc->entryParent)->e->children);
00612     } else {
00613       insertChildOnList(nc->e, &w->tree.root);
00614     }
00615     relayout = True;
00616   } else if (nc->entryExpanded != oc->entryExpanded) {
00617     relayout = True;
00618   }
00619 
00620   if (relayout) {
00621 
00622     // calculate a new ideal size (reset the widget size first so
00623     // calcSize will compute a new one)
00624     width = 0;
00625     height = 0;
00626     if (cls->treeClass.calcSize) {
00627       (*cls->treeClass.calcSize)((Widget)w, NULL, &width, &height);
00628     } else {
00629       calcSize((Widget)w, NULL, &width, &height);
00630     }
00631 
00632     // make resize request to parent -- keep asking until we get a yes
00633     // or no
00634     while (XtMakeResizeRequest((Widget)w, width, height, &width, &height)
00635        == XtGeometryAlmost) ;
00636 
00637     // relayout the widget
00638     if (cls->treeClass.layout) {
00639       (*cls->treeClass.layout)((Widget)w, NULL);
00640     } else {
00641       layout((Widget)w, NULL);
00642     }
00643   }
00644 
00645   return relayout;
00646 }
00647 
00648 static void insertChildOnList(XPDFTreeEntry *e, XPDFTreeEntry **listHead) {
00649   int pos;
00650   XPDFTreeEntry *e2;
00651 
00652   pos = XPDFTreeCPart(e->widget)->entryPosition;
00653   if (!*listHead || pos < XPDFTreeCPart((*listHead)->widget)->entryPosition) {
00654     e->next = *listHead;
00655     *listHead = e;
00656   } else {
00657     for (e2 = *listHead;
00658      e2->next && pos >= XPDFTreeCPart(e2->next->widget)->entryPosition;
00659      e2 = e2->next) ;
00660     e->next = e2->next;
00661     e2->next = e;
00662   }
00663 }
00664 
00665 static void deleteChildFromList(XPDFTreeEntry *e, XPDFTreeEntry **listHead) {
00666   XPDFTreeEntry **p;
00667 
00668   for (p = listHead; *p; p = &(*p)->next) {
00669     if (*p == e) {
00670       *p = e->next;
00671       e->next = NULL;
00672       return;
00673     }
00674   }
00675 }
00676 
00677 static void createGC(Widget widget) {
00678   XPDFTreeWidget w = (XPDFTreeWidget)widget;
00679   XGCValues gcValues;
00680 
00681   gcValues.foreground = w->manager.foreground;
00682   gcValues.line_width = 0;
00683   gcValues.line_style = LineSolid;
00684   w->tree.plainGC = XtGetGC(widget,
00685                 GCForeground | GCLineWidth | GCLineStyle,
00686                 &gcValues);
00687 
00688   gcValues.line_style = LineOnOffDash;
00689   gcValues.dashes = 1;
00690   gcValues.dash_offset = 0;
00691   w->tree.dottedGC = XtGetGC(widget,
00692                  GCForeground | GCLineWidth | GCLineStyle |
00693                      GCDashList | GCDashOffset,
00694                  &gcValues);
00695 }
00696 
00697 static void destroyGC(Widget widget) {
00698   XPDFTreeWidget w = (XPDFTreeWidget)widget;
00699 
00700   XtReleaseGC(widget, w->tree.plainGC);
00701   XtReleaseGC(widget, w->tree.dottedGC);
00702 }
00703 
00704 static void layout(Widget widget, Widget instigator) {
00705   XPDFTreeWidget w = (XPDFTreeWidget)widget;
00706   XPDFTreeEntry *e;
00707   Position x, y;
00708 
00709   x = w->tree.marginWidth + xpdfTreeIndent;
00710   y = w->tree.marginHeight;
00711   for (e = w->tree.root; e; e = e->next) {
00712     y = layoutSubtree(w, instigator, e, x, y, True);
00713   }
00714 }
00715 
00716 static int layoutSubtree(XPDFTreeWidget w, Widget instigator,
00717              XPDFTreeEntry *e, Position x, Position y,
00718              Boolean visible) {
00719   Widget ew;
00720   XPDFTreeEntry *child;
00721   XPDFTreeConstraint c;
00722 
00723   ew = e->widget;
00724   if (!XtIsManaged(ew)) {
00725     return y;
00726   }
00727   c = XPDFTreeCPart(ew);
00728 
00729   // place this entry
00730   if (ew) {
00731     if (visible) {
00732       if (ew == instigator) {
00733     ew->core.x = x;
00734     ew->core.y = y;
00735       } else {
00736 #if XmVERSION > 1
00737     XmeConfigureObject(ew, x, y, ew->core.width, ew->core.height,
00738                ew->core.border_width);
00739 #else
00740     _XmConfigureObject(ew, x, y, ew->core.width, ew->core.height,
00741                ew->core.border_width);
00742 #endif
00743       }
00744       y += ew->core.height + 2 * ew->core.border_width;
00745     }
00746   }
00747 
00748   // place this entry's children
00749   x += xpdfTreeIndent;
00750   for (child = e->children; child; child = child->next) {
00751     y = layoutSubtree(w, instigator, child, x, y,
00752               visible && (!c || c->entryExpanded));
00753   }
00754 
00755   return y;
00756 }
00757 
00758 static void calcSize(Widget widget, Widget instigator,
00759              Dimension *totalWidth,
00760              Dimension *totalHeight) {
00761   XPDFTreeWidget w = (XPDFTreeWidget)widget;
00762   XPDFTreeEntry *e;
00763   Dimension w1, h1, w2, h2;
00764 
00765   w1 = h1 = 0;
00766   for (e = w->tree.root; e; e = e->next) {
00767     calcSubtreeSize(w, instigator, e, &w2, &h2);
00768     if (w2 > w1) {
00769       w1 = w2;
00770     }
00771     h1 += h2;
00772   }
00773   w1 += xpdfTreeIndent + 2 * w->tree.marginWidth;
00774   h1 += 2 * w->tree.marginHeight;
00775   if (h1 == 0) {
00776     h1 = 1;
00777   }
00778   if (!*totalWidth) {
00779     *totalWidth = w1;
00780   }
00781   if (!*totalHeight) {
00782     *totalHeight = h1;
00783   }
00784 }
00785 
00786 static void calcSubtreeSize(XPDFTreeWidget w, Widget instigator,
00787                 XPDFTreeEntry *e,
00788                 Dimension *width, Dimension *height) {
00789   Widget ew;
00790   XPDFTreeEntry *child;
00791   XPDFTreeConstraint c;
00792   XtWidgetGeometry geom;
00793   Dimension w1, h1, w2, h2;
00794   
00795   ew = e->widget;
00796   if (!XtIsManaged(ew)) {
00797     *width = *height = 0;
00798     return;
00799   }
00800   c = XPDFTreeCPart(ew);
00801 
00802   // get size of this entry
00803   if (ew) {
00804     if (!XtIsManaged(ew)) {
00805       *width = *height = 0;
00806       return;
00807     }
00808     if (ew == instigator) {
00809       w1 = ew->core.width;
00810       h1 = ew->core.height;
00811     } else {
00812       XtQueryGeometry(ew, NULL, &geom);
00813       w1 = (geom.request_mode & CWWidth) ? geom.width : ew->core.width;
00814       h1 = (geom.request_mode & CWHeight) ? geom.height : ew->core.height;
00815     }
00816     h1 += 2 * ew->core.border_width;
00817   } else {
00818     // root of tree
00819     w1 = 0;
00820     h1 = 0;
00821   }
00822 
00823   // if this entry is expanded, get size of all of its children
00824   if (c->entryExpanded) {
00825     for (child = e->children; child; child = child->next) {
00826       calcSubtreeSize(w, instigator, child, &w2, &h2);
00827       w2 += xpdfTreeIndent;
00828       if (w2 > w1) {
00829     w1 = w2;
00830       }
00831       h1 += h2;
00832     }
00833   }
00834 
00835   *width = w1;
00836   *height = h1;
00837 }
00838 
00839 static Boolean needRelayout(Widget oldWidget, Widget newWidget) {
00840   XPDFTreeWidget ow = (XPDFTreeWidget)oldWidget;
00841   XPDFTreeWidget nw = (XPDFTreeWidget)newWidget;
00842 
00843   if (nw->tree.marginWidth != ow->tree.marginWidth ||
00844       nw->tree.marginHeight != ow->tree.marginHeight) {
00845     return True;
00846   }
00847   return False;
00848 }
00849 
00850 static void click(Widget widget, XEvent *event,
00851           String *params, Cardinal *numParams) {
00852   XPDFTreeWidget w = (XPDFTreeWidget)widget;
00853   XButtonPressedEvent *bpe;
00854   XPDFTreeEntry *e;
00855   Boolean onExpandIcon;
00856   XPDFTreeConstraint c;
00857   XPDFTreeSelectCallbackStruct cbs;
00858 
00859   if (event->type != ButtonPress) {
00860     return;
00861   }
00862   bpe = (XButtonPressedEvent *)event;
00863   if (findPosition(w, bpe->x, bpe->y, &e, &onExpandIcon)) {
00864     if (onExpandIcon) {
00865       c = XPDFTreeCPart(e->widget);
00866       w->tree.redrawY = e->widget->core.y;
00867       XtVaSetValues(e->widget, XPDFNentryExpanded, !c->entryExpanded, NULL);
00868     } else {
00869       XmProcessTraversal(e->widget, XmTRAVERSE_CURRENT);
00870       XtCallActionProc(widget, "ManagerGadgetActivate", event, NULL, 0);
00871       cbs.reason = XmCR_ACTIVATE;
00872       cbs.event = event;
00873       cbs.selectedItem = e->widget;
00874       XtCallCallbackList(widget, w->tree.selectCallback, &cbs);
00875     }
00876   }
00877 }
00878 
00879 static Boolean findPosition(XPDFTreeWidget w, int x, int y,
00880                 XPDFTreeEntry **e, Boolean *onExpandIcon) {
00881   XPDFTreeEntry *e2;
00882 
00883   for (e2 = w->tree.root; e2; e2 = e2->next) {
00884     *e = e2;
00885     if (findPositionInSubtree(w, x, y, e, onExpandIcon)) {
00886       return True;
00887     }
00888   }
00889   return False;
00890 }
00891 
00892 // If (x,y) falls on either an expand/collapse icon or a label gadget,
00893 // set *<e> and *<onExpandIcon> and return true.
00894 static Boolean findPositionInSubtree(XPDFTreeWidget w, int x, int y,
00895                      XPDFTreeEntry **e,
00896                      Boolean *onExpandIcon) {
00897   Widget child;
00898   XPDFTreeConstraint c;
00899   XPDFTreeEntry *e2;
00900   int y1;
00901 
00902   child = (*e)->widget;
00903   y1 = child->core.y + child->core.height / 2;
00904   if (x >= child->core.x && x < child->core.x + child->core.width &&
00905       y >= child->core.y && y < child->core.y + child->core.height) {
00906     *onExpandIcon = False;
00907     return True;
00908   } else if (x >= child->core.x - 16 && x < child->core.x - 4 &&
00909          y >= y1 - 6 && y < y1 + 6 &&
00910          (*e)->children) {
00911     *onExpandIcon = True;
00912     return True;
00913   }
00914   c = XPDFTreeCPart(child);
00915   if (!c || c->entryExpanded) {
00916     for (e2 = (*e)->children; e2; e2 = e2->next) {
00917       *e = e2;
00918       if (findPositionInSubtree(w, x, y, e, onExpandIcon)) {
00919     return True;
00920       }
00921     }
00922   }
00923   return False;
00924 }
00925 
00926 Widget XPDFCreateTree(Widget parent, char *name,
00927               ArgList argList, Cardinal numArgs) {
00928   return XtCreateWidget(name, xpdfTreeWidgetClass, parent, argList, numArgs);
00929 }
KDE Home | KDE Accessibility Home | Description of Access Keys