home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1999 mARCH / PCWK3A99.iso / Linux / DDD331 / DDD-3_1_.000 / DDD-3_1_ / ddd-3.1.1 / ddd / GraphEdit.C < prev    next >
C/C++ Source or Header  |  1998-11-30  |  86KB  |  3,031 lines

  1. // $Id: GraphEdit.C,v 1.65 1998/11/30 08:59:11 zeller Exp $
  2. // GraphEdit Widget
  3.  
  4. // Copyright (C) 1995-1998 Technische Universitaet Braunschweig, Germany.
  5. // Written by Andreas Zeller <zeller@ips.cs.tu-bs.de>.
  6. // 
  7. // This file is part of DDD.
  8. // 
  9. // DDD is free software; you can redistribute it and/or
  10. // modify it under the terms of the GNU General Public
  11. // License as published by the Free Software Foundation; either
  12. // version 2 of the License, or (at your option) any later version.
  13. // 
  14. // DDD is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  17. // See the GNU General Public License for more details.
  18. // 
  19. // You should have received a copy of the GNU General Public
  20. // License along with DDD -- see the file COPYING.
  21. // If not, write to the Free Software Foundation, Inc.,
  22. // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  23. // 
  24. // DDD is the data display debugger.
  25. // For details, see the DDD World-Wide-Web page, 
  26. // `http://www.cs.tu-bs.de/softech/ddd/',
  27. // or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
  28.  
  29. char GraphEdit_rcsid[] = 
  30.     "$Id: GraphEdit.C,v 1.65 1998/11/30 08:59:11 zeller Exp $";
  31.  
  32. #ifdef __GNUG__
  33. #pragma implementation
  34. #endif
  35.  
  36. #include <string.h>
  37. #include <stdlib.h>
  38. #include <stdio.h>
  39. #include <iostream.h>
  40. #include <strstream.h>
  41. #include <iomanip.h>
  42.  
  43. #include <Xm/Xm.h>
  44. #include <Xm/ScrolledW.h>
  45. #include <X11/Xlib.h>
  46. #include <X11/cursorfont.h>
  47. #include <X11/Intrinsic.h>
  48. #include <X11/IntrinsicP.h>
  49. #include <X11/StringDefs.h>
  50.  
  51. #include "events.h"
  52. #include "Graph.h"
  53. #include "GraphEditP.h"
  54. #include "GraphEdit.h"
  55. #include "HintGraphN.h"
  56. #include "LineGraphE.h"
  57. #include "layout.h"
  58. #include "misc.h"
  59. #include "cook.h"
  60. #include "strtoul.h"
  61. #include "TimeOut.h"
  62. #include "EdgeAPA.h"
  63. #include "GraphNPA.h"
  64.  
  65.  
  66. #if XmVersion < 1002
  67. // Motif 1.1 backwards compatibility
  68. #ifndef XmInheritBorderHighlight
  69. #define XmInheritBorderHighlight (XtWidgetProc)_XtInherit
  70. #endif
  71.  
  72. #ifndef XmInheritBorderUnhighlight
  73. #define XmInheritBorderUnhighlight (XtWidgetProc)_XtInherit
  74. #endif
  75.  
  76. #ifndef XmInheritArmAndActivate
  77. #define XmInheritArmAndActivate (XmArmAndActivate)_XtInherit
  78. #endif
  79. #endif // XmVersion
  80.  
  81.  
  82. static BoxRegion EVERYWHERE(BoxPoint(0,0), BoxSize(INT_MAX, INT_MAX));
  83.  
  84.  
  85. // Compute default foreground
  86.  
  87. static void defaultForeground(Widget w, int, XrmValue *value)
  88. {
  89.     const GraphEditWidget _w = GraphEditWidget(w);
  90.     value->addr = caddr_t(&_w->primitive.foreground);
  91. }
  92.  
  93.  
  94. // Resource list
  95.  
  96. static XtResource resources[] = {
  97. #define offset(field) XtOffsetOf(GraphEditRec, graphEdit.field)
  98.     // {name, class, type, size, offset, default_type, default_addr}
  99.     { XtNgraph, XtCGraph, XtRPointer, sizeof(Graph *),
  100.     offset(graph), XtRImmediate, XtPointer(NULL) },
  101.     { XtNmoveDelta, XtCMoveDelta, XtRDimension, sizeof(Dimension),
  102.     offset(moveDelta), XtRImmediate, XtPointer(4) },
  103.     { XtNrubberEdges, XtCRubberEdges, XtRBoolean, sizeof(Boolean),
  104.     offset(rubberEdges), XtRImmediate, XtPointer(True) },
  105.     { XtNrubberArrows, XtCRubberEdges, XtRBoolean, sizeof(Boolean),
  106.     offset(rubberArrows), XtRImmediate, XtPointer(False) },
  107.     { XtNrubberAnnotations, XtCRubberAnnotations, XtRBoolean, sizeof(Boolean),
  108.     offset(rubberAnnotations), XtRImmediate, XtPointer(False) },
  109.     { XtNopaqueMove, XtCOpaqueMove, XtRBoolean, sizeof(Boolean),
  110.     offset(opaqueMove), XtRImmediate, XtPointer(False) },
  111.  
  112.     { XtNautoRaise, XtCAutoRaise, XtRBoolean, sizeof(Boolean),
  113.     offset(autoRaise), XtRImmediate, XtPointer(True) },
  114.  
  115.     { XtNshowHints, XtCShowHints, XtRBoolean, sizeof(Boolean),
  116.     offset(showHints), XtRImmediate, XtPointer(False) },
  117.     { XtNhintSize, XtCHintSize, XtRDimension, sizeof(Dimension),
  118.     offset(hintSize), XtRImmediate, XtPointer(6) },
  119.  
  120.     { XtNshowAnnotations, XtCShowAnnotations, XtRBoolean, sizeof(Boolean),
  121.     offset(showAnnotations), XtRImmediate, XtPointer(True) },
  122.  
  123.     { XtNgridWidth, XtCGridSize, XtRDimension, sizeof(Dimension),
  124.     offset(gridWidth), XtRImmediate, XtPointer(16) },
  125.     { XtNgridHeight, XtCGridSize, XtRDimension, sizeof(Dimension),
  126.     offset(gridHeight), XtRImmediate, XtPointer(16) },
  127.  
  128.     { XtNshowGrid, XtCShowGrid, XtRBoolean, sizeof(Boolean),
  129.     offset(showGrid), XtRImmediate, XtPointer(False) },
  130.     { XtNsnapToGrid, XtCSnapToGrid, XtRBoolean, sizeof(Boolean),
  131.     offset(snapToGrid), XtRImmediate, XtPointer(False) },
  132.  
  133.     { XtNautoLayout, XtCAutoLayout, XtRBoolean, sizeof(Boolean),
  134.     offset(autoLayout), XtRImmediate, XtPointer(False) },
  135.  
  136.     { XtNrotation, XtCRotation, XtRCardinal, sizeof(Cardinal),
  137.     offset(rotation), XtRImmediate, XtPointer(0)},
  138.  
  139.     { XtNedgeWidth, XtCEdgeWidth, XtRDimension, sizeof(Dimension),
  140.     offset(edgeWidth), XtRImmediate, XtPointer(0) },
  141.     { XtNarrowAngle, XtCArrowAngle, XtRDimension, sizeof(Dimension),
  142.     offset(arrowAngle), XtRImmediate, XtPointer(30) },
  143.     { XtNarrowLength, XtCArrowLength, XtRDimension, sizeof(Dimension),
  144.     offset(arrowLength), XtRImmediate, XtPointer(10) },
  145.     { XtNselfEdgeDiameter, XtCSelfEdgeDiameter, XtRDimension, 
  146.         sizeof(Dimension), offset(selfEdgeDiameter), 
  147.         XtRImmediate, XtPointer(32) },
  148.  
  149.     { XtNextraWidth, XtCExtraSize, XtRDimension, sizeof(Dimension),
  150.     offset(extraWidth), XtRImmediate, XtPointer(0) },
  151.     { XtNextraHeight, XtCExtraSize, XtRDimension, sizeof(Dimension),
  152.     offset(extraHeight), XtRImmediate, XtPointer(0) },
  153.  
  154.     { XtNrequestedWidth, XtCRequestedSize, XtRDimension, sizeof(Dimension),
  155.     offset(requestedWidth), XtRImmediate, XtPointer(0) },
  156.     { XtNrequestedHeight, XtCRequestedSize, XtRDimension, sizeof(Dimension),
  157.     offset(requestedHeight), XtRImmediate, XtPointer(0) },
  158.  
  159.     { XtNselectTile, XtCBitmap, XtRBitmap, sizeof(Pixmap),
  160.     offset(selectTile), XtRImmediate, XtPointer(0)},
  161.  
  162.     { XtNedgeAttachMode, XtCEdgeAttachMode, XtREdgeAttachMode,
  163.     sizeof(EdgeAttachMode), offset(edgeAttachMode), 
  164.         XtRImmediate, XtPointer(Straight) },
  165.     { XtNlayoutMode, XtCLayoutMode, XtRLayoutMode,
  166.     sizeof(LayoutMode), offset(layoutMode), 
  167.         XtRImmediate, XtPointer(RegularLayoutMode) },
  168.     { XtNselfEdgePosition, XtCSelfEdgePosition, XtRSelfEdgePosition,
  169.     sizeof(SelfEdgePosition), offset(selfEdgePosition), 
  170.         XtRImmediate, XtPointer(NorthEast) },
  171.     { XtNselfEdgeDirection, XtCSelfEdgeDirection, XtRSelfEdgeDirection,
  172.     sizeof(SelfEdgeDirection), offset(selfEdgeDirection), 
  173.         XtRImmediate, XtPointer(Counterclockwise) },
  174.     { XtNdashedLines, XtCDashedLines, XtRBoolean,
  175.     sizeof(Boolean), offset(dashedLines), 
  176.         XtRImmediate, XtPointer(False) },
  177.  
  178.     { XtNdefaultCursor, XtCCursor, XtRCursor, sizeof(Cursor),
  179.     offset(defaultCursor), XtRImmediate, XtPointer(0)},
  180.     { XtNmoveCursor, XtCCursor, XtRCursor, sizeof(Cursor),
  181.     offset(moveCursor), XtRImmediate, XtPointer(0)},
  182.     { XtNselectCursor, XtCCursor, XtRCursor, sizeof(Cursor),
  183.     offset(selectCursor), XtRImmediate, XtPointer(0)},
  184.     { XtNselectBottomLeftCursor, XtCCursor, XtRCursor, sizeof(Cursor),
  185.     offset(selectBottomLeftCursor), XtRImmediate, XtPointer(0)},
  186.     { XtNselectBottomRightCursor, XtCCursor, XtRCursor, sizeof(Cursor),
  187.     offset(selectBottomRightCursor), XtRImmediate, XtPointer(0)},
  188.     { XtNselectTopLeftCursor, XtCCursor, XtRCursor, sizeof(Cursor),
  189.     offset(selectTopLeftCursor), XtRImmediate, XtPointer(0)},
  190.     { XtNselectTopRightCursor, XtCCursor, XtRCursor, sizeof(Cursor),
  191.     offset(selectTopRightCursor), XtRImmediate, XtPointer(0)},
  192.  
  193.     { XtNnodeColor, XtCColor, XtRPixel, sizeof(Pixel),
  194.     offset(nodeColor), XtRCallProc, XtPointer(defaultForeground) },
  195.     { XtNedgeColor, XtCColor, XtRPixel, sizeof(Pixel),
  196.     offset(edgeColor), XtRCallProc, XtPointer(defaultForeground) },
  197.     { XtNframeColor, XtCColor, XtRPixel, sizeof(Pixel),
  198.     offset(frameColor), XtRCallProc, XtPointer(defaultForeground) },
  199.     { XtNoutlineColor, XtCColor, XtRPixel, sizeof(Pixel),
  200.     offset(outlineColor), XtRCallProc, XtPointer(defaultForeground) },
  201.     { XtNgridColor, XtCColor, XtRPixel, sizeof(Pixel),
  202.     offset(gridColor), XtRCallProc, XtPointer(defaultForeground) },
  203.     { XtNselectColor, XtCColor, XtRPixel, sizeof(Pixel),
  204.     offset(selectColor), XtRCallProc, XtPointer(defaultForeground) },
  205.  
  206.     { XtNnodePrintColor, XtCColor, XtRString, sizeof(String),
  207.     offset(nodePrintColor), XtRImmediate, 0 },
  208.     { XtNedgePrintColor, XtCColor, XtRString, sizeof(String),
  209.     offset(edgePrintColor), XtRImmediate, 0 },
  210.  
  211.     { XtNpositionChangedCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  212.     offset(positionChangedProc), XtRCallback, XtPointer(0) },
  213.     { XtNselectionChangedCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  214.     offset(selectionChangedProc), XtRCallback, XtPointer(0) },
  215.     { XtNsizeChangedCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  216.     offset(sizeChangedProc), XtRCallback, XtPointer(0) },
  217.     { XtNcompareNodesCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  218.     offset(compareNodesProc), XtRCallback, XtPointer(0) },
  219.     { XtNpreLayoutCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  220.     offset(preLayoutProc), XtRCallback, XtPointer(0) },
  221.     { XtNpostLayoutCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  222.     offset(postLayoutProc), XtRCallback, XtPointer(0) },
  223.     { XtNpreSelectionCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  224.     offset(preSelectionProc), XtRCallback, XtPointer(0) },
  225.  
  226. #undef offset
  227. };
  228.  
  229.  
  230. // Action function declarations
  231.  
  232. static void Select      (Widget, XEvent *, String *, Cardinal *);
  233. static void Extend      (Widget, XEvent *, String *, Cardinal *);
  234. static void Toggle      (Widget, XEvent *, String *, Cardinal *);
  235. static void SelectOrMove(Widget, XEvent *, String *, Cardinal *);
  236. static void ExtendOrMove(Widget, XEvent *, String *, Cardinal *);
  237. static void ToggleOrMove(Widget, XEvent *, String *, Cardinal *);
  238. static void MoveSelected(Widget, XEvent *, String *, Cardinal *);
  239. static void Follow      (Widget, XEvent *, String *, Cardinal *);
  240. static void End         (Widget, XEvent *, String *, Cardinal *);
  241. static void ShowEdges   (Widget, XEvent *, String *, Cardinal *);
  242. static void HideEdges   (Widget, XEvent *, String *, Cardinal *);
  243. static void SelectAll   (Widget, XEvent *, String *, Cardinal *);
  244. static void SelectFirst (Widget, XEvent *, String *, Cardinal *);
  245. static void SelectNext  (Widget, XEvent *, String *, Cardinal *);
  246. static void SelectPrev  (Widget, XEvent *, String *, Cardinal *);
  247. static void UnselectAll (Widget, XEvent *, String *, Cardinal *);
  248. static void SnapToGrid  (Widget, XEvent *, String *, Cardinal *);
  249. static void _SnapToGrid (Widget, XEvent *, String *, Cardinal *);
  250. static void Rotate      (Widget, XEvent *, String *, Cardinal *);
  251. static void _Rotate     (Widget, XEvent *, String *, Cardinal *);
  252. static void DoLayout    (Widget, XEvent *, String *, Cardinal *);
  253. static void _Layout     (Widget, XEvent *, String *, Cardinal *);
  254. static void Normalize   (Widget, XEvent *, String *, Cardinal *);
  255. static void _Normalize  (Widget, XEvent *, String *, Cardinal *);
  256.  
  257.  
  258. // Actions table
  259.  
  260. static XtActionsRec actions[] = {
  261.     { "select",         Select },        // select()
  262.     { "extend",         Extend },        // extend()
  263.     { "toggle",         Toggle },        // toggle()
  264.     { "select-or-move", SelectOrMove },     // select-or-move()
  265.     { "extend-or-move", ExtendOrMove },     // extend-or-move()
  266.     { "toggle-or-move", ToggleOrMove },     // toggle-or-move()
  267.     { "move-selected",  MoveSelected },     // move-selected(X, Y)
  268.     { "follow",        Follow },        // follow()
  269.     { "end",        End },           // end()
  270.     { "show-edges",    ShowEdges },     // show-edges([any|both|from|to])
  271.     { "hide-edges",     HideEdges },     // hide-edges([any|both|from|to])
  272.     { "select-all",     SelectAll },     // select-all()
  273.     { "select-first",     SelectFirst },   // select-first()
  274.     { "select-next",     SelectNext },    // select-next()
  275.     { "select-prev",     SelectPrev },    // select-prev()
  276.     { "unselect-all",     UnselectAll },   // unselect-all()
  277.     { "snap-to-grid",    SnapToGrid },    // snap-to-grid()
  278.     { "_snap-to-grid",    _SnapToGrid },
  279.     { "rotate",        Rotate },        // rotate([[+|-]DEGREES])
  280.     { "_rotate",    _Rotate },
  281.     { "layout",        DoLayout },      // layout([regular|compact],
  282.     { "_layout",    _Layout },       //         [[+|-]DEGREES]])
  283.     { "normalize",    Normalize },     // normalize()
  284.     { "_normalize",    _Normalize },
  285. };
  286.  
  287.  
  288. // Default translation table
  289.  
  290. static char defaultTranslations[] =
  291.     "Shift<Btn1Down>:           toggle()\n"
  292.     "~Shift<Btn1Down>:           select-or-move()\n"
  293.     "<Btn1Motion>:           follow()\n"
  294.     "<Btn1Up>:               end()\n"
  295.     "<Btn2Down>:           toggle()\n"
  296.     "<Btn2Motion>:           follow()\n"
  297.     "<Btn2Up>:               end()\n"
  298.     "~Shift Ctrl<Key>KP_1:     move-selected(-grid, +grid)\n"
  299.     "~Shift Ctrl<Key>KP_2:     move-selected(    0, +grid)\n"
  300.     "~Shift Ctrl<Key>KP_3:     move-selected(+grid, +grid)\n"
  301.     "~Shift Ctrl<Key>KP_4:     move-selected(-grid,     0)\n"
  302.     "~Shift Ctrl<Key>KP_6:     move-selected(+grid,     0)\n"
  303.     "~Shift Ctrl<Key>KP_7:     move-selected(-grid, -grid)\n"
  304.     "~Shift Ctrl<Key>KP_8:     move-selected(    0, -grid)\n"
  305.     "~Shift Ctrl<Key>KP_9:     move-selected(+grid, -grid)\n"
  306.     "~Shift Ctrl<Key>B:        move-selected(-grid,     0)\n"
  307.     "~Shift Ctrl<Key>F:        move-selected(+grid,     0)\n"
  308.     "~Shift Ctrl<Key>P:        move-selected(    0, -grid)\n"
  309.     "~Shift Ctrl<Key>N:        move-selected(    0, +grid)\n"
  310.     "~Ctrl Shift<Key>KP_1:     move-selected(-1, +1)\n"
  311.     "~Ctrl Shift<Key>KP_2:     move-selected( 0, +1)\n"
  312.     "~Ctrl Shift<Key>KP_3:     move-selected(+1, +1)\n"
  313.     "~Ctrl Shift<Key>KP_4:     move-selected(-1,  0)\n"
  314.     "~Ctrl Shift<Key>KP_6:     move-selected(+1,  0)\n"
  315.     "~Ctrl Shift<Key>KP_7:     move-selected(-1, -1)\n"
  316.     "~Ctrl Shift<Key>KP_8:     move-selected( 0, -1)\n"
  317.     "~Ctrl Shift<Key>KP_9:     move-selected(+1, -1)\n"
  318.     "~Ctrl Shift<Key>B:        move-selected(-1,  0)\n"
  319.     "~Ctrl Shift<Key>F:        move-selected(+1,  0)\n"
  320.     "~Ctrl Shift<Key>P:        move-selected( 0, -1)\n"
  321.     "~Ctrl Shift<Key>N:        move-selected( 0, +1)\n"
  322.     "~Meta ~Ctrl<Key>KP_4:     select-prev()\n"
  323.     "~Meta ~Ctrl<Key>KP_6:     select-next()\n"
  324.     "~Meta ~Ctrl<Key>KP_7:     select-first()\n"
  325.     "~Meta ~Ctrl<Key>KP_0:     select-first()\n"
  326.     "~Meta ~Ctrl<Key>B:        select-prev()\n"
  327.     "~Meta ~Ctrl<Key>F:        select-next()\n"
  328.     "~Meta ~Ctrl<Key>P:        select-prev()\n"
  329.     "~Meta ~Ctrl<Key>N:        select-next()\n"
  330.     "~Meta ~Ctrl<Key>A:        select-first()\n"
  331.     "~Meta Ctrl<Key>A:         select-all()\n"
  332.     ;
  333.  
  334. // These translations override the XmPrimitive base translations
  335. static char extraTranslations[] =
  336.     "~Meta ~Shift Ctrl<Key>osfLeft:       move-selected(-grid,     0)\n"
  337.     "~Meta ~Shift Ctrl<Key>osfRight:      move-selected(+grid,     0)\n"
  338.     "~Meta ~Shift Ctrl<Key>osfUp:         move-selected(    0, -grid)\n"
  339.     "~Meta ~Shift Ctrl<Key>osfDown:       move-selected(    0, +grid)\n"
  340.     "~Meta ~Shift Ctrl<Key>osfBeginLine:  move-selected(-grid, -grid)\n"
  341.     "~Meta ~Ctrl Shift<Key>osfLeft:       move-selected(-1,  0)\n"
  342.     "~Meta ~Ctrl Shift<Key>osfRight:      move-selected(+1,  0)\n"
  343.     "~Meta ~Ctrl Shift<Key>osfUp:         move-selected( 0, -1)\n"
  344.     "~Meta ~Ctrl Shift<Key>osfDown:       move-selected( 0, +1)\n"
  345.     "~Meta ~Ctrl Shift<Key>osfBeginLine:  move-selected(-1, -1)\n"
  346.     "~Meta ~Shift ~Ctrl<Key>osfLeft:      select-prev()\n"
  347.     "~Meta ~Shift ~Ctrl<Key>osfRight:     select-next()\n"
  348.     "~Meta ~Shift ~Ctrl<Key>osfUp:        select-prev()\n"
  349.     "~Meta ~Shift ~Ctrl<Key>osfDown:      select-next()\n"
  350.     "~Meta ~Shift ~Ctrl<Key>osfBeginLine: select-first()\n"
  351.     "~Meta ~Shift Ctrl<Key>Left:          move-selected(-grid,     0)\n"
  352.     "~Meta ~Shift Ctrl<Key>Right:         move-selected(+grid,     0)\n"
  353.     "~Meta ~Shift Ctrl<Key>Up:            move-selected(    0, -grid)\n"
  354.     "~Meta ~Shift Ctrl<Key>Down:          move-selected(    0, +grid)\n"
  355.     "~Meta ~Ctrl Shift<Key>Left:          move-selected(-1,  0)\n"
  356.     "~Meta ~Ctrl Shift<Key>Right:         move-selected(+1,  0)\n"
  357.     "~Meta ~Ctrl Shift<Key>Up:            move-selected( 0, -1)\n"
  358.     "~Meta ~Ctrl Shift<Key>Down:          move-selected( 0, +1)\n"
  359.     "~Meta ~Shift ~Ctrl<Key>Left:         select-prev()\n"
  360.     "~Meta ~Shift ~Ctrl<Key>Right:        select-next()\n"
  361.     "~Meta ~Shift ~Ctrl<Key>Up:           select-prev()\n"
  362.     "~Meta ~Shift ~Ctrl<Key>Down:         select-next()\n"
  363. ;
  364.  
  365. // Method function declarations
  366.  
  367. static void ClassInitialize();
  368.  
  369. static void Initialize(Widget request, 
  370.                Widget w, 
  371.                ArgList args,
  372.                Cardinal *num_args);
  373.  
  374. static void Redisplay(Widget w, XEvent *event, Region region);
  375.  
  376. static void Realize(Widget w, 
  377.             XtValueMask *value_mask,
  378.             XSetWindowAttributes *attributes);
  379.  
  380. static Boolean SetValues(Widget old, 
  381.              Widget request, 
  382.              Widget new_w,
  383.              ArgList args, 
  384.              Cardinal *num_args);
  385.  
  386. static void Destroy(Widget w);
  387.  
  388.  
  389. // Class record initialization
  390.  
  391. GraphEditClassRec graphEditClassRec = {
  392.   {     /* core fields */
  393.     /* superclass               */  (WidgetClass) &xmPrimitiveClassRec,
  394.     /* class_name               */  "GraphEdit",
  395.     /* widget_size              */  sizeof(GraphEditRec),
  396.     /* class_initialize         */  ClassInitialize,
  397.     /* class_part_initialize    */  NULL,
  398.     /* class_inited             */  False,
  399.     /* initialize               */  Initialize,
  400.     /* initialize_hook          */  NULL,
  401.     /* realize                  */  Realize,
  402.     /* actions                  */  actions,
  403.     /* num_actions              */  XtNumber(actions),
  404.     /* resources                */  resources,
  405.     /* num_resources            */  XtNumber(resources),
  406.     /* xrm_class                */  NULLQUARK,
  407.     /* compress_motion          */  True,
  408.     /* compress_exposure        */  True,
  409.     /* compress_enterleave      */  True,
  410.     /* visible_interest         */  False,
  411.     /* destroy                  */  Destroy,
  412.     /* resize                   */  XtInheritResize,
  413.     /* expose                   */  Redisplay,
  414.     /* set_values               */  SetValues,
  415.     /* set_values_hook          */  NULL,
  416.     /* set_values_almost        */  XtInheritSetValuesAlmost,
  417.     /* get_values_hook          */  NULL,
  418.     /* accept_focus             */  NULL,
  419.     /* version                  */  XtVersion,
  420.     /* callback_private         */  NULL,
  421.     /* tm_table                 */  defaultTranslations,
  422.     /* query_geometry           */  XtInheritQueryGeometry,
  423.     /* display_accelerator      */  XtInheritDisplayAccelerator,
  424.     /* extension                */  NULL,
  425.   },
  426.   {     /* Primitive fields */
  427.     /* border_highlight         */ XmInheritBorderHighlight,
  428.     /* border_unhighlight       */ XmInheritBorderUnhighlight,
  429.     /* translations             */ XtInheritTranslations,
  430.     /* arm_and_activate         */ NULL,   // XmInheritArmAndActivate?
  431.     /* syn_resources            */ NULL,
  432.     /* num_syn_resources        */ 0,
  433.     /* extension                */ NULL
  434. #if defined(__sgi)
  435.     // Paul Sydney <sydney@ulua.mhpcc.af.mil> reports that Motif
  436.     // on an SGI Indy running IRIX 6.5 has an extra
  437.     // `_SG_vendorExtension' field.  If this is not initialized
  438.     // explicitly, then EGCS 1.1 gives a warning.
  439.     , 0
  440. #endif
  441.   },
  442.   {    /* GraphEdit fields */
  443.     /* extension                */ NULL
  444.   },
  445. };
  446.  
  447. WidgetClass graphEditWidgetClass = (WidgetClass)&graphEditClassRec;
  448.  
  449.  
  450. // Method function definitions
  451.  
  452. // Set widget to minimal size
  453. void graphEditSizeChanged(Widget w)
  454. {
  455.     XtCheckSubclass(w, GraphEditWidgetClass, "Bad widget class");
  456.  
  457.     Arg args[10];
  458.     int arg;
  459.  
  460.     const GraphEditWidget _w            = GraphEditWidget(w);
  461.     const Graph* graph                  = _w->graphEdit.graph;
  462.     const Dimension highlight_thickness = _w->primitive.highlight_thickness;
  463.     Boolean& sizeChanged                = _w->graphEdit.sizeChanged;
  464.     const GraphGC& graphGC              = _w->graphEdit.graphGC;
  465.     const Dimension extraWidth          = _w->graphEdit.extraWidth;
  466.     const Dimension extraHeight         = _w->graphEdit.extraHeight;
  467.  
  468.     // Could it be this is invoked without any graph yet?
  469.     if (graph == 0)
  470.     return;
  471.  
  472.     sizeChanged = False;
  473.  
  474.     BoxRegion r = graph->region(graphGC);
  475.  
  476.     Dimension myWidth  = r.origin(X) + r.space(X) + highlight_thickness * 2;
  477.     Dimension myHeight = r.origin(Y) + r.space(Y) + highlight_thickness * 2;
  478.  
  479.     Dimension parentWidth  = 0;
  480.     Dimension parentHeight = 0;
  481.  
  482.     Widget parent = XtParent(w);
  483.     if (!XmIsScrolledWindow(parent))
  484.     parent = XtParent(parent);    // Skip clipping window
  485.     if (XmIsScrolledWindow(parent))
  486.     {
  487.     // Get the size allowed by our parent
  488.     Dimension parentSpacing;
  489.  
  490.     arg = 0;
  491.     XtSetArg(args[arg], XtNwidth,   &parentWidth);   arg++;
  492.     XtSetArg(args[arg], XtNheight,  &parentHeight);  arg++;
  493.     XtSetArg(args[arg], XmNspacing, &parentSpacing); arg++;
  494.     XtGetValues(parent, args, arg);
  495.  
  496.     if (parentWidth >= parentSpacing)
  497.         parentWidth -= parentSpacing;
  498.     if (parentHeight >= parentSpacing)
  499.         parentHeight -= parentSpacing;
  500.     }
  501.  
  502.     Dimension width  = max(parentWidth, myWidth);
  503.     Dimension height = max(parentHeight, myHeight);
  504.  
  505.     width  += extraWidth;
  506.     height += extraHeight;
  507.  
  508.     Dimension width_return;
  509.     Dimension height_return;
  510.     XtGeometryResult result = 
  511.     XtMakeResizeRequest(w, width, height, &width_return, &height_return);
  512.     if (result == XtGeometryAlmost)
  513.     result = XtMakeResizeRequest(w, width_return, height_return,
  514.                      &width_return, &height_return);
  515.  
  516.     if (result == XtGeometryYes)
  517.     {
  518.     // Normally, we should let our manager resize ourselves.
  519.     // But LessTif 0.87 wants it this way.
  520.     XtResizeWidget(w, width_return, height_return, 0);
  521.  
  522.     graphEditRedraw(w);
  523.     }
  524. }
  525.  
  526. // Return the graph's Graphic Context
  527. const GraphGC& graphEditGetGraphGC(Widget w)
  528. {
  529.     XtCheckSubclass(w, GraphEditWidgetClass, "Bad widget class");
  530.  
  531.     const GraphEditWidget _w = GraphEditWidget(w);
  532.     const GraphGC& graphGC   = _w->graphEdit.graphGC;
  533.  
  534.     return graphGC;
  535. }
  536.  
  537. // Return the graph
  538. Graph *graphEditGetGraph(Widget w)
  539. {
  540.     XtCheckSubclass(w, GraphEditWidgetClass, "Bad widget class");
  541.  
  542.     GraphEditWidget _w = GraphEditWidget(w);
  543.     return _w->graphEdit.graph;
  544. }
  545.  
  546.  
  547. // Set grid pixmap
  548. static void setGrid(Widget w, Boolean reset = False)
  549. {
  550.     const GraphEditWidget _w   = GraphEditWidget(w);
  551.     const Pixel     gridColor  = _w->graphEdit.gridColor;
  552.     const Pixel     background = _w->core.background_pixel;
  553.     const Boolean   showGrid   = _w->graphEdit.showGrid;
  554.     Dimension& gridHeight      = _w->graphEdit.gridHeight;
  555.     Dimension& gridWidth       = _w->graphEdit.gridWidth;
  556.     Pixmap& gridPixmap         = _w->graphEdit.gridPixmap;
  557.  
  558.     gridWidth  = max(gridWidth,  2);
  559.     gridHeight = max(gridHeight, 2);
  560.  
  561.     if (reset && gridPixmap != None)
  562.     {
  563.     // delete old pixmap
  564.     XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w), ParentRelative);
  565.     XClearArea(XtDisplay(w), XtWindow(w), 0, 0, 0, 0, True);
  566.  
  567.     XFreePixmap(XtDisplay(w), gridPixmap);
  568.     gridPixmap = None;
  569.     }
  570.  
  571.     if (gridPixmap == None)
  572.     {
  573.     // Create grid pixmap
  574.     int gridDataSize = ((gridWidth + 7) / 8) * gridHeight + 1;
  575.     char *gridData = new char [gridDataSize];
  576.     for (int i = 0; i < gridDataSize; i++)
  577.         gridData[i] = 0x00;
  578.     if (showGrid)
  579.         gridData[0] = 0x01;
  580.  
  581.     int depth = PlanesOfScreen(XtScreen(w));
  582.     gridPixmap = 
  583.         XCreatePixmapFromBitmapData(XtDisplay(w), 
  584.                     XtWindow(w),
  585.                     gridData, 
  586.                     gridWidth, gridHeight,
  587.                     gridColor, background,
  588.                     depth);
  589.  
  590.      XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w), gridPixmap);
  591.     XClearArea(XtDisplay(w), XtWindow(w), 0, 0, 0, 0, True);
  592.  
  593.     delete[] gridData;
  594.     }
  595. }
  596.  
  597.  
  598.  
  599. // Redraw
  600. static void RedrawCB(XtPointer client_data, XtIntervalId *id)
  601. {
  602.     const Widget w                      = Widget(client_data);
  603.     const GraphEditWidget _w            = GraphEditWidget(w);
  604.     const Graph* graph                  = _w->graphEdit.graph;
  605.     const GraphGC& graphGC              = _w->graphEdit.graphGC;
  606.     const Boolean sizeChanged           = _w->graphEdit.sizeChanged;
  607.     const Boolean redisplayEnabled      = _w->graphEdit.redisplayEnabled;
  608.     const Boolean highlight_drawn       = _w->primitive.highlight_drawn;
  609.     const Dimension highlight_thickness = _w->primitive.highlight_thickness;
  610.     XtIntervalId& redrawTimer           = _w->graphEdit.redrawTimer;
  611.  
  612.     (void) id;            // Use it
  613.     assert(redrawTimer == *id);
  614.     redrawTimer = 0;
  615.  
  616.     if (graph == 0)
  617.     return;            // No graph to draw
  618.  
  619.     if (!redisplayEnabled)
  620.     return;            // Display disabled
  621.  
  622.     setGrid(w);
  623.  
  624.     if (sizeChanged)
  625.     graphEditSizeChanged(w);
  626.  
  627.     // Redraw XmPrimitive border
  628.     if (highlight_drawn)
  629.     graphEditClassRec.primitive_class.border_highlight(w);
  630.  
  631.     // Check for pending redrawings
  632.     Boolean redraw_all = True;
  633.     GraphNode *node;
  634.     for (node = graph->firstVisibleNode(); 
  635.      node != 0;
  636.      node = graph->nextVisibleNode(node))
  637.     {
  638.     if (!node->redraw())
  639.     {
  640.         redraw_all = False;
  641.         break;
  642.     }
  643.     }
  644.  
  645.     setGrid(w);
  646.  
  647.     if (redraw_all)
  648.     {
  649.     XClearArea(XtDisplay(w), XtWindow(w),
  650.            highlight_thickness, highlight_thickness, 
  651.            _w->core.width  - highlight_thickness * 2, 
  652.            _w->core.height - highlight_thickness * 2,
  653.            False);
  654.  
  655.     graph->draw(w, EVERYWHERE, graphGC);
  656.     }
  657.  
  658.     for (node = graph->firstVisibleNode(); 
  659.      node != 0;
  660.      node = graph->nextVisibleNode(node))
  661.     {
  662.     if (!redraw_all && node->redraw())
  663.     {
  664.         BoxRegion r = node->region(graphGC);
  665.         XClearArea(XtDisplay(w), XtWindow(w), r.origin(X), r.origin(Y),
  666.                r.space(X), r.space(Y), False);
  667.  
  668.         graph->draw(w, r, graphGC);
  669.     }
  670.  
  671.     node->redraw() = False;
  672.     }
  673. }
  674.  
  675. // Launch redrawing procedure
  676. static void StartRedraw(Widget w)
  677. {
  678.     const GraphEditWidget _w  = GraphEditWidget(w);
  679.     XtIntervalId& redrawTimer = _w->graphEdit.redrawTimer;
  680.  
  681.     if (redrawTimer != 0)
  682.     return;            // Redraw pending
  683.  
  684.     // Redraw after we are back in the event loop
  685.     redrawTimer = XtAppAddTimeOut(XtWidgetToApplicationContext(w),
  686.                   0, RedrawCB, XtPointer(w));
  687. }
  688.  
  689. // Redraw entire graph
  690. void graphEditRedraw(Widget w)
  691. {
  692.     XtCheckSubclass(w, GraphEditWidgetClass, "Bad widget class");
  693.  
  694.     const GraphEditWidget _w   = GraphEditWidget(w);
  695.     const Graph* graph         = _w->graphEdit.graph;
  696.  
  697.     for (GraphNode *node = graph->firstVisibleNode(); 
  698.      node != 0;
  699.      node = graph->nextVisibleNode(node))
  700.     {
  701.     graphEditRedrawNode(w, node);
  702.     }
  703. }
  704.  
  705. // Redraw a specific region
  706. void graphEditRedrawNode(Widget w, GraphNode *node)
  707. {
  708.     XtCheckSubclass(w, GraphEditWidgetClass, "Bad widget class");
  709.  
  710.     if (!node->hidden())
  711.     {
  712.     node->redraw() = True;
  713.     StartRedraw(w);
  714.     }
  715. }
  716.  
  717. // Disable redrawing for a while; return old state
  718. Boolean graphEditEnableRedisplay(Widget w, Boolean state)
  719. {
  720.     XtCheckSubclass(w, GraphEditWidgetClass, "Bad widget class");
  721.  
  722.     const GraphEditWidget _w   = GraphEditWidget(w);
  723.     Boolean& redisplayEnabled  = _w->graphEdit.redisplayEnabled;
  724.  
  725.     Boolean old_state = redisplayEnabled;
  726.     redisplayEnabled = state;
  727.  
  728.     if (redisplayEnabled)
  729.     StartRedraw(w);
  730.  
  731.     return old_state;
  732. }
  733.  
  734.  
  735.  
  736.  
  737. // Converters
  738.  
  739. #define done(type, value) \
  740.     {                            \
  741.     if (toVal->addr != NULL) {            \
  742.         if (toVal->size < sizeof(type)) {        \
  743.         toVal->size = sizeof(type);        \
  744.         return False;                \
  745.         }                        \
  746.         *(type *)(toVal->addr) = (value);        \
  747.     }                        \
  748.     else {                        \
  749.         static type static_val;            \
  750.         static_val = (value);            \
  751.         toVal->addr = (caddr_t)&static_val;            \
  752.     }                        \
  753.                             \
  754.     toVal->size = sizeof(type);            \
  755.     return True;                    \
  756.     }
  757.  
  758.  
  759.  
  760. // Convert String to EdgeAttachMode and vice versa
  761.  
  762. static Boolean CvtStringToEdgeAttachMode (Display *display, XrmValue *,
  763.     Cardinal *num_args, XrmValue *fromVal, XrmValue *toVal,
  764.     XtPointer *)
  765. {
  766.     EdgeAttachMode mode = Straight;
  767.  
  768.     if (*num_args != 0)
  769.     XtAppWarningMsg(XtDisplayToApplicationContext(display),
  770.         "CvtStringToEdgeAttachMode", "wrongParameters",
  771.         "XtToolkitError",
  772.         "String to EdgeAttachMode conversion needs no extra arguments",
  773.         (String *)NULL, (Cardinal *)NULL);
  774.     
  775.     string s = downcase((char *)fromVal->addr);
  776.  
  777.     if (s == "straight")
  778.     mode = Straight;
  779.     else if (s == "circle")
  780.     mode = Circle;
  781.     else if (s == "centered")
  782.     mode = Centered;
  783.     else
  784.     XtDisplayStringConversionWarning(display, (String)fromVal->addr,
  785.         XtREdgeAttachMode);
  786.  
  787.     done(EdgeAttachMode, mode);
  788. }
  789.  
  790. static Boolean CvtEdgeAttachModeToString (Display *display, XrmValue *,
  791.     Cardinal *num_args, XrmValue *fromVal, XrmValue *toVal,
  792.     XtPointer *)
  793. {
  794.     if (*num_args != 0)
  795.     XtAppWarningMsg(XtDisplayToApplicationContext(display),
  796.         "CvtEdgeAttachModeToString", "wrongParameters",
  797.         "XtToolkitError",
  798.         "EdgeAttachMode to String conversion needs no extra arguments",
  799.         (String *)NULL, (Cardinal *)NULL);
  800.  
  801.     EdgeAttachMode mode = *((EdgeAttachMode *)fromVal->addr);
  802.  
  803.     String s = "unknown";
  804.     switch (mode)
  805.     {
  806.     case Straight:
  807.     s = "straight";
  808.     break;
  809.     case Circle:
  810.     s = "circle";
  811.     break;
  812.     case Centered:
  813.     s = "centered";
  814.     break;
  815.     default:
  816.     XtDisplayStringConversionWarning(display, s, XtRString);
  817.     break;
  818.     }
  819.  
  820.     done(String, s);
  821. }
  822.  
  823.  
  824. // Convert String to LayoutMode and vice versa
  825.  
  826. static Boolean CvtStringToLayoutMode (Display *display, XrmValue *,
  827.     Cardinal *num_args, XrmValue *fromVal, XrmValue *toVal,
  828.     XtPointer *)
  829. {
  830.     LayoutMode mode = RegularLayoutMode;
  831.  
  832.     if (*num_args != 0)
  833.     XtAppWarningMsg(XtDisplayToApplicationContext(display),
  834.         "CvtStringToLayoutMode", "wrongParameters",
  835.         "XtToolkitError",
  836.         "String to LayoutMode conversion needs no extra arguments",
  837.         (String *)NULL, (Cardinal *)NULL);
  838.     
  839.     string s = downcase((char *)fromVal->addr);
  840.  
  841.     if (s == "regular")
  842.     mode = RegularLayoutMode;
  843.     else if (s == "compact")
  844.     mode = CompactLayoutMode;
  845.     else
  846.     XtDisplayStringConversionWarning(display, (String)fromVal->addr,
  847.         XtRLayoutMode);
  848.  
  849.     done(LayoutMode, mode);
  850. }
  851.  
  852.  
  853. static Boolean CvtLayoutModeToString (Display *display, XrmValue *,
  854.     Cardinal *num_args, XrmValue *fromVal, XrmValue *toVal,
  855.     XtPointer *)
  856. {
  857.     if (*num_args != 0)
  858.     XtAppWarningMsg(XtDisplayToApplicationContext(display),
  859.         "CvtLayoutModeToString", "wrongParameters",
  860.         "XtToolkitError",
  861.         "LayoutMode to String conversion needs no extra arguments",
  862.         (String *)NULL, (Cardinal *)NULL);
  863.  
  864.     LayoutMode mode = *((LayoutMode *)fromVal->addr);
  865.  
  866.     String s = "unknown";
  867.     switch (mode)
  868.     {
  869.     case RegularLayoutMode:
  870.     s = "regular";
  871.     break;
  872.     case CompactLayoutMode:
  873.     s = "compact";
  874.     break;
  875.     default:
  876.     XtDisplayStringConversionWarning(display, s, XtRString);
  877.     break;
  878.     }
  879.  
  880.     done(String, s);
  881. }
  882.  
  883.  
  884. // Convert String to SelfEdgePosition and vice versa
  885.  
  886. static Boolean CvtStringToSelfEdgePosition (Display *display, XrmValue *,
  887.     Cardinal *num_args, XrmValue *fromVal, XrmValue *toVal,
  888.     XtPointer *)
  889. {
  890.     if (*num_args != 0)
  891.     XtAppWarningMsg(XtDisplayToApplicationContext(display),
  892.         "CvtStringToSelfEdgePosition", "wrongParameters",
  893.         "XtToolkitError",
  894.         "String to SelfEdgePosition conversion needs no extra arguments",
  895.         (String *)NULL, (Cardinal *)NULL);
  896.     
  897.     string s = downcase((char *)fromVal->addr);
  898.  
  899.     SelfEdgePosition pos = NorthWest;
  900.     if (s == "northwest")
  901.     pos = NorthWest;
  902.     else if (s == "northeast")
  903.     pos = NorthEast;
  904.     else if (s == "southwest")
  905.     pos = SouthWest;
  906.     else if (s == "southeast")
  907.     pos = SouthEast;
  908.     else
  909.     XtDisplayStringConversionWarning(display, (String)fromVal->addr,
  910.         XtRSelfEdgePosition);
  911.  
  912.     done(SelfEdgePosition, pos);
  913. }
  914.  
  915. static Boolean CvtSelfEdgePositionToString (Display *display, XrmValue *,
  916.     Cardinal *num_args, XrmValue *fromVal, XrmValue *toVal,
  917.     XtPointer *)
  918. {
  919.     if (*num_args != 0)
  920.     XtAppWarningMsg(XtDisplayToApplicationContext(display),
  921.         "CvtSelfEdgePositionToString", "wrongParameters",
  922.         "XtToolkitError",
  923.         "SelfEdgePosition to String conversion needs no extra arguments",
  924.         (String *)NULL, (Cardinal *)NULL);
  925.  
  926.     SelfEdgePosition pos = *((SelfEdgePosition *)fromVal->addr);
  927.  
  928.     String s = "unknown";
  929.     switch (pos)
  930.     {
  931.     case NorthWest:
  932.     s = "northwest";
  933.     break;
  934.     case NorthEast:
  935.     s = "northeast";
  936.     break;
  937.     case SouthWest:
  938.     s = "southwest";
  939.     break;
  940.     case SouthEast:
  941.     s = "southwest";
  942.     break;
  943.     default:
  944.     XtDisplayStringConversionWarning(display, s, XtRString);
  945.     break;
  946.     }
  947.  
  948.     done(String, s);
  949. }
  950.  
  951.  
  952. // Convert String to SelfEdgeDirection and vice versa
  953.  
  954. static Boolean CvtStringToSelfEdgeDirection (Display *display, XrmValue *,
  955.     Cardinal *num_args, XrmValue *fromVal, XrmValue *toVal,
  956.     XtPointer *)
  957. {
  958.     if (*num_args != 0)
  959.     XtAppWarningMsg(XtDisplayToApplicationContext(display),
  960.         "CvtStringToSelfEdgeDirection", "wrongParameters",
  961.         "XtToolkitError",
  962.         "String to SelfEdgeDirection conversion needs no extra arguments",
  963.         (String *)NULL, (Cardinal *)NULL);
  964.     
  965.     string s = downcase((char *)fromVal->addr);
  966.  
  967.     SelfEdgeDirection dir = Counterclockwise;
  968.     if (s == "counterclockwise")
  969.     dir = Counterclockwise;
  970.     else if (s == "clockwise")
  971.     dir = Clockwise;
  972.     else
  973.     XtDisplayStringConversionWarning(display, (String)fromVal->addr,
  974.         XtRSelfEdgeDirection);
  975.  
  976.     done(SelfEdgeDirection, dir);
  977. }
  978.  
  979. static Boolean CvtSelfEdgeDirectionToString (Display *display, XrmValue *,
  980.     Cardinal *num_args, XrmValue *fromVal, XrmValue *toVal,
  981.     XtPointer *)
  982. {
  983.     if (*num_args != 0)
  984.     XtAppWarningMsg(XtDisplayToApplicationContext(display),
  985.         "CvtSelfEdgeDirectionToString", "wrongParameters",
  986.         "XtToolkitError",
  987.         "SelfEdgeDirection to String conversion needs no extra arguments",
  988.         (String *)NULL, (Cardinal *)NULL);
  989.  
  990.     SelfEdgeDirection pos = *((SelfEdgeDirection *)fromVal->addr);
  991.  
  992.     String s = "unknown";
  993.     switch (pos)
  994.     {
  995.     case Clockwise:
  996.     s = "clockwise";
  997.     break;
  998.     case Counterclockwise:
  999.     s = "counterclockwise";
  1000.     break;
  1001.     default:
  1002.     XtDisplayStringConversionWarning(display, s, XtRString);
  1003.     break;
  1004.     }
  1005.  
  1006.     done(String, s);
  1007. }
  1008.  
  1009.  
  1010.  
  1011.  
  1012. // Standard Converters
  1013.  
  1014. static Boolean CvtBooleanToString (Display *display, XrmValue *,
  1015.     Cardinal *num_args, XrmValue *fromVal, XrmValue *toVal,
  1016.     XtPointer *)
  1017. {
  1018.     if (*num_args != 0)
  1019.     XtAppWarningMsg(XtDisplayToApplicationContext(display),
  1020.         "CvtBooleanToString", "wrongParameters",
  1021.         "XtToolkitError",
  1022.         "Boolean to String conversion needs no extra arguments",
  1023.         (String *)NULL, (Cardinal *)NULL);
  1024.  
  1025.     Boolean mode = *((Boolean *)fromVal->addr);
  1026.  
  1027.     String s = "unknown";
  1028.     switch (mode)
  1029.     {
  1030.     case True:
  1031.     s = "on";
  1032.     break;
  1033.     case False:
  1034.     s = "off";
  1035.     break;
  1036.     default:
  1037.     XtDisplayStringConversionWarning(display, s, XtRString);
  1038.     break;
  1039.     }
  1040.  
  1041.     done(String, s);
  1042. }
  1043.  
  1044. static Boolean CvtDimensionToString (Display *display, XrmValue *,
  1045.     Cardinal *num_args, XrmValue *fromVal, XrmValue *toVal,
  1046.     XtPointer *)
  1047. {
  1048.     if (*num_args != 0)
  1049.     XtAppWarningMsg(XtDisplayToApplicationContext(display),
  1050.         "CvtDimensionToString", "wrongParameters",
  1051.         "XtToolkitError",
  1052.         "Dimension to String conversion needs no extra arguments",
  1053.         (String *)NULL, (Cardinal *)NULL);
  1054.  
  1055.     Dimension d = *((Dimension *)fromVal->addr);
  1056.  
  1057.     ostrstream os;
  1058.     os << d;
  1059.     string os_s(os);
  1060.     String s = (String)XtNewString((String)os_s);
  1061.  
  1062.     done(String, s);
  1063. }
  1064.  
  1065. static Boolean CvtCardinalToString (Display *display, XrmValue *,
  1066.     Cardinal *num_args, XrmValue *fromVal, XrmValue *toVal,
  1067.     XtPointer *)
  1068. {
  1069.     if (*num_args != 0)
  1070.     XtAppWarningMsg(XtDisplayToApplicationContext(display),
  1071.         "CvtCardinalToString", "wrongParameters",
  1072.         "XtToolkitError",
  1073.         "Cardinal to String conversion needs no extra arguments",
  1074.         (String *)NULL, (Cardinal *)NULL);
  1075.  
  1076.     Cardinal d = *((Cardinal *)fromVal->addr);
  1077.  
  1078.     ostrstream os;
  1079.     os << d;
  1080.     string os_s(os);
  1081.     String s = (String)XtNewString((String)os_s);
  1082.  
  1083.     done(String, s);
  1084. }
  1085.  
  1086.  
  1087. // Initialize class
  1088. static void ClassInitialize()
  1089. {
  1090.     // Register own string -> type converters
  1091.     XtSetTypeConverter(XtRString, XtREdgeAttachMode, 
  1092.                CvtStringToEdgeAttachMode,
  1093.                XtConvertArgList(0), 0, 
  1094.                XtCacheAll, XtDestructor(0));
  1095.     XtSetTypeConverter(XtRString, XtRLayoutMode, 
  1096.                CvtStringToLayoutMode,
  1097.                XtConvertArgList(0), 0, 
  1098.                XtCacheAll, XtDestructor(0));
  1099.     XtSetTypeConverter(XtRString, XtRSelfEdgePosition, 
  1100.                CvtStringToSelfEdgePosition,
  1101.                XtConvertArgList(0), 0, 
  1102.                XtCacheAll, XtDestructor(0));
  1103.     XtSetTypeConverter(XtRString, XtRSelfEdgeDirection, 
  1104.                CvtStringToSelfEdgeDirection,
  1105.                XtConvertArgList(0), 0, 
  1106.                XtCacheAll, XtDestructor(0));
  1107.  
  1108.     // Register own type -> string converters
  1109.     XtSetTypeConverter(XtREdgeAttachMode, XtRString, 
  1110.                CvtEdgeAttachModeToString,
  1111.                XtConvertArgList(0), 0, 
  1112.                XtCacheAll, XtDestructor(0));
  1113.     XtSetTypeConverter(XtRLayoutMode, XtRString, 
  1114.                CvtLayoutModeToString,
  1115.                XtConvertArgList(0), 0, 
  1116.                XtCacheAll, XtDestructor(0));
  1117.     XtSetTypeConverter(XtRSelfEdgePosition, XtRString, 
  1118.                CvtSelfEdgePositionToString,
  1119.                XtConvertArgList(0), 0, 
  1120.                XtCacheAll, XtDestructor(0));
  1121.     XtSetTypeConverter(XtRSelfEdgeDirection, XtRString, 
  1122.                CvtSelfEdgeDirectionToString,
  1123.                XtConvertArgList(0), 0, 
  1124.                XtCacheAll, XtDestructor(0));
  1125.  
  1126.     // Register standard type -> string converters
  1127.     XtSetTypeConverter(XtRBoolean, XtRString, 
  1128.                CvtBooleanToString,
  1129.                XtConvertArgList(0), 0, 
  1130.                XtCacheAll, XtDestructor(0));
  1131.     XtSetTypeConverter(XtRDimension, XtRString, 
  1132.                CvtDimensionToString,
  1133.                XtConvertArgList(0), 0, 
  1134.                XtCacheAll, XtDestructor(0));
  1135.     XtSetTypeConverter(XtRCardinal, XtRString, 
  1136.                CvtCardinalToString,
  1137.                XtConvertArgList(0), 0, 
  1138.                XtCacheAll, XtDestructor(0));
  1139. }
  1140.  
  1141.  
  1142. // Initialize widget
  1143.  
  1144. inline void createCursor(Widget w, Cursor& cursor, unsigned int shape)
  1145. {
  1146.     if (cursor == 0)
  1147.     cursor = XCreateFontCursor(XtDisplay(w), shape);
  1148. }
  1149.  
  1150. static void setGCs(Widget w)
  1151. {
  1152.     const GraphEditWidget _w        = GraphEditWidget(w);
  1153.  
  1154.     // read-only
  1155.     const Dimension edgeWidth       = _w->graphEdit.edgeWidth;
  1156.     const Pixmap selectTile         = _w->graphEdit.selectTile;
  1157.     const Pixel background          = _w->core.background_pixel;
  1158.     const Pixel nodeColor           = _w->graphEdit.nodeColor;
  1159.     const Pixel edgeColor           = _w->graphEdit.edgeColor;
  1160.     const Pixel frameColor          = _w->graphEdit.frameColor;
  1161.     const Pixel outlineColor        = _w->graphEdit.outlineColor;
  1162.     const Pixel selectColor         = _w->graphEdit.selectColor;
  1163.     const Boolean dashedLines       = _w->graphEdit.dashedLines;
  1164.  
  1165.     // write-only
  1166.     GC& nodeGC                      = _w->graphEdit.nodeGC;
  1167.     GC& edgeGC                      = _w->graphEdit.edgeGC;
  1168.     GC& invertGC                    = _w->graphEdit.invertGC;
  1169.     GC& clearGC                     = _w->graphEdit.clearGC;
  1170.     GC& frameGC                     = _w->graphEdit.frameGC;
  1171.     GC& outlineGC                   = _w->graphEdit.outlineGC;
  1172.  
  1173.     int line_style = dashedLines ? LineOnOffDash : LineSolid;
  1174.  
  1175.     // set nodeGC
  1176.     XGCValues gcv;
  1177.     gcv.foreground = nodeColor;
  1178.     gcv.line_width = 1;
  1179.     gcv.line_style = line_style;
  1180.     nodeGC = XtGetGC(w, GCForeground | GCLineWidth | GCLineStyle, &gcv);
  1181.  
  1182.     // set edgeGC
  1183.     gcv.foreground = edgeColor;
  1184.     gcv.line_width = edgeWidth;
  1185.     gcv.line_style = line_style;
  1186.     edgeGC = XtGetGC(w, GCForeground | GCLineWidth | GCLineStyle, &gcv);
  1187.  
  1188.     // set invertGC
  1189.     if (selectTile)
  1190.     {
  1191.     gcv.foreground = selectColor;
  1192.     gcv.function   = GXcopy;
  1193.     gcv.fill_style = FillStippled;
  1194.     gcv.stipple    = selectTile;
  1195.     invertGC = XtGetGC(w, GCForeground | GCFunction | 
  1196.                GCFillStyle | GCStipple, &gcv);
  1197.     }
  1198.     else
  1199.     {
  1200.     gcv.foreground = selectColor;
  1201.     gcv.function   = GXinvert;
  1202.     gcv.plane_mask = selectColor ^ background;
  1203.     invertGC = XtGetGC(w, GCForeground | GCFunction | GCPlaneMask, &gcv);
  1204.     }
  1205.  
  1206.     // set clearGC
  1207.     gcv.foreground = background;
  1208.     gcv.function   = GXcopy;
  1209.     clearGC = XtGetGC(w, GCForeground | GCFunction, &gcv);
  1210.  
  1211.     // set frameGC
  1212.     gcv.foreground = frameColor;
  1213.     gcv.function   = GXinvert;
  1214.     gcv.line_width = 1;
  1215.     gcv.line_style = LineSolid;
  1216.     gcv.plane_mask = frameColor ^ background;
  1217.     frameGC = XtGetGC(w, GCForeground | GCFunction | 
  1218.               GCLineWidth | GCLineStyle | GCPlaneMask, &gcv);
  1219.  
  1220.     // set outlineGC
  1221.     gcv.foreground = outlineColor;
  1222.     gcv.function   = GXinvert;
  1223.     gcv.line_width = 1;
  1224.     gcv.line_style = LineSolid;
  1225.     gcv.plane_mask = outlineColor ^ background;
  1226.     outlineGC = XtGetGC(w, GCForeground | GCFunction | 
  1227.             GCLineWidth | GCLineStyle | GCPlaneMask, &gcv);
  1228. }
  1229.  
  1230.  
  1231. static void setGraphGC(Widget w)
  1232. {
  1233.     const GraphEditWidget _w        = GraphEditWidget(w);
  1234.  
  1235.     // Read only
  1236.     const Dimension arrowAngle      = _w->graphEdit.arrowAngle;
  1237.     const Dimension arrowLength     = _w->graphEdit.arrowLength;
  1238.     const Dimension hintSize        = _w->graphEdit.hintSize;
  1239.     const Boolean showHints         = _w->graphEdit.showHints;
  1240.     const Boolean showAnnotations   = _w->graphEdit.showAnnotations;
  1241.     const GC nodeGC                 = _w->graphEdit.nodeGC;
  1242.     const GC edgeGC                 = _w->graphEdit.edgeGC;
  1243.     const GC invertGC               = _w->graphEdit.invertGC;
  1244.     const GC clearGC                = _w->graphEdit.clearGC;
  1245.  
  1246.     const Dimension selfEdgeDiameter = 
  1247.     _w->graphEdit.selfEdgeDiameter;
  1248.     const SelfEdgePosition selfEdgePosition = 
  1249.     _w->graphEdit.selfEdgePosition;
  1250.     const SelfEdgeDirection selfEdgeDirection = 
  1251.     _w->graphEdit.selfEdgeDirection;
  1252.     const EdgeAttachMode edgeAttachMode = 
  1253.     EdgeAttachMode(_w->graphEdit.edgeAttachMode);
  1254.  
  1255.     // Write only
  1256.     GraphGC& graphGC                = _w->graphEdit.graphGC;
  1257.  
  1258.     // Set graphGC
  1259.     graphGC = GraphGC(nodeGC, edgeGC, invertGC, clearGC);
  1260.     graphGC.arrowAngle        = arrowAngle;
  1261.     graphGC.arrowLength       = arrowLength;
  1262.     graphGC.edgeAttachMode    = EdgeAttachMode(edgeAttachMode);
  1263.     graphGC.drawHints         = showHints;
  1264.     graphGC.drawAnnotations   = showAnnotations;
  1265.     graphGC.hintSize          = hintSize;
  1266.     graphGC.selfEdgeDiameter  = selfEdgeDiameter;
  1267.     graphGC.selfEdgePosition  = selfEdgePosition;
  1268.     graphGC.selfEdgeDirection = selfEdgeDirection;
  1269.  
  1270.     // Get print colors
  1271.  
  1272.     if (_w->graphEdit.nodePrintColor != 0)
  1273.     {
  1274.     XColor exact_def;
  1275.     Status ok = 
  1276.         XParseColor(XtDisplay(w), _w->core.colormap, 
  1277.             _w->graphEdit.nodePrintColor, &exact_def);
  1278.  
  1279.     if (ok)
  1280.     {
  1281.         graphGC.node_red   = exact_def.red;
  1282.         graphGC.node_green = exact_def.green;
  1283.         graphGC.node_blue  = exact_def.blue;
  1284.     }
  1285.     else
  1286.     {
  1287.         Cardinal one = 1;
  1288.  
  1289.         XtAppWarningMsg(XtWidgetToApplicationContext(w),
  1290.                 "GraphEdit::Initialize", "badColor",
  1291.                 "XtToolkitError",
  1292.                 "Cannot parse " XtNnodePrintColor " \"%s\"",
  1293.                 (String *)&_w->graphEdit.nodePrintColor, &one);
  1294.     }
  1295.     }
  1296.  
  1297.     if (_w->graphEdit.edgePrintColor != 0)
  1298.     {
  1299.     XColor exact_def;
  1300.     Status ok = 
  1301.         XParseColor(XtDisplay(w), _w->core.colormap, 
  1302.             _w->graphEdit.edgePrintColor, &exact_def);
  1303.  
  1304.     if (ok)
  1305.     {
  1306.         graphGC.edge_red   = exact_def.red;
  1307.         graphGC.edge_green = exact_def.green;
  1308.         graphGC.edge_blue  = exact_def.blue;
  1309.     }
  1310.     else
  1311.     {
  1312.         Cardinal one = 1;
  1313.  
  1314.         XtAppWarningMsg(XtWidgetToApplicationContext(w),
  1315.                 "GraphEdit::Initialize", "badColor",
  1316.                 "XtToolkitError",
  1317.                 "Cannot parse " XtNedgePrintColor " spec \"%s\"",
  1318.                 (String *)&_w->graphEdit.edgePrintColor, &one);
  1319.     }
  1320.     }
  1321. }
  1322.  
  1323.  
  1324. static void Initialize(Widget request, Widget w, ArgList, Cardinal *)
  1325. {
  1326.     // read-only
  1327.     const GraphEditWidget _w        = GraphEditWidget(w);
  1328.  
  1329.     // write-only
  1330.     GraphEditState& state           = _w->graphEdit.state;
  1331.     Cursor& moveCursor              = _w->graphEdit.moveCursor;
  1332.     Cursor& selectCursor            = _w->graphEdit.selectCursor;
  1333.     Cursor& selectBottomLeftCursor  = _w->graphEdit.selectBottomLeftCursor;
  1334.     Cursor& selectBottomRightCursor = _w->graphEdit.selectBottomRightCursor;
  1335.     Cursor& selectTopLeftCursor     = _w->graphEdit.selectTopLeftCursor;
  1336.     Cursor& selectTopRightCursor    = _w->graphEdit.selectTopRightCursor;
  1337.     Pixmap& gridPixmap              = _w->graphEdit.gridPixmap;
  1338.     Boolean& sizeChanged            = _w->graphEdit.sizeChanged;
  1339.     Boolean& redisplayEnabled       = _w->graphEdit.redisplayEnabled;
  1340.     Time& lastSelectTime            = _w->graphEdit.lastSelectTime;
  1341.     XtIntervalId& redrawTimer       = _w->graphEdit.redrawTimer;
  1342.     Dimension& requestedWidth       = _w->graphEdit.requestedWidth;
  1343.     Dimension& requestedHeight      = _w->graphEdit.requestedHeight;
  1344.  
  1345.     // init state
  1346.     state = NopState;
  1347.  
  1348.     // init sizeChanged
  1349.     sizeChanged = False;
  1350.  
  1351.     // init redisplayEnabled
  1352.     redisplayEnabled = True;
  1353.  
  1354.     // init lastSelectTime
  1355.     lastSelectTime = 0;
  1356.  
  1357.     // init redrawTimer
  1358.     redrawTimer = 0;
  1359.  
  1360.     // set GCs
  1361.     setGCs(w);
  1362.  
  1363.     // set Graph GC
  1364.     setGraphGC(w);
  1365.  
  1366.     // set grid pixmap
  1367.     gridPixmap = None;
  1368.  
  1369.     // create cursors if not already set
  1370.     createCursor(w, moveCursor,              XC_fleur);
  1371.     createCursor(w, selectCursor,            XC_plus);
  1372.     createCursor(w, selectBottomLeftCursor,  XC_ll_angle);
  1373.     createCursor(w, selectBottomRightCursor, XC_lr_angle);
  1374.     createCursor(w, selectTopLeftCursor,     XC_ul_angle);
  1375.     createCursor(w, selectTopRightCursor,    XC_ur_angle);
  1376.  
  1377.     // save requested size
  1378.     requestedWidth  = request->core.width;
  1379.     requestedHeight = request->core.height;
  1380.  
  1381.     // set size
  1382.     graphEditSizeChanged(w);
  1383.  
  1384.     // Override XmPrimitive translations
  1385.     static XtTranslations translations = 
  1386.     XtParseTranslationTable(extraTranslations);
  1387.  
  1388.     XtOverrideTranslations(w, translations);
  1389. }
  1390.  
  1391. inline void defineCursor(Widget w, Cursor cursor)
  1392. {
  1393.     if (cursor != 0)
  1394.     XDefineCursor(XtDisplay(w), XtWindow(w), cursor);
  1395.     else
  1396.     XUndefineCursor(XtDisplay(w), XtWindow(w));
  1397. }
  1398.  
  1399. // Realize widget
  1400. static void Realize(Widget w, 
  1401.             XtValueMask *value_mask,
  1402.             XSetWindowAttributes *attributes)
  1403. {
  1404.     const GraphEditWidget _w = GraphEditWidget(w);
  1405.     Cursor defaultCursor     = _w->graphEdit.defaultCursor;
  1406.  
  1407.     // Call superclass realize method
  1408.     graphEditClassRec.core_class.superclass->
  1409.     core_class.realize(w, value_mask, attributes);
  1410.     
  1411.     // Setup default cursor
  1412.     defineCursor(w, defaultCursor);
  1413. }
  1414.  
  1415.  
  1416. // Redisplay widget
  1417. static void Redisplay(Widget w, XEvent *event, Region)
  1418. {
  1419.     const GraphEditWidget _w       = GraphEditWidget(w);
  1420.     const Graph* graph             = _w->graphEdit.graph;
  1421.     const GraphGC& graphGC         = _w->graphEdit.graphGC;
  1422.     const Boolean sizeChanged      = _w->graphEdit.sizeChanged;
  1423.     const Boolean redisplayEnabled = _w->graphEdit.redisplayEnabled;
  1424.     const Boolean highlight_drawn  = _w->primitive.highlight_drawn;
  1425.  
  1426.     if (!redisplayEnabled)
  1427.     {
  1428.     graphEditRedraw(w);
  1429.     return;
  1430.     }
  1431.  
  1432.     if (sizeChanged)
  1433.     graphEditSizeChanged(w);
  1434.  
  1435.     setGrid(w);
  1436.  
  1437.     // Redraw XmPrimitive border
  1438.     if (highlight_drawn)
  1439.     graphEditClassRec.primitive_class.border_highlight(w);
  1440.  
  1441.     graph->draw(w, BoxRegion(point(event), size(event)), graphGC);
  1442. }
  1443.  
  1444.  
  1445. // Set widget values
  1446. static Boolean SetValues(Widget old, Widget, Widget new_w, 
  1447.              ArgList, Cardinal *)
  1448. {
  1449.     GraphEditWidget before = GraphEditWidget(old);
  1450.     GraphEditWidget after  = GraphEditWidget(new_w);
  1451.  
  1452.     Boolean redisplay = False;
  1453.  
  1454.     // redisplay graph if changed
  1455.     if (before->graphEdit.graph != after->graphEdit.graph)
  1456.     {
  1457.     redisplay = True;
  1458.  
  1459.     // Re-layout if auto-layout is enabled
  1460.     if (after->graphEdit.autoLayout)
  1461.     {
  1462.         Cardinal zero = 0;
  1463.         _Layout(new_w, 0, 0, &zero);
  1464.     }
  1465.  
  1466.     // Snap to grid if enabled
  1467.     if (after->graphEdit.snapToGrid)
  1468.     {
  1469.         Cardinal zero = 0;
  1470.         _SnapToGrid(new_w, 0, 0, &zero);
  1471.     }
  1472.     }
  1473.  
  1474.     Boolean new_gcs = False;
  1475.  
  1476.     // reset GCs if changed
  1477.     if (before->graphEdit.edgeWidth      != after->graphEdit.edgeWidth ||
  1478.     before->graphEdit.selectTile     != after->graphEdit.selectTile ||
  1479.     before->graphEdit.dashedLines    != after->graphEdit.dashedLines ||
  1480.     before->graphEdit.nodeColor      != after->graphEdit.nodeColor ||
  1481.     before->graphEdit.edgeColor      != after->graphEdit.edgeColor ||
  1482.     before->graphEdit.frameColor     != after->graphEdit.frameColor ||
  1483.     before->graphEdit.outlineColor   != after->graphEdit.outlineColor ||
  1484.     before->graphEdit.gridColor      != after->graphEdit.gridColor ||
  1485.     before->graphEdit.selectColor    != after->graphEdit.selectColor)
  1486.     {    
  1487.     setGCs(new_w);
  1488.     new_gcs = True;
  1489.     redisplay = True;
  1490.     }
  1491.  
  1492.     // reset GraphGC if changed
  1493.     if (new_gcs ||
  1494.     before->graphEdit.arrowAngle      != after->graphEdit.arrowAngle     ||
  1495.     before->graphEdit.arrowLength     != after->graphEdit.arrowLength    ||
  1496.     before->graphEdit.showHints       != after->graphEdit.showHints      ||
  1497.     before->graphEdit.hintSize        != after->graphEdit.hintSize       ||
  1498.     before->graphEdit.edgeAttachMode  != after->graphEdit.edgeAttachMode ||
  1499.     before->graphEdit.showAnnotations != after->graphEdit.showAnnotations)
  1500.     {
  1501.     setGraphGC(new_w);
  1502.     redisplay = True;
  1503.     }
  1504.  
  1505.     if (before->graphEdit.nodePrintColor != after->graphEdit.nodePrintColor ||
  1506.     before->graphEdit.edgePrintColor != after->graphEdit.edgePrintColor)
  1507.     {
  1508.     setGraphGC(new_w);
  1509.     }
  1510.  
  1511.     // reset grid pixmap if changed
  1512.     if (before->graphEdit.gridWidth  != after->graphEdit.gridWidth  ||
  1513.     before->graphEdit.gridHeight != after->graphEdit.gridHeight ||
  1514.     before->graphEdit.showGrid   != after->graphEdit.showGrid)
  1515.     {
  1516.     setGrid(new_w, True);
  1517.     redisplay = True;
  1518.     }
  1519.  
  1520.     // Always recompute size
  1521.     after->graphEdit.sizeChanged = True;
  1522.  
  1523.     return redisplay;
  1524. }
  1525.  
  1526. // Destroy widget
  1527. static void Destroy(Widget)
  1528. {
  1529.     // Delete graph?
  1530. }
  1531.  
  1532.  
  1533.  
  1534.  
  1535. // Action function definitions
  1536.  
  1537. // Helping stuff
  1538.  
  1539. // Find node at point
  1540. GraphNode *graphEditGetNodeAtPoint(Widget w, BoxPoint p)
  1541. {
  1542.     XtCheckSubclass(w, GraphEditWidgetClass, "Bad widget class");
  1543.  
  1544.     const GraphEditWidget _w  = GraphEditWidget(w);
  1545.     const Graph* graph        = _w->graphEdit.graph;
  1546.     GraphGC& graphGC          = _w->graphEdit.graphGC;
  1547.     GraphNode *found = 0;
  1548.  
  1549.     // Could it be this is invoked without any graph yet?
  1550.     if (graph == 0)
  1551.     return 0;
  1552.  
  1553.     // note that we return the last matching node in the list;
  1554.     // thus on overlapping nodes we select the top one
  1555.  
  1556.     for (GraphNode *node = graph->firstVisibleNode(); node != 0;
  1557.     node = graph->nextVisibleNode(node))
  1558.     if (p <= node->sensitiveRegion(graphGC))
  1559.         found = node;
  1560.  
  1561.     return found;
  1562. }
  1563.     
  1564. GraphNode *graphEditGetNodeAtEvent(Widget w, XEvent *event)
  1565. {
  1566.     return graphEditGetNodeAtPoint(w, point(event));
  1567. }
  1568.  
  1569.  
  1570. // Get frame region
  1571. static BoxRegion frameRegion(Widget w)
  1572. {
  1573.     const GraphEditWidget _w    = GraphEditWidget(w);
  1574.     const BoxPoint& startAction = _w->graphEdit.startAction;
  1575.     const BoxPoint& endAction   = _w->graphEdit.endAction;
  1576.  
  1577.     BoxPoint origin(min(startAction[X], endAction[X]),
  1578.     min(startAction[Y], endAction[Y]));
  1579.  
  1580.     BoxSize space(abs(startAction[X] - endAction[X]),
  1581.     abs(startAction[Y] - endAction[Y]));
  1582.  
  1583.     return BoxRegion(origin, space);
  1584. }
  1585.  
  1586. // Get frame cursor
  1587. static void setRegionCursor(Widget w)
  1588. {
  1589.     const GraphEditWidget _w    = GraphEditWidget(w);
  1590.     const BoxPoint& startAction = _w->graphEdit.startAction;
  1591.     const BoxPoint& endAction   = _w->graphEdit.endAction;
  1592.  
  1593.     Cursor selectCursor            = _w->graphEdit.selectCursor;
  1594.     Cursor selectBottomLeftCursor  = _w->graphEdit.selectBottomLeftCursor;
  1595.     Cursor selectBottomRightCursor = _w->graphEdit.selectBottomRightCursor;
  1596.     Cursor selectTopLeftCursor     = _w->graphEdit.selectTopLeftCursor;
  1597.     Cursor selectTopRightCursor    = _w->graphEdit.selectTopRightCursor;
  1598.  
  1599.     Cursor cursor = selectCursor;
  1600.  
  1601.     if (endAction[X] < startAction[X])
  1602.     {
  1603.     if (endAction[Y] < startAction[Y])
  1604.         cursor = selectTopLeftCursor;
  1605.     else if (endAction[Y] > startAction[Y])
  1606.         cursor = selectBottomLeftCursor;
  1607.     }
  1608.     else if (endAction[X] > startAction[X])
  1609.     {
  1610.     if (endAction[Y] < startAction[Y])
  1611.         cursor = selectTopRightCursor;
  1612.     else if (endAction[Y] > startAction[Y])
  1613.         cursor = selectBottomRightCursor;
  1614.     }
  1615.  
  1616.     defineCursor(w, cursor);
  1617. }
  1618.  
  1619.  
  1620. inline void myXDrawLine(Display *display, 
  1621.             Drawable d, 
  1622.             GC gc,
  1623.             const BoxPoint& f, const BoxPoint& t)
  1624. {
  1625.     if (f != t)
  1626.     XDrawLine(display, d, gc, f[X], f[Y], t[X], t[Y]);
  1627. }
  1628.     
  1629.  
  1630. static void redrawSelectFrame(Widget w, const BoxRegion& r)
  1631. {
  1632.     const GraphEditWidget _w = GraphEditWidget(w);
  1633.     const GC frameGC = _w->graphEdit.frameGC;
  1634.  
  1635.     Display *display = XtDisplay(w);
  1636.     Window window    = XtWindow(w);
  1637.  
  1638.     // North
  1639.     myXDrawLine(display, window, frameGC, 
  1640.         r.origin() + BoxPoint(1, 0),
  1641.         r.origin() + BoxPoint(r.space(X) - 1, 0));
  1642.  
  1643.     // South
  1644.     myXDrawLine(display, window, frameGC,
  1645.         r.origin() + BoxPoint(0, r.space(Y)),
  1646.         r.origin() + BoxPoint(r.space(X), r.space(Y)));
  1647.  
  1648.     // East
  1649.     myXDrawLine(display, window, frameGC,
  1650.         r.origin(),
  1651.         r.origin() + BoxPoint(0, r.space(Y) - 1));
  1652.  
  1653.     // West
  1654.     myXDrawLine(display, window, frameGC,
  1655.         r.origin() + BoxPoint(r.space(X), 0),
  1656.         r.origin() + BoxPoint(r.space(X), r.space(Y) - 1));
  1657. }
  1658.  
  1659.  
  1660. static void drawSelectFrames(Widget w, 
  1661.                  const BoxRegion& r0, 
  1662.                  const BoxRegion& r1)
  1663. {
  1664.     // Clear old frame (by redrawing it)
  1665.     redrawSelectFrame(w, r0);
  1666.  
  1667.     // Draw new frame
  1668.     redrawSelectFrame(w, r1);
  1669.  
  1670.     // Set appropriate cursor
  1671.     setRegionCursor(w);
  1672. }
  1673.  
  1674.  
  1675. // Draw the selection frame
  1676. inline void drawSelectFrame(Widget w)
  1677. {
  1678.     drawSelectFrames(w, frameRegion(w),
  1679.     BoxRegion(BoxPoint(0, 0), BoxSize(0, 0)));
  1680. }
  1681.  
  1682.  
  1683. // Redraw selection frame
  1684. static void redrawSelectFrame(Widget w, BoxPoint& p)
  1685. {
  1686.     const GraphEditWidget _w = GraphEditWidget(w);
  1687.     BoxPoint& endAction      = _w->graphEdit.endAction;
  1688.  
  1689.     BoxRegion r0 = frameRegion(w);
  1690.     endAction = p;
  1691.     BoxRegion r1 = frameRegion(w);
  1692.  
  1693.     drawSelectFrames(w, r0, r1);
  1694. }
  1695.  
  1696.  
  1697. // Find min possible offset
  1698. static void getMinimalOffset(Widget w)
  1699. {
  1700.     const GraphEditWidget _w            = GraphEditWidget(w);
  1701.     const Graph* graph                  = _w->graphEdit.graph;
  1702.     const Dimension highlight_thickness = _w->primitive.highlight_thickness;
  1703.     const GraphGC& graphGC              = _w->graphEdit.graphGC;
  1704.     BoxPoint& minimalOffset             = _w->graphEdit.minimalOffset;
  1705.  
  1706.     const Dimension min_origin = highlight_thickness + 2;
  1707.  
  1708.     Boolean found[NDimensions];
  1709.     found[X] = False;
  1710.     found[Y] = False;
  1711.  
  1712.     for (GraphNode *node = graph->firstVisibleNode(); node != 0;
  1713.     node = graph->nextVisibleNode(node))
  1714.     {
  1715.     if (node->selected())
  1716.     {
  1717.         BoxRegion r = node->region(graphGC);
  1718.  
  1719.         for (BoxDimension d = X; d <= Y; d++)
  1720.         {
  1721.         if (!found[d] || minimalOffset[d] < min_origin - r.origin(d))
  1722.         {
  1723.             minimalOffset[d] = min_origin - r.origin(d);
  1724.             found[d] = True;
  1725.         }
  1726.         }
  1727.     }
  1728.     }
  1729. }
  1730.  
  1731. // Return current offset
  1732. static BoxPoint actionOffset(Widget w)
  1733. {
  1734.     const GraphEditWidget _w      = GraphEditWidget(w);
  1735.     const Dimension gridWidth     = _w->graphEdit.gridWidth;
  1736.     const Dimension gridHeight    = _w->graphEdit.gridHeight;
  1737.     const Boolean snapToGrid      = _w->graphEdit.snapToGrid;
  1738.     const BoxPoint& startAction   = _w->graphEdit.startAction;
  1739.     const BoxPoint& endAction     = _w->graphEdit.endAction;
  1740.     const BoxPoint& minimalOffset = _w->graphEdit.minimalOffset;
  1741.  
  1742.     BoxPoint offset = endAction - startAction;
  1743.     BoxPoint grid(gridWidth, gridHeight);
  1744.  
  1745.     for (BoxDimension d = X; d <= Y; d++)
  1746.     {
  1747.     // Offset must not move nodes out of area
  1748.     if (offset[d] < minimalOffset[d])
  1749.         offset[d] = minimalOffset[d];
  1750.  
  1751.     // Offset must be a grid multiple
  1752.     if (snapToGrid && grid[d] > 0)
  1753.     {
  1754.         offset[d] = ((offset[d] + grid[d] / 2) / grid[d]) * grid[d];
  1755.         if (offset[d] < minimalOffset[d])
  1756.         offset[d] += grid[d];
  1757.     }
  1758.     }
  1759.  
  1760.     return offset;
  1761. }
  1762.  
  1763. // Draw moving frames and edges for nodes at (endAction - startAction)
  1764. static void drawOutlines(Widget w, const BoxPoint& offset)
  1765. {
  1766.     const GraphEditWidget _w            = GraphEditWidget(w);
  1767.     const Graph* graph                  = _w->graphEdit.graph;
  1768.     const Boolean rubberArrows          = _w->graphEdit.rubberArrows;
  1769.     const Boolean rubberAnnotations     = _w->graphEdit.rubberAnnotations;
  1770.     const Boolean rubberEdges           = _w->graphEdit.rubberEdges;
  1771.     const GraphGC& graphGC              = _w->graphEdit.graphGC;
  1772.     const GC& outlineGC                 = _w->graphEdit.outlineGC;
  1773.  
  1774.     for (GraphNode *node = graph->firstVisibleNode(); node != 0;
  1775.     node = graph->nextVisibleNode(node))
  1776.     {
  1777.     if (node->selected())
  1778.     {
  1779.         // this should also handle opaqueMove (FIXME)...
  1780.         BoxRegion r = node->region(graphGC);
  1781.         XDrawRectangle(XtDisplay(w), XtWindow(w), outlineGC,
  1782.         r.origin(X) + offset[X], r.origin(Y) + offset[Y],
  1783.         r.space(X), r.space(Y));
  1784.     }
  1785.     }
  1786.  
  1787.     if (rubberEdges)
  1788.     {
  1789.     GraphGC gc(graphGC);
  1790.     gc.edgeGC           = outlineGC;
  1791.     gc.offsetIfSelected = offset;
  1792.     gc.drawArrowHeads   = rubberArrows;
  1793.     gc.drawAnnotations  = (gc.drawAnnotations && rubberAnnotations);
  1794.  
  1795.     for (GraphEdge *edge = graph->firstVisibleEdge(); edge != 0;
  1796.         edge = graph->nextVisibleEdge(edge))
  1797.     {
  1798.         // if (edge->from()->selected() || edge->to()->selected())
  1799.         edge->draw(w, EVERYWHERE, gc);
  1800.     }
  1801.     }
  1802. }
  1803.  
  1804. // Move Node to specified position and call callbacks
  1805. static void moveTo(Widget w, 
  1806.            GraphNode *node, 
  1807.            const BoxPoint& newPos,
  1808.            Boolean isLast)
  1809. {
  1810.     const GraphEditWidget _w  = GraphEditWidget(w);
  1811.     Graph* graph              = _w->graphEdit.graph;
  1812.  
  1813.     if (node->pos() != newPos)
  1814.     {
  1815.     GraphEditPositionChangedInfo info;
  1816.  
  1817.     info.graph        = graph;
  1818.     info.node         = node;
  1819.     info.old_position = node->pos();
  1820.     info.new_position = newPos;
  1821.     info.is_last      = isLast;
  1822.  
  1823.     XtCallCallbacks(w, XtNpositionChangedCallback, caddr_t(&info));
  1824.  
  1825.     node->moveTo(newPos);
  1826.     }
  1827. }
  1828.  
  1829. // Call ``selection changed'' callbacks
  1830. static void selectionChanged(Widget w, XEvent *event, Boolean double_click)
  1831. {
  1832.     const GraphEditWidget _w  = GraphEditWidget(w);
  1833.     Graph* graph              = _w->graphEdit.graph;
  1834.  
  1835.     GraphEditSelectionChangedInfo info;
  1836.     info.graph        = graph;
  1837.     info.event        = event;
  1838.     info.double_click = double_click;
  1839.  
  1840.     XtCallCallbacks(w, XtNselectionChangedCallback, caddr_t(&info));
  1841. }
  1842.  
  1843.  
  1844. // Action functions
  1845.  
  1846. // Select all nodes
  1847. static Boolean _SelectAll(Widget w, XEvent *, String *, Cardinal *)
  1848. {
  1849.     const GraphEditWidget _w = GraphEditWidget(w);
  1850.     const Graph* graph       = _w->graphEdit.graph;
  1851.  
  1852.     Boolean changed = False;
  1853.     for (GraphNode *node = graph->firstVisibleNode(); node != 0;
  1854.     node = graph->nextVisibleNode(node))
  1855.     {
  1856.     if (!node->selected())
  1857.     {
  1858.         changed = True;
  1859.         node->selected() = True;
  1860.         graphEditRedrawNode(w, node);
  1861.     }
  1862.     }
  1863.  
  1864.     return changed;
  1865. }
  1866.  
  1867. static void SelectAll(Widget w, XEvent *event, String *params,
  1868.     Cardinal *num_params)
  1869. {
  1870.     if (_SelectAll(w, event, params, num_params))
  1871.     selectionChanged(w, event, False);
  1872. }
  1873.  
  1874.  
  1875. // Unselect all nodes
  1876. static Boolean _UnselectAll(Widget w, XEvent *, String *, Cardinal *)
  1877. {
  1878.     const GraphEditWidget _w = GraphEditWidget(w);
  1879.     const Graph* graph       = _w->graphEdit.graph;
  1880.  
  1881.     Boolean changed = False;
  1882.     for (GraphNode *node = graph->firstNode(); node != 0;
  1883.     node = graph->nextNode(node))
  1884.     {
  1885.     if (node->selected())
  1886.     {
  1887.         changed = True;
  1888.         node->selected() = False;
  1889.         graphEditRedrawNode(w, node);
  1890.     }
  1891.     }
  1892.  
  1893.     return changed;
  1894. }
  1895.  
  1896. static void UnselectAll(Widget w, XEvent *event, String *params,
  1897.     Cardinal *num_params)
  1898. {
  1899.     if (_UnselectAll(w, event, params, num_params))
  1900.     selectionChanged(w, event, False);
  1901. }
  1902.  
  1903. // Find nodes connected to ROOT
  1904. static void find_connected_nodes(GraphNode *root, GraphNodePointerArray& nodes)
  1905. {
  1906.     for (int i = 0; i < nodes.size(); i++)
  1907.     if (nodes[i] == root)
  1908.         return;
  1909.  
  1910.     // if (!root->hidden()) 
  1911.     nodes += root;
  1912.  
  1913.     GraphEdge *edge;
  1914.     for (edge = root->firstFrom(); edge != 0; edge = root->nextFrom(edge))
  1915.     find_connected_nodes(edge->to(), nodes);
  1916.  
  1917.     for (edge = root->firstTo(); edge != 0; edge = root->nextTo(edge))
  1918.     find_connected_nodes(edge->from(), nodes);
  1919. }
  1920.  
  1921. // Select an entire subgraph
  1922. static Boolean select_graph(Widget w, GraphNode *root, Boolean set = True)
  1923. {
  1924.     // Find all connected nodes
  1925.     GraphNodePointerArray nodes;
  1926.     find_connected_nodes(root, nodes);
  1927.  
  1928.     // Select them
  1929.     Boolean changed = False;
  1930.     for (int i = 0; i < nodes.size(); i++)
  1931.     {
  1932.     GraphNode *node = nodes[i];
  1933.     if (node->selected() != set)
  1934.     {
  1935.         node->selected() = set;
  1936.         graphEditRedrawNode(w, node);
  1937.         changed = True;
  1938.     }
  1939.     }
  1940.  
  1941.     return changed;
  1942. }
  1943.  
  1944. inline Boolean unselect_graph(Widget w, GraphNode *root)
  1945. {
  1946.     return select_graph(w, root, False);
  1947. }
  1948.  
  1949. // Raise node NODE such that it is placed on top of all others
  1950. void graphEditRaiseNode(Widget w, GraphNode *node)
  1951. {
  1952.     XtCheckSubclass(w, GraphEditWidgetClass, "Bad widget class");
  1953.  
  1954.     const GraphEditWidget _w = GraphEditWidget(w);
  1955.     Graph* graph             = _w->graphEdit.graph;
  1956.  
  1957.     // The last node in the list is drawn last (i.e. on top)
  1958.     graph->makeNodeLast(node);
  1959. }
  1960.  
  1961. // Same, but only if the autoRaise resource is set
  1962. static void raise_node(Widget w, GraphNode *node)
  1963. {
  1964.     const GraphEditWidget _w = GraphEditWidget(w);
  1965.     Boolean autoRaise        = _w->graphEdit.autoRaise;
  1966.  
  1967.     if (autoRaise)
  1968.     graphEditRaiseNode(w, node);
  1969. }
  1970.  
  1971. // Begin selecting or moving
  1972. static void _SelectOrMove(Widget w, XEvent *event, String *params,
  1973.     Cardinal *num_params, SelectionMode mode, Boolean follow)
  1974. {
  1975.     const GraphEditWidget _w = GraphEditWidget(w);
  1976.  
  1977.     Graph* graph             = _w->graphEdit.graph;
  1978.     Cursor moveCursor        = _w->graphEdit.moveCursor;
  1979.  
  1980.     GraphEditState& state    = _w->graphEdit.state;
  1981.     BoxPoint& startAction    = _w->graphEdit.startAction;
  1982.     BoxPoint& endAction      = _w->graphEdit.endAction;
  1983.     Time& lastSelectTime     = _w->graphEdit.lastSelectTime;
  1984.  
  1985.     // Get the input focus
  1986.     XmProcessTraversal(w, XmTRAVERSE_CURRENT);
  1987.  
  1988.     BoxPoint p = point(event);
  1989.     startAction = p;
  1990.     endAction   = p;
  1991.  
  1992.     Time t = time(event);
  1993.     Boolean double_click = 
  1994.     (Time(t - lastSelectTime) <= Time(XtGetMultiClickTime(XtDisplay(w))));
  1995.     lastSelectTime = t;
  1996.  
  1997.     GraphNode *node = graphEditGetNodeAtPoint(w, p);
  1998.  
  1999.     if (mode == SetSelection)
  2000.     {
  2001.     GraphEditPreSelectionInfo info;
  2002.     info.graph        = graph;
  2003.     info.node         = node;
  2004.     info.event        = event;
  2005.     info.doit         = True;
  2006.     info.double_click = double_click;
  2007.  
  2008.     XtCallCallbacks(w, XtNpreSelectionCallback, caddr_t(&info));
  2009.  
  2010.     if (!info.doit)
  2011.         return;
  2012.     }
  2013.  
  2014.     if (node == 0 || node->hidden())
  2015.     {
  2016.     // On the background
  2017.     switch (mode)
  2018.     {
  2019.     case SetSelection:
  2020.         if (double_click)
  2021.         SelectAll(w, event, params, num_params);
  2022.         else
  2023.         UnselectAll(w, event, params, num_params);
  2024.         break;
  2025.  
  2026.     case ExtendSelection:
  2027.     case ToggleSelection:
  2028.         // Nothing to do on background
  2029.         break;
  2030.     }
  2031.  
  2032.     if (follow)
  2033.     {
  2034.         // Not on a node: draw a frame
  2035.         state = SelectState;
  2036.  
  2037.         // start drawing a frame
  2038.         drawSelectFrame(w);
  2039.     }
  2040.     }
  2041.     else
  2042.     {
  2043.     // On a node
  2044.     Boolean changed = False;
  2045.     switch (mode)
  2046.     {
  2047.     case SetSelection:
  2048.         if (!node->selected())
  2049.         {
  2050.         // Create new selection
  2051.         changed = _UnselectAll(w, event, params, num_params);
  2052.         }
  2053.         // FALL THROUGH
  2054.  
  2055.     case ExtendSelection:
  2056.         if (double_click)
  2057.         {
  2058.         // Select all connected nodes
  2059.         changed = select_graph(w, node);
  2060.         }
  2061.         else
  2062.         {
  2063.         // Select single node
  2064.         if (!node->selected())
  2065.         {        
  2066.             node->selected() = True;
  2067.             graphEditRedrawNode(w, node);
  2068.             raise_node(w, node);
  2069.             changed = True;
  2070.         }
  2071.         }
  2072.         break;
  2073.  
  2074.     case ToggleSelection:
  2075.         if (double_click)
  2076.         {
  2077.         // Toggle all connected modes
  2078.         if (node->selected())
  2079.             changed = select_graph(w, node);
  2080.         else
  2081.             changed = unselect_graph(w, node);
  2082.         }
  2083.         else
  2084.         {
  2085.         // Toggle single node
  2086.         node->selected() = !node->selected();
  2087.         graphEditRedrawNode(w, node);
  2088.         raise_node(w, node);
  2089.         changed = True;
  2090.         }
  2091.         break;
  2092.     }
  2093.  
  2094.     if (changed)
  2095.         selectionChanged(w, event, double_click);
  2096.  
  2097.     if (follow)
  2098.     {
  2099.         // Wait for movement
  2100.         state = DeltaState;
  2101.  
  2102.         // Set moving cursor
  2103.         defineCursor(w, moveCursor);
  2104.     }
  2105.     }
  2106. }
  2107.  
  2108. static void SelectOrMove(Widget w, XEvent *event, String *params,
  2109.     Cardinal *num_params)
  2110. {
  2111.     _SelectOrMove(w, event, params, num_params, SetSelection, True);
  2112. }
  2113.  
  2114. static void ExtendOrMove(Widget w, XEvent *event, String *params,
  2115.     Cardinal *num_params)
  2116. {
  2117.     _SelectOrMove(w, event, params, num_params, ExtendSelection, True);
  2118. }
  2119.  
  2120. static void ToggleOrMove(Widget w, XEvent *event, String *params,
  2121.     Cardinal *num_params)
  2122. {
  2123.     _SelectOrMove(w, event, params, num_params, ToggleSelection, True);
  2124. }
  2125.  
  2126. static void Select(Widget w, XEvent *event, String *params,
  2127.     Cardinal *num_params)
  2128. {
  2129.     _SelectOrMove(w, event, params, num_params, SetSelection, False);
  2130. }
  2131.  
  2132. static void Extend(Widget w, XEvent *event, String *params,
  2133.     Cardinal *num_params)
  2134. {
  2135.     _SelectOrMove(w, event, params, num_params, ExtendSelection, False);
  2136. }
  2137.  
  2138. static void Toggle(Widget w, XEvent *event, String *params,
  2139.     Cardinal *num_params)
  2140. {
  2141.     _SelectOrMove(w, event, params, num_params, ToggleSelection, False);
  2142. }
  2143.  
  2144. // Keep on acting...
  2145. static void Follow(Widget w, XEvent *event, String *, Cardinal *)
  2146. {
  2147.     const GraphEditWidget _w    = GraphEditWidget(w);
  2148.     const BoxPoint& startAction = _w->graphEdit.startAction;
  2149.     BoxPoint& endAction         = _w->graphEdit.endAction;
  2150.     GraphEditState& state       = _w->graphEdit.state;
  2151.     BoxPoint& lastOffset        = _w->graphEdit.lastOffset;
  2152.     const Dimension moveDelta   = _w->graphEdit.moveDelta;
  2153.  
  2154.     BoxPoint p = point(event);
  2155.  
  2156.     switch(state)
  2157.     {
  2158.     case SelectState:
  2159.         // Draw new select frame
  2160.         redrawSelectFrame(w, p);
  2161.         break;
  2162.  
  2163.     case MoveState:
  2164.     {
  2165.         // Draw new move frames
  2166.         endAction = p;
  2167.         BoxPoint newOffset = actionOffset(w);
  2168.         if (newOffset != lastOffset)
  2169.         {
  2170.         drawOutlines(w, lastOffset);
  2171.         drawOutlines(w, lastOffset = newOffset);
  2172.         }
  2173.         break;
  2174.     }
  2175.  
  2176.     case DeltaState:
  2177.         // Wait for movement
  2178.         if (abs(p[X] - startAction[X]) > moveDelta ||
  2179.         abs(p[Y] - startAction[Y]) > moveDelta)
  2180.         {
  2181.         // start moving
  2182.         endAction = p;
  2183.         getMinimalOffset(w);
  2184.         graphEditSizeChanged(w);
  2185.         drawOutlines(w, lastOffset = actionOffset(w));
  2186.         state = MoveState;
  2187.         }
  2188.         break;
  2189.  
  2190.     default:
  2191.         // Do nothing
  2192.         break;
  2193.     }
  2194. }
  2195.  
  2196. // Now, all is done.
  2197.  
  2198. static void move_selected_nodes(Widget w, const BoxPoint& offset)
  2199. {
  2200.     const GraphEditWidget _w   = GraphEditWidget(w);
  2201.     const Graph* graph         = _w->graphEdit.graph;
  2202.     const GraphGC& graphGC     = _w->graphEdit.graphGC;
  2203.  
  2204.     if (offset == BoxPoint(0, 0))
  2205.     return;
  2206.  
  2207.     // Clear graph area
  2208.     BoxRegion r = graph->region(graphGC);
  2209.     XClearArea(XtDisplay(w), XtWindow(w), r.origin(X), r.origin(Y),
  2210.            r.space(X), r.space(Y), False);
  2211.  
  2212.     // Move selected nodes
  2213.     GraphNode *lastNode = 0;
  2214.     for (GraphNode *node = graph->firstVisibleNode(); 
  2215.      node != 0;
  2216.      node = graph->nextVisibleNode(node))
  2217.     {
  2218.     if (node->selected())
  2219.     {
  2220.         if (lastNode)
  2221.         moveTo(w, lastNode, lastNode->pos() + offset, False);
  2222.         lastNode = node;
  2223.     }
  2224.     }
  2225.     if (lastNode)
  2226.     moveTo(w, lastNode, lastNode->pos() + offset, True);
  2227.  
  2228.     // resize widget to graph size and redraw graph
  2229.     graphEditSizeChanged(w);
  2230.     graphEditRedraw(w);
  2231. }
  2232.  
  2233. static void End(Widget w, XEvent *event, String *, Cardinal *)
  2234. {
  2235.     const GraphEditWidget _w   = GraphEditWidget(w);
  2236.     const Graph* graph         = _w->graphEdit.graph;
  2237.     const GraphGC& graphGC     = _w->graphEdit.graphGC;
  2238.     const BoxPoint& lastOffset = _w->graphEdit.lastOffset;
  2239.     Cursor defaultCursor       = _w->graphEdit.defaultCursor;
  2240.  
  2241.     BoxPoint& endAction        = _w->graphEdit.endAction;
  2242.     GraphEditState& state      = _w->graphEdit.state;
  2243.  
  2244.     Boolean changed = False;
  2245.  
  2246.     switch(state)
  2247.     {
  2248.     case SelectState:
  2249.     {
  2250.         drawSelectFrame(w);
  2251.         endAction = point(event);
  2252.  
  2253.         BoxRegion selected = frameRegion(w);
  2254.         Boolean have_unselected_nodes = False;
  2255.  
  2256.         // Find all nodes in frame and select them
  2257.         for (GraphNode *node = graph->firstVisibleNode(); node != 0;
  2258.         node = graph->nextVisibleNode(node))
  2259.         {
  2260.         if (!node->selected())
  2261.         {
  2262.             // Both corners must be inside frame
  2263.             BoxPoint nw = node->region(graphGC).origin();
  2264.             BoxPoint se = nw + node->region(graphGC).space() - 1;
  2265.  
  2266.             if (nw <= selected && se <= selected)
  2267.             {
  2268.             have_unselected_nodes = True;
  2269.             node->selected() = True;
  2270.             graphEditRedrawNode(w, node);
  2271.             changed = True;
  2272.             }
  2273.         }
  2274.         }
  2275.  
  2276.         if (!have_unselected_nodes)
  2277.         {
  2278.         // All selected nodes are already selected - unselect them
  2279.         for (GraphNode *node = graph->firstVisibleNode(); node != 0;
  2280.              node = graph->nextVisibleNode(node))
  2281.         {
  2282.             if (node->selected())
  2283.             {
  2284.             // Both corners must be inside frame
  2285.             BoxPoint nw = node->region(graphGC).origin();
  2286.             BoxPoint se = nw + node->region(graphGC).space() - 1;
  2287.  
  2288.             if (nw <= selected && se <= selected)
  2289.             {
  2290.                 node->selected() = False;
  2291.                 graphEditRedrawNode(w, node);
  2292.                 changed = True;
  2293.             }
  2294.             }
  2295.         }
  2296.         }
  2297.  
  2298.         state = NopState;
  2299.         break;
  2300.     }
  2301.  
  2302.         case MoveState:
  2303.     {
  2304.         // Move all selected nodes to new positions
  2305.            
  2306.         // clear graph area
  2307.         drawOutlines(w, lastOffset);
  2308.  
  2309.             // move nodes
  2310.         endAction = point(event);
  2311.         BoxPoint offset = actionOffset(w);
  2312.         move_selected_nodes(w, offset);
  2313.  
  2314.         state = NopState;
  2315.         break;
  2316.     }
  2317.  
  2318.     default:
  2319.         // Do nothing
  2320.         break;
  2321.     }
  2322.  
  2323.     if (changed)
  2324.     selectionChanged(w, event, False);
  2325.  
  2326.     defineCursor(w, defaultCursor);
  2327. }
  2328.  
  2329. // Key movement action
  2330. static void MoveSelected(Widget w, XEvent *, String *params, 
  2331.              Cardinal *num_params)
  2332. {
  2333.     const GraphEditWidget _w      = GraphEditWidget(w);
  2334.     const Dimension gridWidth     = _w->graphEdit.gridWidth;
  2335.     const Dimension gridHeight    = _w->graphEdit.gridHeight;
  2336.     const BoxPoint& minimalOffset = _w->graphEdit.minimalOffset;
  2337.  
  2338.     BoxPoint grid(gridWidth, gridHeight);
  2339.  
  2340.     if (num_params == 0 || *num_params != 2)
  2341.     {
  2342.     cerr << "move-selected: usage: move-selected(X, Y)\n";
  2343.     return;
  2344.     }
  2345.  
  2346.     BoxPoint offset;
  2347.  
  2348.     string offset_s[2];
  2349.     offset_s[X] = params[0];
  2350.     offset_s[Y] = params[1];
  2351.  
  2352.     BoxDimension d;
  2353.     for (d = X; d <= Y; d++)
  2354.     {
  2355.     BoxCoordinate& c = offset[d];
  2356.     string& s = offset_s[d];
  2357.  
  2358.     if (s == "+grid" || s == "grid")
  2359.         c = +grid[d];
  2360.     else if (s == "-grid")
  2361.         c = -grid[d];
  2362.     else
  2363.     {
  2364.         char *ptr;
  2365.         char *str = (char *)s;
  2366.         c = strtol(str, &ptr, 0);
  2367.         if (ptr == str)
  2368.         {
  2369.         cerr << "move-selected: illegal argument " << str << "\n";
  2370.         return;
  2371.         }
  2372.     }
  2373.     }
  2374.  
  2375.     getMinimalOffset(w);
  2376.     
  2377.     for (d = X; d <= Y; d++)
  2378.     {
  2379.     // Offset must not move nodes out of area
  2380.     if (offset[d] < minimalOffset[d])
  2381.         offset[d] = minimalOffset[d];
  2382.     }
  2383.  
  2384.     if (offset != BoxPoint(0, 0))
  2385.     {
  2386.     move_selected_nodes(w, offset);
  2387.     graphEditSizeChanged(w);
  2388.     }
  2389. }
  2390.  
  2391.  
  2392. // Select single node
  2393. static void select_single_node(Widget w, XEvent *event, GraphNode *selectNode)
  2394. {
  2395.     if (selectNode == 0)
  2396.     return;
  2397.  
  2398.     const GraphEditWidget _w   = GraphEditWidget(w);
  2399.     const Graph* graph         = _w->graphEdit.graph;
  2400.  
  2401.     Boolean changed = False;
  2402.  
  2403.     for (GraphNode *node = graph->firstVisibleNode(); 
  2404.      node != 0;
  2405.      node = graph->nextVisibleNode(node))
  2406.     {
  2407.     if (node != selectNode && node->selected())
  2408.     {
  2409.         node->selected() = False;
  2410.         changed = True;
  2411.         graphEditRedrawNode(w, node);
  2412.     }
  2413.     }
  2414.  
  2415.     if (!selectNode->selected())
  2416.     {
  2417.     selectNode->selected() = True;
  2418.     changed = True;
  2419.     raise_node(w, selectNode);
  2420.     graphEditRedrawNode(w, selectNode);
  2421.     }
  2422.  
  2423.     if (changed)
  2424.     selectionChanged(w, event, False);
  2425. }
  2426.  
  2427. // Select first node
  2428. static void SelectFirst(Widget w, XEvent *event, String *, Cardinal *)
  2429. {
  2430.     const GraphEditWidget _w   = GraphEditWidget(w);
  2431.     const Graph* graph         = _w->graphEdit.graph;
  2432.  
  2433.     select_single_node(w, event, graph->firstVisibleNode());
  2434. }
  2435.  
  2436. // Select next node
  2437. static void SelectNext(Widget w, XEvent *event, String *, Cardinal *)
  2438. {
  2439.     const GraphEditWidget _w   = GraphEditWidget(w);
  2440.     const Graph* graph         = _w->graphEdit.graph;
  2441.  
  2442.     GraphNode *selectNode = 0;
  2443.     for (GraphNode *node = graph->firstVisibleNode(); 
  2444.      node != 0;
  2445.      node = graph->nextVisibleNode(node))
  2446.     {
  2447.     if (node->selected())
  2448.     {
  2449.         selectNode = graph->nextVisibleNode(node);
  2450.         break;
  2451.     }
  2452.     }
  2453.  
  2454.     if (selectNode == 0)
  2455.     selectNode = graph->firstVisibleNode();
  2456.  
  2457.     select_single_node(w, event, selectNode);
  2458. }
  2459.  
  2460. // Select previous node
  2461. static void SelectPrev(Widget w, XEvent *event, String *, Cardinal *)
  2462. {
  2463.     const GraphEditWidget _w = GraphEditWidget(w);
  2464.     const Graph* graph       = _w->graphEdit.graph;
  2465.  
  2466.     GraphNode *lastNode = 0;
  2467.     GraphNode *selectNode = 0;
  2468.     for (GraphNode *node = graph->firstVisibleNode(); 
  2469.      node != 0;
  2470.      node = graph->nextVisibleNode(node))
  2471.     {
  2472.     if (node->selected())
  2473.         selectNode = lastNode;
  2474.     lastNode = node;
  2475.     }
  2476.  
  2477.     if (selectNode == 0)
  2478.     selectNode = lastNode;
  2479.  
  2480.     select_single_node(w, event, selectNode);
  2481. }
  2482.  
  2483. // Return nearest grid position near P
  2484. static BoxPoint NearestGridPosition(const BoxPoint& grid, const BoxPoint& p)
  2485. {
  2486.     BoxPoint pos(p);
  2487.  
  2488.     for (BoxDimension d = X; d <= Y; d++)
  2489.     if (grid[d] > 0)
  2490.         pos[d] = ((pos[d] + grid[d] / 2) / grid[d]) * grid[d];
  2491.  
  2492.     return pos;
  2493. }
  2494.  
  2495.  
  2496. // Return final position (if snapToGrid is enabled, for example)
  2497. BoxPoint graphEditFinalPosition(Widget w, const BoxPoint& p)
  2498. {
  2499.     XtCheckSubclass(w, GraphEditWidgetClass, "Bad widget class");
  2500.  
  2501.     const GraphEditWidget _w   = GraphEditWidget(w);
  2502.     const Boolean snapToGrid   = _w->graphEdit.snapToGrid;
  2503.     const Dimension gridWidth  = _w->graphEdit.gridWidth;
  2504.     const Dimension gridHeight = _w->graphEdit.gridHeight;
  2505.  
  2506.     if (snapToGrid)
  2507.     {
  2508.     BoxPoint grid(gridWidth, gridHeight);
  2509.     return NearestGridPosition(grid, p);
  2510.     }
  2511.     else
  2512.     return p;
  2513. }
  2514.  
  2515. // Snap nodes to grid
  2516. static void _SnapToGrid(Widget w, XEvent *, String *params, 
  2517.             Cardinal *num_params)
  2518. {
  2519.     const GraphEditWidget _w   = GraphEditWidget(w);
  2520.     const Graph* graph         = _w->graphEdit.graph;
  2521.     const Dimension gridWidth  = _w->graphEdit.gridWidth;
  2522.     const Dimension gridHeight = _w->graphEdit.gridHeight;
  2523.  
  2524.     BoxPoint grid(gridWidth, gridHeight);
  2525.  
  2526.     if (*num_params >= 1)
  2527.     grid[X] = atoi(params[0]);
  2528.     if (*num_params >= 2)
  2529.     grid[Y] = atoi(params[1]);
  2530.  
  2531.     for (GraphNode *node = graph->firstVisibleNode(); node != 0;
  2532.     node = graph->nextVisibleNode(node))
  2533.     {
  2534.     BoxPoint pos = NearestGridPosition(grid, node->pos());
  2535.     if (pos != node->pos())
  2536.     {
  2537.             // Set new node position
  2538.         moveTo(w, node, pos, graph->nextVisibleNode(node) == 0);
  2539.     }
  2540.     }
  2541. }
  2542.  
  2543. static void SnapToGrid(Widget w, XEvent *event, String *params,
  2544.     Cardinal *num_params)
  2545. {
  2546.     _SnapToGrid(w, event, params, num_params);
  2547.     graphEditRedraw(w);
  2548. }
  2549.  
  2550. static int get_new_rotation(Widget w, String *params, Cardinal *num_params,
  2551.                 String name, String default_param, 
  2552.                 String extra_args = "")
  2553. {
  2554.     const GraphEditWidget _w   = GraphEditWidget(w);
  2555.     const Cardinal rotation    = _w->graphEdit.rotation;
  2556.  
  2557.     string param = "";
  2558.     if (num_params && *num_params >= 1)
  2559.     param = params[0];
  2560.     if (param == "")
  2561.     param = default_param;
  2562.  
  2563.     int new_rotation = atoi(param);
  2564.     new_rotation = (new_rotation % 360 + 360) % 360;
  2565.  
  2566.     if (new_rotation % 90 != 0)
  2567.     {
  2568.     cerr << name << ": usage: " << name << "(" 
  2569.          << extra_args << "[[+-]DEGREES]), "
  2570.         "where DEGREES is a multiple of 90\n";
  2571.     return -1;
  2572.     }
  2573.  
  2574.     if (param[0] == '+')
  2575.     new_rotation = (rotation + new_rotation + 360) % 360;
  2576.     else if (param[0] == '-')
  2577.     new_rotation = (rotation - new_rotation + 360) % 360;
  2578.  
  2579.     return new_rotation;
  2580. }
  2581.  
  2582.  
  2583. // Rotate nodes
  2584. static void _Rotate(Widget w, XEvent *event, String *params,
  2585.     Cardinal *num_params)
  2586. {
  2587.     const GraphEditWidget _w   = GraphEditWidget(w);
  2588.     const Graph* graph         = _w->graphEdit.graph;
  2589.     Cardinal& rotation         = _w->graphEdit.rotation;
  2590.  
  2591.     int new_rotation = 
  2592.     get_new_rotation(w, params, num_params, "rotate", "+90");
  2593.     if (new_rotation < 0)
  2594.     return;
  2595.  
  2596.     int width  = _w->core.width;
  2597.     int height = _w->core.height;
  2598.  
  2599.     for (int offset  = (rotation - new_rotation + 360) % 360;
  2600.      offset > 0; offset -= 90)
  2601.     {
  2602.     for (GraphNode *node = graph->firstNode(); 
  2603.          node != 0;
  2604.          node = graph->nextNode(node))
  2605.     {
  2606.         BoxPoint pos = node->pos();
  2607.         pos[X] = width - node->pos()[Y];
  2608.         pos[Y] = node->pos()[X];
  2609.  
  2610.         if (pos != node->pos())
  2611.         {
  2612.         moveTo(w, node, pos, 
  2613.                graph->nextNode(node) == 0 && new_rotation <= 90);
  2614.         }
  2615.     }
  2616.  
  2617.     int tmp = width;
  2618.     width = height;
  2619.     height = tmp;
  2620.     }
  2621.  
  2622.     rotation = new_rotation;
  2623.  
  2624.     Cardinal zero = 0;
  2625.     _Normalize(w, event, 0, &zero);
  2626.     _SnapToGrid(w, event, 0, &zero);
  2627. }
  2628.  
  2629. static void Rotate(Widget w, XEvent *event, String *params,
  2630.     Cardinal *num_params)
  2631. {
  2632.     const GraphEditWidget _w   = GraphEditWidget(w);
  2633.     const Boolean autoLayout   = _w->graphEdit.autoLayout;
  2634.  
  2635.     _Rotate(w, event, params, num_params);
  2636.     if (autoLayout)
  2637.     {
  2638.     Cardinal zero = 0;
  2639.     _Layout(w, event, 0, &zero);
  2640.     }
  2641.     graphEditRedraw(w);
  2642. }
  2643.  
  2644.  
  2645.  
  2646. // Layout nodes
  2647.  
  2648. static Graph *layout_graph = 0;
  2649. static Widget layout_widget = 0;
  2650.  
  2651. static void LayoutNodeCB(char *node_name, int x, int y)
  2652. {
  2653.     GraphNode *node = (GraphNode *)strtoul(node_name, 0, 16);
  2654.     assert(node != 0);
  2655.  
  2656.     node->moveTo(BoxPoint(x, y));
  2657. }
  2658.  
  2659. static void LayoutHintCB(char *from_name, char *to_name, int x, int y)
  2660. {
  2661.     GraphNode *from = (GraphNode *)strtoul(from_name, 0, 16);
  2662.     assert(from != 0);
  2663.     GraphNode *to   = (GraphNode *)strtoul(to_name, 0, 16);
  2664.     assert(to != 0);
  2665.  
  2666.     for (GraphEdge *edge = from->firstFrom();
  2667.      edge != 0;
  2668.      edge = from->nextFrom(edge))
  2669.     {
  2670.     if (edge->hidden())
  2671.         continue;
  2672.  
  2673.     GraphNode *n = edge->to();
  2674.     while (n->isHint())
  2675.         n = n->firstFrom()->to();
  2676.     if (n == to)
  2677.     {
  2678.         // We hide the original edge...
  2679.         edge->hidden() = True;
  2680.  
  2681.         // ... fetch its annotation ...
  2682.         LineGraphEdge *ge = ptr_cast(LineGraphEdge, edge);
  2683.         EdgeAnnotation *anno = ge->annotation();
  2684.         if (anno != 0)
  2685.         anno = anno->dup();
  2686.  
  2687.         // ... and insert an edge hint at the end
  2688.         // of the path between FROM and TO.
  2689.         HintGraphNode *hint = new HintGraphNode(BoxPoint(x, y));
  2690.      
  2691.         *layout_graph += hint;
  2692.         *layout_graph += new LineGraphEdge(edge->from(), hint, anno);
  2693.         *layout_graph += new LineGraphEdge(hint, edge->to());
  2694.  
  2695.         return;
  2696.     }
  2697.     }
  2698. }
  2699.  
  2700. static int LayoutCompareCB(char *name1, char *name2)
  2701. {
  2702.     GraphNode *node1 = (GraphNode *)strtoul(name1, 0, 16);
  2703.     assert(node1 != 0);
  2704.     GraphNode *node2 = (GraphNode *)strtoul(name2, 0, 16);
  2705.     assert(node2 != 0);
  2706.  
  2707.     GraphEditCompareNodesInfo info;
  2708.  
  2709.     info.graph = layout_graph;
  2710.     info.node1 = node1;
  2711.     info.node2 = node2;
  2712.  
  2713.     XtCallCallbacks(layout_widget, XtNcompareNodesCallback, caddr_t(&info));
  2714.  
  2715.     return info.result;
  2716. }
  2717.  
  2718. string node_name(GraphNode *node)
  2719. {
  2720.     char buffer[BUFSIZ];
  2721.     sprintf(buffer, "0x%lx", (unsigned long) node);
  2722.     return string(buffer);
  2723. }
  2724.  
  2725. static void remove_all_hints(Graph *graph)
  2726. {
  2727.     // Find all hint nodes
  2728.     GraphNodePointerArray hints;
  2729.  
  2730.     for (GraphNode *node = graph->firstNode(); 
  2731.      node != 0;
  2732.      node = graph->nextNode(node))
  2733.     {
  2734.     if (node->isHint())
  2735.         hints += node;
  2736.     }
  2737.  
  2738.     // Remove hint nodes
  2739.     for (int i = 0; i < hints.size(); i++)
  2740.     *graph -= hints[i];
  2741.  
  2742.     // Enable remaining edges
  2743.     for (GraphEdge *edge = graph->firstEdge(); 
  2744.      edge != 0;
  2745.      edge = graph->nextEdge(edge))
  2746.     {
  2747.     edge->hidden() = False;
  2748.     }
  2749. }
  2750.  
  2751. // Replace all paths A -> HINT_1 -> HINT_2 -> ... -> HINT_N -> B
  2752. // by A -> B, where B is placed at the position of HINT_1.
  2753.  
  2754. static void compact_layouted_graph(Graph *graph)
  2755. {
  2756.     for (GraphNode *node = graph->firstNode();
  2757.      node != 0;
  2758.      node = graph->nextNode(node))
  2759.     {
  2760.     if (!node->isHint())
  2761.     {
  2762.         for (GraphEdge *edge = node->firstFrom();
  2763.          edge != 0; edge = node->nextFrom(edge))
  2764.         {
  2765.         if (edge->to()->isHint())
  2766.         {
  2767.             GraphNode *hint = edge->to();
  2768.             const BoxPoint& hintPos = hint->pos();
  2769.  
  2770.             GraphEdge *hint_edge = hint->firstFrom();
  2771.             while (hint_edge && hint_edge->to()->isHint())
  2772.             hint_edge = hint_edge->to()->firstFrom();
  2773.  
  2774.             if (hint_edge)
  2775.             {
  2776.             // Move non-hint node at first hint position
  2777.             hint_edge->to()->moveTo(hintPos);
  2778.             }
  2779.  
  2780.             // Remove edge to hint such that we are not visited again
  2781.             *graph -= edge;
  2782.         }
  2783.         }
  2784.     }
  2785.     }
  2786.  
  2787.     remove_all_hints(graph);
  2788. }    
  2789.  
  2790. static void _Layout(Widget w, XEvent *event, String *params,
  2791.     Cardinal *num_params)
  2792. {
  2793.     const GraphEditWidget _w   = GraphEditWidget(w);
  2794.     Graph* graph               = _w->graphEdit.graph;
  2795.     const GraphGC& graphGC     = _w->graphEdit.graphGC;
  2796.     Cardinal& rotation         = _w->graphEdit.rotation;
  2797.     LayoutMode mode            = _w->graphEdit.layoutMode;
  2798.     Boolean& autoLayout        = _w->graphEdit.autoLayout;
  2799.  
  2800.     static char graph_name[] = "graph";
  2801.  
  2802.     if (num_params && *num_params > 0 && params[0][0] != '\0')
  2803.     {
  2804.     LayoutMode mode_param;
  2805.     XrmValue v1, v2;
  2806.     v1.addr = caddr_t(params[0]);
  2807.     v1.size = sizeof(String);
  2808.     v2.addr = caddr_t(&mode_param);
  2809.     v2.size = sizeof(LayoutMode);
  2810.  
  2811.     Boolean ok = 
  2812.         XtConvertAndStore(w, XtRString, &v1, XtRLayoutMode, &v2);
  2813.     if (ok)
  2814.         mode = mode_param;
  2815.     }
  2816.  
  2817.     Cardinal new_num_params = 
  2818.     (num_params && *num_params > 0 ? *num_params - 1 : 0);
  2819.     int new_rotation = 
  2820.     get_new_rotation(w, params - 1, &new_num_params, 
  2821.              "layout", "+0", "MODE, ");
  2822.     if (new_rotation < 0)
  2823.     return;
  2824.  
  2825.     // Don't get called again while setting values from hooks
  2826.     Boolean old_autoLayout = autoLayout;
  2827.     autoLayout = False;
  2828.  
  2829.     // Call hooks before layouting
  2830.     GraphEditLayoutInfo info;
  2831.     info.graph    = graph;
  2832.     info.mode     = mode;
  2833.     info.rotation = new_rotation;
  2834.     XtCallCallbacks(w, XtNpreLayoutCallback, caddr_t(&info));
  2835.  
  2836.     // Remove all hint nodes
  2837.     remove_all_hints(graph);
  2838.  
  2839.     // Send graph to layouter
  2840.     Layout::add_graph(graph_name);
  2841.  
  2842.     for (GraphNode *node = graph->firstVisibleNode(); 
  2843.      node != 0;
  2844.      node = graph->nextVisibleNode(node))
  2845.     {
  2846.     BoxRegion r = node->region(graphGC);
  2847.     int width  = r.space(X);
  2848.     int height = r.space(Y);
  2849.  
  2850.     if ((new_rotation % 180) / 90 > 0)
  2851.     {
  2852.         int tmp = width;
  2853.         width = height;
  2854.         height = tmp;
  2855.     }
  2856.  
  2857.     string name = node_name(node);
  2858.     Layout::add_node(graph_name, name);
  2859.     Layout::set_node_width(graph_name, name, width);
  2860.     Layout::set_node_height(graph_name, name, height);
  2861.     Layout::set_node_position(graph_name, name, -1, -1);
  2862.     }
  2863.  
  2864.     for (GraphEdge *edge = graph->firstVisibleEdge(); 
  2865.      edge != 0;
  2866.      edge = graph->nextVisibleEdge(edge))
  2867.     {
  2868.     Layout::add_edge(graph_name, 
  2869.              node_name(edge->from()), node_name(edge->to()));
  2870.     }
  2871.  
  2872.     // Layout the graph
  2873.     layout_widget = w;
  2874.     layout_graph  = graph;
  2875.     Layout::node_callback    = LayoutNodeCB;
  2876.     Layout::hint_callback    = LayoutHintCB;
  2877.     Layout::compare_callback = LayoutCompareCB;
  2878.     Layout::layout(graph_name);
  2879.  
  2880.     // Post-process graph for compact representation
  2881.     if (mode == CompactLayoutMode)
  2882.     compact_layouted_graph(graph);
  2883.  
  2884.     // Clear the graph...
  2885.     Layout::remove_graph(graph_name);
  2886.  
  2887.     // ... and re-rotate it.
  2888.     ostrstream os;
  2889.     os << new_rotation;
  2890.  
  2891.     string rotation_s = os;
  2892.     Cardinal rotate_num_params = 1;
  2893.     String rotate_params[1];
  2894.     rotate_params[0] = rotation_s;
  2895.  
  2896.     rotation = 0;
  2897.     _Rotate(w, event, rotate_params, &rotate_num_params);
  2898.  
  2899.     // Layout is done
  2900.     XtCallCallbacks(w, XtNpostLayoutCallback, caddr_t(&info));
  2901.  
  2902.     autoLayout = old_autoLayout;
  2903. }
  2904.  
  2905. // DoLayout() should be named Layout(), but this conflicts with the
  2906. // `Layout' class on some pre-ARM C++ compilers :-(
  2907. static void DoLayout(Widget w, XEvent *event, String *params,
  2908.     Cardinal *num_params)
  2909. {
  2910.     _Layout(w, event, params, num_params);
  2911.     graphEditRedraw(w);
  2912. }
  2913.  
  2914.  
  2915. // Normalize graph
  2916. static void _Normalize(Widget w, XEvent *, String *, Cardinal *)
  2917. {
  2918.     const GraphEditWidget _w   = GraphEditWidget(w);
  2919.     const Graph* graph         = _w->graphEdit.graph;
  2920.     const GraphGC& graphGC     = _w->graphEdit.graphGC;
  2921.     const Dimension gridHeight = _w->graphEdit.gridHeight;
  2922.     const Dimension gridWidth  = _w->graphEdit.gridWidth;
  2923.  
  2924.     BoxRegion r = graph->region(graphGC);
  2925.  
  2926.     for (GraphNode *node = graph->firstVisibleNode(); 
  2927.      node != 0;
  2928.      node = graph->nextVisibleNode(node))
  2929.     {
  2930.     BoxPoint pos = node->pos() - r.origin() 
  2931.         + BoxPoint(gridHeight, gridWidth);
  2932.  
  2933.     if (pos != node->pos())
  2934.     {
  2935.             // set new node position
  2936.         moveTo(w, node, pos, graph->nextVisibleNode(node) == 0);
  2937.     }
  2938.     }
  2939. }
  2940.  
  2941. static void Normalize(Widget w, XEvent *event, String *params,
  2942.     Cardinal *num_params)
  2943. {
  2944.     _Normalize(w, event, params, num_params);
  2945.     graphEditRedraw(w);
  2946. }
  2947.  
  2948.  
  2949. // Show and hide edges
  2950.  
  2951. static void considerEdges(Widget w, XEvent *, String *params,
  2952.               Cardinal *num_params, Boolean shallBeHidden)
  2953. {
  2954.     const GraphEditWidget _w = GraphEditWidget(w);
  2955.     const Graph* graph       = _w->graphEdit.graph;
  2956.  
  2957.     // get the mode
  2958.     enum { Nope = 0, Both = 1, From = 2, To = 3, Any = 4 } themode = Nope;
  2959.     Boolean changedSomething = False;
  2960.  
  2961.     string p = "any";
  2962.     if (*num_params >= 1)
  2963.     p = params[0];
  2964.  
  2965.     if (p == "from")
  2966.     themode = From;
  2967.     else if (p == "to")
  2968.     themode = To;
  2969.     else if (p == "any")
  2970.     themode = Any;
  2971.     else if (p == "both")
  2972.     themode = Both;
  2973.     else
  2974.     cerr << "show-edges: bad mode " << '`' << p << "'" << "\n";
  2975.  
  2976.     for (GraphEdge *edge = graph->firstEdge(); edge != 0;
  2977.     edge = graph->nextEdge(edge))
  2978.     {
  2979.     Boolean set = False;
  2980.  
  2981.     switch (themode)
  2982.     {
  2983.         // There should be a better way of coding this, but I don't know...
  2984.  
  2985.         case From:
  2986.         set = edge->from()->selected();
  2987.         break;
  2988.  
  2989.         case To:
  2990.         set = edge->to()->selected();
  2991.         break;
  2992.  
  2993.         case Any:
  2994.         set = edge->to()->selected() || edge->from()->selected();
  2995.         break;
  2996.  
  2997.         case Both:
  2998.         set = edge->to()->selected() && edge->from()->selected();
  2999.         break;
  3000.  
  3001.         case Nope:
  3002.         set = False;
  3003.         break;
  3004.     }
  3005.  
  3006.     if (set)
  3007.     {
  3008.         if (edge->hidden() != shallBeHidden)
  3009.         {
  3010.         changedSomething = True;
  3011.         edge->hidden() = shallBeHidden;
  3012.         }
  3013.     }
  3014.     }
  3015.  
  3016.     if (changedSomething)
  3017.     graphEditRedraw(w);
  3018. }
  3019.  
  3020. static void ShowEdges(Widget w, XEvent *event, String *params,
  3021.     Cardinal *num_params)
  3022. {
  3023.     considerEdges(w, event, params, num_params, False);
  3024. }
  3025.  
  3026. static void HideEdges(Widget w, XEvent *event, String *params,
  3027.     Cardinal *num_params)
  3028. {
  3029.     considerEdges(w, event, params, num_params, True);
  3030. }
  3031.