]> granicus.if.org Git - graphviz/commitdiff
remove lefty-grace contrib tool
authorMatthew Fernandez <matthew.fernandez@gmail.com>
Sun, 22 Jan 2023 00:41:42 +0000 (16:41 -0800)
committerMatthew Fernandez <matthew.fernandez@gmail.com>
Thu, 26 Jan 2023 03:16:19 +0000 (19:16 -0800)
This tool was an enhancement to Lefty that was removed in
b2f287ca315dd5bf689d9ce2ca71dbfe63fb8a61.

contrib/INDEX
contrib/Makefile.am
contrib/lefty-grace/README [deleted file]
contrib/lefty-grace/dotty.lefty [deleted file]
contrib/lefty-grace/dotty_draw.lefty [deleted file]
contrib/lefty-grace/dotty_edit.lefty [deleted file]
contrib/lefty-grace/dotty_ui.lefty [deleted file]

index c39380c15002248fd219551cdfab796885a509f7..a120314c74ac3d53322fbe6c00cf64bd3b20a60c 100644 (file)
@@ -1,10 +1,5 @@
 Contributed software
 
-  lefty-grace :
-    user interface enhancements to dotty
-    Robert M. Grace
-    rgrace@draper.com
-
   dirgraph :
     Perl script for creating a dot graph file from a directory hierarchy
     Diomidis Spinellis
index 5ea4bfc6b1a0a125931a120c243ea84bfd63c3d0..7871637a5ee8f16cb88e3d1e2734053d9785b4c5 100644 (file)
@@ -3,4 +3,4 @@
 SUBDIRS = prune diffimg
 
 EXTRA_DIST = INDEX dotmcl.pl gprof2dot.awk \
-       sdlshapes lefty-grace dirgraph pangotest
+       sdlshapes dirgraph pangotest
diff --git a/contrib/lefty-grace/README b/contrib/lefty-grace/README
deleted file mode 100644 (file)
index 52ee26d..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-
-Enhancements to the dotty interface:
-
-- Smooth scroll following mouse - activated by pressing/holding the TAB
-  key down and scrolling to point where mouse pointer is.
-- Add "zoom slowly" function to use , and . and follow mouse to fine-grain
-  zoom in on a particular region w/ mouse panning.  'Z' and 'z'
-  still work for coarse zoom, but this will pan to cursor as well.
-- Add new node with 'n' key (dialog prompts for name).  This worked 
-  better for me than the dual-use left mouse button.
-- Add new edge with 'e' key (ditto)
-- Folding option, using 'f' key over node to fold all child
-  nodes into parent (folded node has "Msquare" cornered box appearance)
-  , and unfold with another 'f' keypress.  Child
-  nodes that are shared with another parent are not folded.
-  Would like to use unique node shape, or maybe you can think of
-  a better way to represent a folded node (thick border, etc?)
-- Added a 'change label' menu item for node/edge menu
-- Added 'Q' for quit, which prompts for save
-  (will unfold any folded nodes before any save is done, since I haven't
-  figured out how to encode/restore fold info to/from the graph file)
-- Added save prompt for current graph on 'load graph' menu selection
-- Rudimentary "hyperlink" - click on node and select "load subgraph"
-  from the menu to load and display ./{nodename}.gv graph.  Can
-  navigate back to immediate parent through menu selection.
-  '+' also goes to parent, '-' to subgraph of a node.  Was not able to
-  store parent information in graph spec file, so a graph has to be 
-  loaded as a subgraph to another graph before the "parent" feature will
-  work.  Needs improvement...
-
-To install, copy the *.lefty files here into 
-
-  <GraphVizRoot>/lib/lefty
-
-or before building and installing, copy the files into
-
-  <GraphVizRoot>/src/pkg/dotty
-
-and then build the system.
-
-Author:
- Robert M. Grace
- Software Engineer
- Draper Laboratory
- 555 Technology Sq. MS 34 
- Cambridge, MA 02139
- rgrace@draper.com
-
diff --git a/contrib/lefty-grace/dotty.lefty b/contrib/lefty-grace/dotty.lefty
deleted file mode 100644 (file)
index 052e4e4..0000000
+++ /dev/null
@@ -1,736 +0,0 @@
-#
-# DOTTY
-#
-dotty = [
-    'keys' = [
-        'nid'    = 'nid';
-        'eid'    = 'eid';
-        'gid'    = 'gid';
-        'name'   = 'name';
-        'attr'   = 'attr';
-        'gattr'  = 'graphattr';
-        'eattr'  = 'edgeattr';
-        'nattr'  = 'nodeattr';
-        'edges'  = 'edges';
-        'tail'   = 'tail';
-        'tport'  = 'tport';
-        'head'   = 'head';
-        'hport'  = 'hport';
-        'pos'    = 'pos';
-        'size'   = 'size';
-        'points' = 'points';
-        'fname'  = 'fontname';
-        'fsize'  = 'fontsize';
-        'fcolor' = 'fontcolor';
-        'color'  = 'color';
-        'parent' = 'parent';
-    ];
-    'maps' = [
-        'X11' = [
-            'fontmap' = [
-                'Times-Roman'  =
-                        '-*-times-medium-r-*--%d-*-*-*-*-*-*-1';
-                'Times-Italic' =
-                        '-*-times-medium-i-*--%d-*-*-*-*-*-*-1';
-                'Times-Bold'  =
-                        '-*-times-bold-r-*--%d-*-*-*-*-*-*-1';
-                'Courier'      =
-                        '-*-courier-bold-r-*--%d-*-*-*-*-*-*-1';
-                'Courier-Bold' =
-                        '-*-courier-bold-r-*--%d-*-*-*-*-*-*-1';
-            ];
-            'psfontmap' = [
-                'Times-Roman'  = 'Times-Roman';
-                'Times-Italic' = 'Times-Italic';
-                'Times-Bold'   = 'Times-Bold';
-                'Courier'      = 'Courier';
-                'Courier-Bold' = 'Courier-Bold';
-            ];
-        ];
-        'mswin' = [
-            'fontmap' = [
-                'Times-Roman'  = 'Times New Roman';
-                'Times-Italic' = 'Times New Roman Italic';
-                'Times-Bold'   = 'Times New Roman Bold';
-                'Courier'      = 'Courier New';
-                'Courier-Bold' = 'Courier New Bold';
-            ];
-            'psfontmap' = [
-                'Times-Roman'  = 'Times New Roman';
-                'Times-Italic' = 'Times New Roman Italic';
-                'Times-Bold'   = 'Times New Roman Bold';
-                'Courier'      = 'Courier New';
-                'Courier-Bold' = 'Courier New Bold';
-            ];
-        ];
-    ];
-    'protogt' = [
-        'graph' = [
-            'graphattr' = [
-                'fontsize' = '14';
-                'fontname' = 'Times-Roman';
-                'fontcolor' = 'black';
-                'color' = 'black';
-            ];
-            'nodeattr' = [
-                'shape' = 'ellipse';
-                'fontsize' = '14';
-                'fontname' = 'Times-Roman';
-                'fontcolor' = 'black';
-                'color' = 'black';
-                'folded' = '0';
-            ];
-            'edgeattr' = [
-                'fontsize' = '14';
-                'fontname' = 'Times-Roman';
-                'fontcolor' = 'black';
-                'color' = 'black';
-            ];
-            'graphdict' = [];
-            'nodedict' = [];
-            'graphs' = [];
-            'nodes' = [];
-            'edges' = [];
-            'maxgid' = 0;
-            'maxnid' = 0;
-            'maxeid' = 0;
-            'type' = 'digraph';
-        ];
-        'layoutmode' = 'sync';
-        'lserver' = 'dot';
-        'edgehandles' = 1;
-        'noundo' = 0;
-    ];
-    'lservers' = [];
-    'mlevel' = 0;
-    'graphs' = [];
-    'views' = [];
-    'protovt' = [
-        'normal' = [
-            'name' = 'DOTTY';
-            'orig' = ['x' = 1; 'y' = 60;];
-            'size' = ['x' = 520; 'y' = 640;];
-            'wrect' = [
-                0 = ['x' = 0; 'y' = 0;];
-                1 = ['x' = 400; 'y' = 500;];
-            ];
-            'vsize' = ['x' = 400; 'y' = 500;];
-            'w2v' = 1;
-        ];
-        'birdseye' = [
-            'type' = 'birdseye';
-            'name' = 'DOTTY birdseye view';
-            'orig' = ['x' = 1; 'y' = 1;];
-            'size' = ['x' = 220; 'y' = 260;];
-            'wrect' = [
-                0 = ['x' = 0; 'y' = 0;];
-                1 = ['x' = 200; 'y' = 250;];
-            ];
-            'vsize' = ['x' = 200; 'y' = 250;];
-            'w2v' = 1;
-        ];
-    ];
-    'pagesizes' = [
-        '8.5x11' = ['x' =    8; 'y' = 10.5;];
-        '11x17'  = ['x' = 10.5; 'y' = 16.5;];
-        '36x50'  = ['x' = 35.5; 'y' = 49.5;];
-    ];
-];
-load ('dotty_draw.lefty');
-load ('dotty_edit.lefty');
-load ('dotty_layout.lefty');
-load ('dotty_ui.lefty');
-#
-# initialization functions
-#
-dotty.init = function () {
-    dotty.outlinecolor = 1;
-    dotty.fontmap = dotty.maps[getenv ('LEFTYWINSYS')].fontmap;
-    dotty.clipgt = dotty.protogt.creategraph (['noundo' = 1;]);
-    dotty.inited = 1;
-};
-dotty.simple = function (file) {
-    if (dotty.inited ~= 1)
-        dotty.init ();
-    dotty.createviewandgraph (file, 'file', null, null);
-    txtview ('off');
-};
-#
-# main operations
-#
-dotty.protogt.creategraph = function (protogt) {
-    local gt, id, gtid;
-
-    if (~protogt)
-        protogt = dotty.protogt;
-    for (gtid = 0; dotty.graphs[gtid]; gtid = gtid + 1)
-        ;
-    gt = (dotty.graphs[gtid] = []);
-    if (protogt.mode ~= 'replace') {
-        for (id in dotty.protogt)
-            gt[id] = copy (dotty.protogt[id]);
-    }
-    for (id in protogt)
-        gt[id] = copy (protogt[id]);
-    gt.gtid = gtid;
-    gt.views = [];
-    gt.undoarray = ['level' = 0; 'entries' = [];];
-    gt.busy = 0;
-    return gt;
-};
-dotty.protogt.copygraph = function (ogt) {
-    local gt, gtid, id;
-
-    for (gtid = 0; dotty.graphs[gtid]; gtid = gtid + 1)
-        ;
-    gt = (dotty.graphs[gtid] = []);
-    for (id in ogt)
-        gt[id] = copy (ogt[id]);
-    gt.gtid = gtid;
-    gt.views = [];
-    gt.undoarray = ['level' = 0; 'entries' = [];];
-    gt.busy = 0;
-    return gt;
-};
-dotty.protogt.destroygraph = function (gt) {
-    local vid, vlist;
-
-    if (gt.layoutpending > 0)
-        gt.cancellayout (gt);
-    for (vid in gt.views)
-        vlist[vid] = gt.views[vid];
-    for (vid in gt.views)
-        gt.destroyview (gt, vlist[vid]);
-    remove (gt.gtid, dotty.graphs);
-};
-dotty.protogt.loadgraph = function (gt, name, type, protograph, layoutflag) {
-    local vid, vt, fd, graph, nid, eid, gid;
-
-    if (gt.layoutpending > 0)
-        gt.cancellayout (gt);
-    if (~name)
-        if (~(name = ask ('file name:', 'file', '')))
-            return;
-    dotty.pushbusy (gt, gt.views);
-    dotty.message (1, 'loading');
-    if (~protograph)
-        protograph = dotty.protogt.graph;
-    if (~((fd = dotty.openio (name, type, 'r')) >= 0) |
-            ~(graph = readgraph (fd, protograph))) {
-#        dotty.message (0, 'cannot load graph');
-        return;
-    }
-    for (vid in gt.views) {
-        vt = gt.views[vid];
-        vt.colors = [];
-        vt.colorn = 2;
-    }
-    gt.graph = graph;
-    gt.name = name;
-    gt.type = type;
-    gt.undoarray = ['level' = 0; 'entries' = [];];
-    if (~(type == 'file' & name == '-'))
-        closeio (fd);
-    graph.maxgid = tablesize (graph.graphs);
-    graph.maxnid = tablesize (graph.nodes);
-    graph.maxeid = tablesize (graph.edges);
-    for (nid in graph.nodes)
-        graph.nodes[nid][dotty.keys.nid] = nid;
-    for (eid in graph.edges)
-        graph.edges[eid][dotty.keys.eid] = eid;
-    for (gid in graph.graphs)
-        graph.graphs[gid][dotty.keys.gid] = gid;
-    gt.unpackattr (gt);
-    if (layoutflag) {
-        dotty.message (1, 'generating layout');
-        gt.layoutgraph (gt);
-    }
-    dotty.popbusy (gt, gt.views);
-    return gt.graph;
-};
-dotty.protogt.savegraph = function (gt, name, type, savecoord) {
-    local fd, graph, gid, sgraph, nid, node, eid, edge, pointi, attr;
-
-    if (~name)
-        if (~(name = ask ('file name:', 'file', '')))
-            return;
-    if (savecoord) {
-        graph = copy (gt.graph);
-        for (gid in graph.graphs) {
-            sgraph = graph.graphs[gid];
-            if (sgraph.rect)
-                sgraph.graphattr.bb = concat (sgraph.rect[0].x, ',',
-                        sgraph.rect[0].y, ',', sgraph.rect[1].x, ',',
-                        sgraph.rect[1].y);
-            if (sgraph.lp & tablesize (sgraph.lp) > 0)
-                sgraph.graphattr.lp = concat (sgraph.lp.x, ',', sgraph.lp.y);
-            else if (sgraph.lp)
-                sgraph.graphattr.lp = "";
-        }
-        for (nid in graph.nodes) {
-            node = graph.nodes[nid];
-            if (~node.attr.label)
-                node.attr.label = '\N';
-            if (node.pos)
-                node.attr.pos = concat (node.pos.x, ',', node.pos.y);
-            if (node.size) {
-                node.attr.width = node.size.x / 72;
-                node.attr.height = node.size.y / 72;
-            }
-            if (node.fields)
-                node.attr.rects = gt.packrecordfields (gt, node.fields);
-        }
-        for (eid in graph.edges) {
-            edge = graph.edges[eid];
-            if (edge.points) {
-                attr = '';
-                if (edge.sp)
-                    attr = concat ('s,', edge.sp.x, ',', edge.sp.y, ' ');
-                if (edge.ep)
-                    attr = concat (attr, 'e,', edge.ep.x, ',', edge.ep.y, ' ');
-                for (pointi = 0; edge.points[pointi]; pointi = pointi + 1)
-                    attr = concat (attr, ' ', edge.points[pointi].x, ',',
-                        edge.points[pointi].y);
-                edge.attr.pos = attr;
-            }
-            if (edge.lp)
-                edge.attr.lp = concat (edge.lp.x, ',', edge.lp.y);
-        }
-        if (graph.rect)
-            graph.graphattr.bb = concat (graph.rect[0].x, ',',
-                    graph.rect[0].y, ',', graph.rect[1].x, ',',
-                    graph.rect[1].y);
-        if (graph.lp & tablesize (graph.lp) > 0)
-            graph.graphattr.lp = concat (graph.lp.x, ',', graph.lp.y);
-    } else
-        graph = gt.graph;
-    if (~((fd = dotty.openio (name, type, 'w')) >= 0) |
-            ~writegraph (fd, graph, 0)) {
-        dotty.message (0, 'cannot save graph');
-        return;
-    }
-    if (~(type == 'file' & name == '-'))
-        closeio (fd);
-};
-dotty.protogt.packrecordfields = function (gt, fields) {
-    local attrs, attr, fid, field;
-
-    for (fid = 0; fields[fid]; fid = fid + 1) {
-        field = fields[fid];
-        if (field.fields)
-            attr = gt.packrecordfields (gt, field.fields);
-        else
-            attr = concat (field.rect[0].x, ',', field.rect[0].y,
-                    ',', field.rect[1].x, ',', field.rect[1].y);
-        if (attrs)
-            attrs = concat (attrs, ' ', attr);
-        else
-            attrs = attr;
-    }
-    return attrs;
-};
-dotty.protogt.setgraph = function (gt, graph) {
-    local vid, vt, nid, eid, gid;
-
-    if (gt.layoutpending > 0)
-        gt.cancellayout (gt);
-    for (vid in gt.views) {
-        vt = gt.views[vid];
-        vt.colors = [];
-        vt.colorn = 2;
-    }
-    gt.graph = copy (graph);
-    gt.undoarray = ['level' = 0; 'entries' = [];];
-    gt.unpackattr (gt);
-    gt.graph.maxgid = tablesize (graph.graphs);
-    gt.graph.maxnid = tablesize (graph.nodes);
-    gt.graph.maxeid = tablesize (graph.edges);
-    for (nid in gt.graph.nodes)
-        gt.graph.nodes[nid][dotty.keys.nid] = nid;
-    for (eid in gt.graph.edges)
-        gt.graph.edges[eid][dotty.keys.eid] = eid;
-    for (gid in gt.graph.graphs)
-        gt.graph.graphs[gid][dotty.keys.gid] = gid;
-    gt.unpackattr (gt);
-    dotty.message (1, 'generating layout');
-    gt.layoutgraph (gt);
-    return gt.graph;
-};
-dotty.protogt.erasegraph = function (gt, protogt, protovt) {
-    local vid, vt;
-
-    if (gt.layoutpending > 0)
-        gt.cancellayout (gt);
-    for (vid in gt.views) {
-        vt = gt.views[vid];
-        vt.colors = [];
-        vt.colorn = 2;
-        clear (vt.canvas);
-    }
-    if (~protogt)
-        protogt = dotty.protogt;
-    gt.graph = copy (protogt.graph);
-    gt.undoarray = ['level' = 0; 'entries' = [];];
-};
-dotty.protogt.layoutgraph = function (gt) {
-    if (gt.graph.graphattr.bb) {
-        gt.unpacklayout (gt, gt.graph);
-        gt.setviewsize (gt.views, gt.graph.rect);
-        gt.redrawgraph (gt, gt.views);
-        return;
-    }
-    if (gt.layoutmode == 'async') {
-        if (~gt.haveinput) {
-            gt.startlayout (gt);
-            return;
-        }
-        if (~gt.finishlayout (gt))
-            return;
-        gt.setviewsize (gt.views, gt.graph.rect);
-        gt.redrawgraph (gt, gt.views);
-    } else {
-        if (~gt.startlayout (gt))
-            return;
-        else
-            while (~gt.finishlayout (gt))
-                ;
-        gt.setviewsize (gt.views, gt.graph.rect);
-        gt.redrawgraph (gt, gt.views);
-    }
-};
-dotty.protogt.createview = function (gt, protovt) {
-    local vt, ovt, id, t;
-
-    vt = [];
-    vt.colors = [];
-    vt.colorn = 2;
-    if (~protovt)
-        protovt = dotty.protovt.normal;
-    if (protovt.mode ~= 'replace') {
-        for (id in dotty.protovt[protovt.type])
-            vt[id] = copy (dotty.protovt[protovt.type][id]);
-    }
-    for (id in protovt)
-        vt[id] = copy (protovt[id]);
-    if (~(vt.parent >= 0)) {
-        vt.view = createwidget (-1, [
-            'type'   = 'view';
-            'name'   = vt.name;
-            'origin' = vt.orig;
-            'size'   = vt.size;
-        ]);
-        vt.scroll = createwidget (vt.view, ['type' = 'scroll';]);
-    } else {
-        vt.view = -1;
-        vt.scroll = createwidget (vt.parent, [
-            'type' = 'scroll';
-            'size' = vt.size;
-        ]);
-    }
-    vt.canvas = createwidget (vt.scroll, ['type' = 'canvas';]);
-    setwidgetattr (vt.canvas, [
-        'window' = vt.wrect;
-        'viewport' = vt.vsize;
-    ]);
-    clear (vt.canvas);
-    dotty.views[vt.canvas] = vt;
-    vt.vtid = vt.canvas;
-    vt.gtid = gt.gtid;
-    gt.views[vt.vtid] = vt;
-    dotty.views[vt.scroll] = vt;
-    if (vt.view ~= -1)
-        dotty.views[vt.view] = vt;
-    if (protovt.colors & tablesize (protovt.colors) > 0) {
-        for (id in protovt.colors)
-            if (setwidgetattr (vt.canvas, ['color' = [
-                protovt.colors[id] = id;
-            ];]) ~= 1) {
-                t = split (id, ' ');
-                if (tablesize (t) ~= 3 | setwidgetattr (vt.canvas, [
-                    'color' = [protovt.colors[id] = [
-                        'h' = ston (t[0]); 's' = ston (t[1]); 'v' = ston (t[2]);
-                    ];];
-                ]) ~= 1) {
-                    dotty.message (0,
-                            concat ('unknown color ', id, ' using #1'));
-                }
-            }
-        vt.colors = copy (protovt.colors);
-        vt.colorn = protovt.colorn;
-    } else if (tablesize (gt.views) > 1) {
-        for (id in gt.views)
-            if (gt.views[id] ~= vt)
-                break;
-        ovt = gt.views[id];
-        for (id in ovt.colors)
-            if (setwidgetattr (vt.canvas, ['color' = [
-                ovt.colors[id] = id;
-            ];]) ~= 1) {
-                t = split (id, ' ');
-                if (tablesize (t) ~= 3 | setwidgetattr (vt.canvas, [
-                    'color' = [ovt.colors[id] = [
-                        'h' = ston (t[0]); 's' = ston (t[1]); 'v' = ston (t[2]);
-                    ];];
-                ]) ~= 1) {
-                    dotty.message (0,
-                            concat ('unknown color ', id, ' using #1'));
-                }
-            }
-        vt.colors = copy (ovt.colors);
-        vt.colorn = ovt.colorn;
-    }
-    if (gt.graph.rect)
-        gt.setviewsize ([vt.vtid = vt;], gt.graph.rect);
-    gt.drawgraph (gt, [vt.vtid = vt;]);
-    for (id in vt.uifuncs)
-        if (id == 'closeview')
-            widgets[vt.view][id] = vt.uifuncs[id];
-        else
-            widgets[vt.canvas][id] = vt.uifuncs[id];
-    return vt;
-};
-dotty.protogt.destroyview = function (gt, vt) {
-    destroywidget (vt.canvas);
-    destroywidget (vt.scroll);
-    if (vt.view ~= -1) {
-        destroywidget (vt.view);
-        remove (vt.view, dotty.views);
-    }
-    remove (vt.scroll, dotty.views);
-    remove (vt.canvas, dotty.views);
-    if (vt.gtid >= 0)
-        remove (vt.vtid, gt.views);
-    if (tablesize (dotty.views) == 0)
-        exit ();
-};
-dotty.protogt.zoom = function (gt, vt, factor, pos) {
-    gt.setviewscale ([vt.vtid = vt;], factor);
-    if (pos)
-        gt.setviewcenter ([vt.vtid = vt;], pos);
-     gt.redrawgraph (gt, [vt.vtid = vt;]);
-};
-dotty.protogt.findnode = function (gt, vt) {
-    local key, node, node1, nid;
-
-    if (~(key = ask ('give node name or label')))
-        return;
-    if (gt.graph.nodedict[key] >= 0)
-        node = gt.graph.nodes[gt.graph.nodedict[key]];
-    else if (gt.graph.nodedict[ston (key)] >= 0)
-        node = gt.graph.nodes[gt.graph.nodedict[ston (key)]];
-    else {
-        for (nid in gt.graph.nodes) {
-            node1 = gt.graph.nodes[nid];
-            if (node1.attr.label == key | node1.attr.label == ston (key)) {
-                node = node1;
-                break;
-            }
-        }
-    }
-    if (~node) {
-        dotty.message (0, concat ('cannot find node: ', key));
-        return;
-    }
-    gt.setviewcenter ([vt.vtid = vt;], node.pos);
-};
-dotty.protogt.setattr = function (gt, obj) {
-    local kv, t, attr, value;
-
-    if (~(kv = ask ('give attr/value, eg. color=blue')))
-        return;
-    t = split (kv, '=');
-    attr = t[0];
-    value = t[1];
-    if (obj.nid >= 0) {
-        gt.undrawnode (gt, gt.views, obj);
-        obj.attr[attr] = value;
-        gt.unpacknodeattr (gt, obj);
-        gt.drawnode (gt, gt.views, obj);
-    } else if (obj.eid >= 0) {
-        gt.undrawedge (gt, gt.views, obj);
-        obj.attr[attr] = value;
-        gt.unpackedgeattr (gt, obj);
-        gt.drawedge (gt, gt.views, obj);
-    }
-};
-dotty.protogt.getattr = function (gt, node) {
-    local kv;
-
-    if (~(kv.key = ask ('give attr name')))
-        return null;
-    if ((kv.val = node.attr[kv.key]))
-        return kv;
-    return null;
-};
-#
-# utilities
-#
-dotty.createviewandgraph = function (name, type, protogt, protovt) {
-    local vt, gt;
-
-    if (~protogt)
-        protogt = dotty.protogt;
-    if (protogt.creategraph)
-        gt = protogt.creategraph (protogt);
-    else
-        gt = dotty.protogt.creategraph (protogt);
-    vt = gt.createview (gt, protovt);
-    if (~protogt.graph)
-        protogt.graph = copy (dotty.protogt.graph);
-    if (name)
-        gt.loadgraph (gt, name, type, protogt.graph, 1);
-    return ['gt' = gt; 'vt' = vt;];
-};
-dotty.openio = function (name, type, mode) {
-    local fd;
-
-    if (~name)
-        return null;
-    if (type == 'file') {
-        if (name == '-') {
-            if (mode == 'r' | mode == 'r+')
-                fd = 0;
-            else
-                fd = 1;
-        } else if (~((fd = openio ('file', name, mode)) >= 0)) {
-            dotty.message (0, concat ('cannot open file: ', name));
-            return null;
-        }
-    } else if (type == 'pipe') {
-        if (~((fd = openio ('pipe', 'ksh', mode,
-                concat ("%e ", name))) >= 0)) {
-            dotty.message (0, concat ('cannot run command: ', name));
-            return null;
-        }
-    } else
-        return null;
-    return fd;
-};
-dotty.pushbusy = function (gt, views) {
-    local vid;
-
-    if (gt.busy == 0)
-        for (vid in gt.views)
-            setwidgetattr (vid, ['cursor' = 'watch';]);
-    gt.busy = gt.busy + 1;
-};
-dotty.popbusy = function (gt, views) {
-    local vid;
-
-    gt.busy = gt.busy - 1;
-    if (gt.busy == 0)
-        for (vid in gt.views)
-            setwidgetattr (vid, ['cursor' = 'default';]);
-};
-dotty.message = function (level, text) {
-    if (level <= dotty.mlevel)
-        echo ('dotty.lefty: ', text);
-};
-#
-# printing or saving to file
-#
-dotty.protogt.printorsave = function (gt, vt, otype, name, mode, ptype) {
-    local pr, wrect, vsize, xy, psize, canvas, pscanvas, cid, cname, t;
-    local graph, edgehandles, fontmap, eid, edge, nid, node, gid, sgraph;
-
-    if (~otype)
-        if (~(otype = ask ('print to', 'choice', 'file|printer')))
-            return;
-    if (otype == 'printer') {
-        name = '/tmp/dottyout.ps';
-        if (getenv ('LEFTYWINSYS') ~= 'mswin' & ~pr)
-            if (~(pr = ask ('printer command', 'string', 'lpr')))
-                return;
-    }
-    if (~name)
-        if (~(name = ask ('postscript file', 'file', 'out.ps')))
-            return;
-    if (~ptype)
-        if (~(ptype = ask ('page size', 'choice', '8.5x11|11x17|36x50')))
-            return;
-    if (~mode)
-        if (~(mode = ask ('mode', 'choice', 'portrait|landscape|best fit')))
-            return;
-    wrect = copy (vt.wrect);
-    wrect[0].x = wrect[0].x - 1;
-    wrect[1].x = wrect[1].x + 1;
-    wrect[0].y = wrect[0].y - 1;
-    wrect[1].y = wrect[1].y + 1;
-    vsize = copy (vt.vsize);
-    if (vsize.x == 0)
-        vsize.x = 1;
-    if (vsize.y == 0)
-        vsize.y = 1;
-    xy = vsize.x / vsize.y;
-    if (mode == 'best fit') {
-        if (xy < 1)
-            mode = 'portrait';
-        else
-            mode = 'landscape';
-    }
-    psize = dotty.pagesizes[ptype];
-    if (mode == 'portrait') {
-        if (xy < psize.x / psize.y) {
-            vsize.y = psize.y * 300;
-            vsize.x = vsize.y * xy;
-        } else {
-            vsize.x = psize.x * 300;
-            vsize.y = vsize.x / xy;
-        }
-    } else {
-        if (xy < psize.y / psize.x) {
-            vsize.y = psize.x * 300;
-            vsize.x = vsize.y * xy;
-        } else {
-            vsize.x = psize.y * 300;
-            vsize.y = vsize.x / xy;
-        }
-    }
-    pscanvas = createwidget (-1, [
-        'type'   = 'ps';
-        'origin' = ['x' = 0; 'y' = 0;];
-        'size'   = vsize;
-        'mode'   = mode;
-        'name'   = name;
-    ]);
-    for (cname in vt.colors) {
-        cid = vt.colors[cname];
-        if (setwidgetattr (pscanvas, ['color' = [cid = cname;];]) ~= 1) {
-            t = split (cname, ' ');
-            if (tablesize (t) ~= 3 |
-                    setwidgetattr (pscanvas, ['color' = [cid = [
-                        'h' = ston (t[0]); 's' = ston (t[1]); 'v' = ston (t[2]);
-                    ];];]) ~= 1) {
-                dotty.message (0, concat ('unknown color ',
-                        cname, ' using #1'));
-            }
-        }
-    }
-    setwidgetattr (pscanvas, ['window' = wrect;]);
-    graph = copy (gt.graph);
-    canvas = vt.canvas;
-    vt.canvas = pscanvas;
-    edgehandles = gt.edgehandles;
-    gt.edgehandles = 0;
-    fontmap = dotty.maps[getenv ('LEFTYWINSYS')].psfontmap;
-    for (eid in graph.edges) {
-        edge = graph.edges[eid];
-        edge.fontname = fontmap[edge.attr.fontname];
-        gt.drawedge (gt, [0 = vt;], edge);
-    }
-    for (nid in graph.nodes) {
-        node = graph.nodes[nid];
-        node.fontname = fontmap[node.attr.fontname];
-        gt.drawnode (gt, [0 = vt;], node);
-    }
-    for (gid in graph.graphs) {
-        sgraph = graph.graphs[gid];
-        sgraph.fontname = fontmap[sgraph.graphattr.fontname];
-        gt.drawsgraph (gt, [0 = vt;], sgraph);
-    }
-    gt.edgehandles = edgehandles;
-    vt.canvas = canvas;
-    destroywidget (pscanvas);
-    if (otype == 'printer' & getenv ('LEFTYWINSYS') ~= 'mswin')
-        system (concat (pr, ' /tmp/dottyout.ps; rm /tmp/dottyout.ps'));
-};
diff --git a/contrib/lefty-grace/dotty_draw.lefty b/contrib/lefty-grace/dotty_draw.lefty
deleted file mode 100644 (file)
index 1023eed..0000000
+++ /dev/null
@@ -1,779 +0,0 @@
-#
-# dotty_draw: drawing functions and data structures
-#
-dotty.protogt.drawgraph = function (gt, views) {
-    local gid, eid, nid, graph;
-
-    graph = gt.graph;
-    gt.drawsgraph (gt, views, graph);
-    for (gid in graph.graphs)
-        gt.drawsgraph (gt, views, graph.graphs[gid]);
-    for (eid in graph.edges)
-        gt.drawedge (gt, views, graph.edges[eid]);
-    for (nid in graph.nodes)
-        gt.drawnode (gt, views, graph.nodes[nid]);
-};
-dotty.protogt.redrawgraph = function (gt, views) {
-    local vid;
-
-    for (vid in views)
-        clear (views[vid].canvas);
-    gt.drawgraph (gt, views);
-};
-dotty.protogt.setviewsize = function (views, r) {
-    local vid, vt, w2v, scale, attr;
-
-    for (vid in views) {
-        vt = views[vid];
-        vt.wrect = copy (r);
-        if (r[1].x == 0 | r[1].y == 0) {
-            attr = getwidgetattr (vt.scroll, [0 = 'size';]);
-            vt.wrect[1] = copy (attr.size);
-        }
-        if (vt.type == 'birdseye') {
-            attr = getwidgetattr (vt.scroll, [0 = 'size';]);
-            scale.x = (vt.wrect[1].x - vt.wrect[0].x) / attr.size.x;
-            scale.y = (vt.wrect[1].y - vt.wrect[0].y) / attr.size.y;
-            if (scale.x > 1 & scale.x > scale.y)
-                vt.w2v = scale.x;
-            else if (scale.y > 1)
-                vt.w2v = scale.y;
-            else
-                vt.w2v = 1;
-        }
-        w2v = vt.w2v;
-        vt.vsize = [
-            'x' = (vt.wrect[1].x - vt.wrect[0].x) / w2v;
-            'y' = (vt.wrect[1].y - vt.wrect[0].y) / w2v;
-        ];
-        setwidgetattr (vt.canvas, [
-            'window' = vt.wrect;
-            'viewport' = vt.vsize;
-        ]);
-    }
-};
-dotty.protogt.setviewscale = function (views, factor) {
-    local vid, vt, w2v;
-
-    for (vid in views) {
-        vt = views[vid];
-        if ((w2v = vt.w2v * factor) < 0.01) {
-            dotty.message (0, 'cannot zoom any closer');
-            return;
-        }
-        vt.w2v = w2v;
-        vt.vsize = [
-            'x' = (vt.wrect[1].x - vt.wrect[0].x) / w2v;
-            'y' = (vt.wrect[1].y - vt.wrect[0].y) / w2v;
-        ];
-        setwidgetattr (vt.canvas, ['viewport' = vt.vsize;]);
-    }
-};
-dotty.protogt.setviewcenter = function (views, center) {
-    local vid, vt, pos;
-
-    for (vid in views) {
-        vt = views[vid];
-        pos = [
-            'x' = center.x * vt.vsize.x / (vt.wrect[1].x - vt.wrect[0].x);
-            'y' = (vt.wrect[1].y - center.y) * vt.vsize.y /
-                (vt.wrect[1].y - vt.wrect[0].y);
-        ];
-        setwidgetattr (vt.scroll, ['childcenter' = pos;]);
-    }
-};
-dotty.protogt.foldsgraph = function (gt, views, sgraph) {
-    local nid;
-
-#   gt.undrawsgraph(gt, views, sgraph);
-    for (nid in sgraph.nodes)
-        gt.undrawnode (gt, views, graph.nodes[nid]);
-};
-
-dotty.protogt.drawsgraph = function (gt, views, sgraph) {
-    local vid, canvas, pos;
-
-    sgraph.draw = 1;
-    if (~sgraph.rect[0] | sgraph.graphattr.style == 'invis')
-        return;
-    for (vid in views) {
-        canvas = views[vid].canvas;
-        if (~sgraph.type) # 'type' only exists on top level
-            box (canvas, null, sgraph.rect, ['color' = sgraph.color;]);
-        if (sgraph.graphattr.label) {
-            if (sgraph.lp.x >= 0) {
-                pos = sgraph.lp;
-                text (canvas, null, pos, sgraph.graphattr.label,
-                        sgraph.fontname, sgraph.fontsize, 'cc',
-                        ['color' = sgraph.fontcolor;]);
-            } else {
-                pos = ['x' = sgraph.rect[0].x; 'y' = sgraph.rect[1].y;];
-                text (canvas, null, pos, sgraph.graphattr.label,
-                        sgraph.fontname, sgraph.fontsize, 'ld',
-                        ['color' = sgraph.fontcolor;]);
-            }
-        }
-    }
-};
-dotty.protogt.undrawsgraph = function (gt, views, sgraph) {
-    local vid, canvas, pos;
-
-    if (~sgraph.drawn)
-        return;
-    sgraph.drawn = 0;
-    if (~sgraph.rect[0] | sgraph.graphattr.style == 'invis')
-        return;
-    for (vid in views) {
-        canvas = views[vid].canvas;
-        if (~sgraph.type) # 'type' only exists on top level
-            box (canvas, null, sgraph.rect, ['color' = 0;]);
-        if (sgraph.graphattr.label) {
-            if (sgraph.lp.x >= 0) {
-                pos = sgraph.lp;
-                text (canvas, null, pos, sgraph.graphattr.label,
-                        sgraph.fontname, sgraph.fontsize, 'cc',
-                        ['color' = 0;]);
-            } else {
-                pos = ['x' = sgraph.rect[0].x; 'y' = sgraph.rect[1].y;];
-                text (canvas, null, pos, sgraph.graphattr.label,
-                        sgraph.fontname, sgraph.fontsize, 'ld',
-                        ['color' = 0;]);
-            }
-        }
-        clearpick (canvas, sgraph);
-    }
-};
-dotty.protogt.drawnode = function (gt, views, node) {
-    local vid, func, pos, size, rect;
-
-    node.drawn = 1;
-    if (~node.pos)
-        return;
-    if (node.attr.style == 'invis') {
-        pos = node.pos;
-        size = node.size;
-        rect[0] = ['x' = pos.x - size.x / 2; 'y' = pos.y - size.y / 2;];
-        rect[1] = ['x' = rect[0].x + size.x; 'y' = rect[0].y + size.y;];
-        for (vid in views)
-            setpick (views[vid].canvas, node, rect);
-        return;
-    }
-
-    if ( node.attr.folded == '1' )
-        func = gt.shapefunc['Msquare'];
-    else if (~(func = gt.shapefunc[node.attr.shape]))
-        func = gt.shapefunc['box'];
-    for (vid in views)
-        func (gt, views[vid].canvas, node);
-};
-dotty.protogt.undrawnode = function (gt, views, node) {
-    local vid, func, pos, size, rect, color, fontcolor, outlinecolor;
-
-    if (~node.drawn)
-        return;
-    node.drawn = 0;
-    if (~node.pos)
-        return;
-    if (node.attr.style == 'invis') {
-        pos = node.pos;
-        size = node.size;
-        rect[0] = ['x' = pos.x - size.x / 2; 'y' = pos.y - size.y / 2;];
-        rect[1] = ['x' = rect[0].x + size.x; 'y' = rect[0].y + size.y;];
-        for (vid in views)
-            clearpick (views[vid].canvas, node);
-        return;
-    }
-    color = node.color;
-    node.color = 0;
-    fontcolor = node.fontcolor;
-    node.fontcolor = 0;
-    outlinecolor = dotty.outlinecolor;
-    dotty.outlinecolor = 0;
-    if ( node.attr.folded == '1' )
-        func = gt.shapefunc['Msquare'];
-    else if (~(func = gt.shapefunc[node.attr.shape]))
-        func = gt.shapefunc['box'];
-    for (vid in views) {
-        func (gt, views[vid].canvas, node);
-        clearpick (views[vid].canvas, node);
-    }
-    node.color = color;
-    node.fontcolor = fontcolor;
-    dotty.outlinecolor = outlinecolor;
-};
-dotty.protogt.shapefunc.record = function (gt, canvas, node) {
-    local rect, pos, size;
-
-    pos = node.pos;
-    size = node.size;
-    rect[0] = ['x' = pos.x - size.x / 2; 'y' = pos.y - size.y / 2;];
-    rect[1] = ['x' = rect[0].x + size.x; 'y' = rect[0].y + size.y;];
-    if (node.attr.style == 'filled') {
-        box (canvas, node, rect, ['color' = node.color; 'fill' = 'on';]);
-        box (canvas, node, rect, ['color' = dotty.outlinecolor;]);
-    }
-    gt.shapefunc.rfields (gt, canvas, node, node.fields);
-    setpick (canvas, node, rect);
-};
-dotty.protogt.shapefunc.rfields = function (gt, canvas, node, fields) {
-    local fid, field, pos, label;
-
-    for (fid in fields) {
-        field = fields[fid];
-        if (field.fields)
-            gt.shapefunc.rfields (gt, canvas, node, field.fields);
-        else {
-            if (node.attr.style == 'filled')
-                box (canvas, null, field.rect, ['color' = dotty.outlinecolor;]);
-            else
-                box (canvas, null, field.rect, ['color' = node.color;]);
-            pos.x = (field.rect[1].x + field.rect[0].x) / 2;
-            pos.y = (field.rect[1].y + field.rect[0].y) / 2;
-            if (~(label = field.text) | label == '\N')
-                label = node.name;
-            text (canvas, null, pos, label, node.fontname, node.fontsize,
-                    'cc', ['color' = node.fontcolor;]);
-        }
-    }
-};
-dotty.protogt.shapefunc.plaintext = function (gt, canvas, node) {
-    local pos, size, label, rect;
-
-    pos = node.pos;
-    size = node.size;
-    if (~(label = node.attr.label) | label == '\N')
-        label = node.name;
-    rect[0] = ['x' = pos.x - size.x / 2; 'y' = pos.y - size.y / 2;];
-    rect[1] = ['x' = rect[0].x + size.x; 'y' = rect[0].y + size.y;];
-    setpick (canvas, node, rect);
-    text (canvas, null, pos, label, node.fontname, node.fontsize,
-            'cc', ['color' = node.fontcolor;]);
-};
-dotty.protogt.shapefunc.box = function (gt, canvas, node) {
-    local pos, size, label, rect;
-
-    pos = node.pos;
-    size = node.size;
-    if (~(label = node.attr.label) | label == '\N')
-        label = node.name;
-    rect[0] = ['x' = pos.x - size.x / 2; 'y' = pos.y - size.y / 2;];
-    rect[1] = ['x' = rect[0].x + size.x; 'y' = rect[0].y + size.y;];
-    if (node.attr.style == 'filled') {
-        box (canvas, node, rect, ['color' = node.color; 'fill' = 'on';]);
-        box (canvas, node, rect, ['color' = dotty.outlinecolor;]);
-    } else
-        box (canvas, node, rect, ['color' = node.color;]);
-    text (canvas, null, pos, label, node.fontname, node.fontsize,
-            'cc', ['color' = node.fontcolor;]);
-};
-dotty.protogt.shapefunc.Msquare = function (gt, canvas, node) {
-    local pos, size, label, rect, color;
-
-    pos = node.pos;
-    size = node.size;
-    if (~(label = node.attr.label) | label == '\N')
-        label = node.name;
-    rect[0] = ['x' = pos.x - size.x / 2; 'y' = pos.y - size.y / 2;];
-    rect[1] = ['x' = rect[0].x + size.x; 'y' = rect[0].y + size.y;];
-    if (node.attr.style == 'filled') {
-        box (canvas, node, rect, ['color' = node.color; 'fill' = 'on';]);
-        color = dotty.outlinecolor;
-        box (canvas, node, rect, ['color' = color;]);
-        line (canvas, null, ['x' = rect[0].x; 'y' = rect[0].y + 10;],
-                ['x' = rect[0].x + 10; 'y' = rect[0].y;], ['color' = color;]);
-        line (canvas, null, ['x' = rect[0].x; 'y' = rect[1].y - 10;],
-                ['x' = rect[0].x + 10; 'y' = rect[1].y;], ['color' = color;]);
-        line (canvas, null, ['x' = rect[1].x; 'y' = rect[0].y + 10;],
-                ['x' = rect[1].x - 10; 'y' = rect[0].y;], ['color' = color;]);
-        line (canvas, null, ['x' = rect[1].x; 'y' = rect[1].y - 10;],
-                ['x' = rect[1].x - 10; 'y' = rect[1].y;], ['color' = color;]);
-    } else {
-        color = node.color;
-        box (canvas, node, rect, ['color' = color;]);
-        line (canvas, null, ['x' = rect[0].x; 'y' = rect[0].y + 10;],
-                ['x' = rect[0].x + 10; 'y' = rect[0].y;], ['color' = color;]);
-        line (canvas, null, ['x' = rect[0].x; 'y' = rect[1].y - 10;],
-                ['x' = rect[0].x + 10; 'y' = rect[1].y;], ['color' = color;]);
-        line (canvas, null, ['x' = rect[1].x; 'y' = rect[0].y + 10;],
-                ['x' = rect[1].x - 10; 'y' = rect[0].y;], ['color' = color;]);
-        line (canvas, null, ['x' = rect[1].x; 'y' = rect[1].y - 10;],
-                ['x' = rect[1].x - 10; 'y' = rect[1].y;], ['color' = color;]);
-    }
-    text (canvas, null, pos, label, node.fontname, node.fontsize,
-            'cc', ['color' = node.fontcolor;]);
-};
-dotty.protogt.shapefunc.ellipse = function (gt, canvas, node) {
-    local pos, size, label;
-
-    pos = node.pos;
-    size.x = node.size.x / 2;
-    size.y = node.size.y / 2;
-    if (~(label = node.attr.label) | label == '\N')
-        label = node.name;
-    if (node.attr.style == 'filled') {
-        if (node.attr.shape == 'doublecircle') {
-            arc (canvas, node, pos, size, ['color' = dotty.outlinecolor;]);
-            size.x = size.x - 4;
-            size.y = size.y - 4;
-        }
-        arc (canvas, node, pos, size, ['color' = node.color; 'fill' = 'on';]);
-        arc (canvas, node, pos, size, ['color' = dotty.outlinecolor;]);
-    } else {
-        if (node.attr.shape == 'doublecircle') {
-            arc (canvas, node, pos, size, ['color' = node.color;]);
-            size.x = size.x - 4;
-            size.y = size.y - 4;
-        }
-        arc (canvas, node, pos, size, ['color' = node.color;]);
-    }
-    text (canvas, null, pos, label, node.fontname, node.fontsize,
-            'cc', ['color' = node.fontcolor;]);
-};
-dotty.protogt.shapefunc.circle = dotty.protogt.shapefunc.ellipse;
-dotty.protogt.shapefunc.doublecircle = dotty.protogt.shapefunc.ellipse;
-dotty.protogt.shapefunc.diamond = function (gt, canvas, node) {
-    local pos, size, label, p, rect;
-
-    pos = node.pos;
-    size = node.size;
-    if (~(label = node.attr.label) | label == '\N')
-        label = node.name;
-    p[0] = ['x' = pos.x; 'y' = pos.y + size.y / 2;];
-    p[1] = ['x' = pos.x + size.x / 2; 'y' = pos.y;];
-    p[2] = ['x' = pos.x; 'y' = pos.y - size.y / 2;];
-    p[3] = ['x' = pos.x - size.x / 2; 'y' = pos.y;];
-    p[4] = p[0];
-    rect[0] = ['x' = pos.x - size.x / 2; 'y' = pos.y - size.y / 2;];
-    rect[1] = ['x' = rect[0].x + size.x; 'y' = rect[0].y + size.y;];
-    if (node.attr.style == 'filled') {
-        polygon (canvas, node, p, ['color' = node.color; 'fill' = 'on';]);
-        polygon (canvas, node, p, ['color' = dotty.outlinecolor;]);
-    } else
-        polygon (canvas, node, p, ['color' = node.color;]);
-    setpick (canvas, node, rect);
-    text (canvas, null, pos, label, node.fontname, node.fontsize,
-            'cc', ['color' = node.fontcolor;]);
-};
-dotty.protogt.shapefunc.parallelogram = function (gt, canvas, node) {
-    local pos, size, label, rect, color, dx, p;
-
-    pos = node.pos;
-    size = node.size;
-    if (~(label = node.attr.label) | label == '\N')
-        label = node.name;
-    rect[0] = ['x' = pos.x - size.x / 2; 'y' = pos.y - size.y / 2;];
-    rect[1] = ['x' = rect[0].x + size.x; 'y' = rect[0].y + size.y;];
-    dx = (rect[1].x - rect[0].x) / 5;
-    p[0] = ['x' = rect[0].x; 'y' = rect[0].y;];
-    p[1] = ['x' = rect[1].x - dx; 'y' = rect[0].y;];
-    p[2] = ['x' = rect[1].x; 'y' = rect[1].y;];
-    p[3] = ['x' = rect[0].x + dx; 'y' = rect[1].y;];
-    p[4] = ['x' = rect[0].x; 'y' = rect[0].y;];
-    if (node.attr.style == 'filled') {
-        polygon (canvas, node, p, ['color' = node.color; 'fill' = 'on';]);
-        polygon (canvas, node, p, ['color' = dotty.outlinecolor;]);
-    } else
-        polygon (canvas, node, p, ['color' = node.color;]);
-    setpick (canvas, node, rect);
-    text (canvas, null, pos, label, node.fontname, node.fontsize,
-            'cc', ['color' = node.fontcolor;]);
-};
-dotty.protogt.shapefunc.trapezium = function (gt, canvas, node) {
-    local pos, size, label, rect, color, dx, p;
-
-    pos = node.pos;
-    size = node.size;
-    if (~(label = node.attr.label) | label == '\N')
-        label = node.name;
-    rect[0] = ['x' = pos.x - size.x / 2; 'y' = pos.y - size.y / 2;];
-    rect[1] = ['x' = rect[0].x + size.x; 'y' = rect[0].y + size.y;];
-    dx = (rect[1].x - rect[0].x) / 5;
-    p[0] = ['x' = rect[0].x; 'y' = rect[0].y;];
-    p[1] = ['x' = rect[1].x; 'y' = rect[0].y;];
-    p[2] = ['x' = rect[1].x - dx; 'y' = rect[1].y;];
-    p[3] = ['x' = rect[0].x + dx; 'y' = rect[1].y;];
-    p[4] = ['x' = rect[0].x; 'y' = rect[0].y;];
-    if (node.attr.style == 'filled') {
-        polygon (canvas, node, p, ['color' = node.color; 'fill' = 'on';]);
-        polygon (canvas, node, p, ['color' = dotty.outlinecolor;]);
-    } else
-        polygon (canvas, node, p, ['color' = node.color;]);
-    setpick (canvas, node, rect);
-    text (canvas, null, pos, label, node.fontname, node.fontsize,
-            'cc', ['color' = node.fontcolor;]);
-};
-dotty.protogt.shapefunc.triangle = function (gt, canvas, node) {
-    local pos, size, label, rect, color, dx, dy, p;
-
-    pos = node.pos;
-    size = node.size;
-    if (~(label = node.attr.label) | label == '\N')
-        label = node.name;
-    rect[0] = ['x' = pos.x - size.x / 2; 'y' = pos.y - size.y / 2;];
-    rect[1] = ['x' = rect[0].x + size.x; 'y' = rect[0].y + size.y;];
-    if (node.attr.orientation ~= -90) {
-        dx = size.x / 2;
-        dy = size.y / 4;
-        p[0] = ['x' = pos.x - dx; 'y' = pos.y - dy;];
-        p[1] = ['x' = pos.x + dx; 'y' = pos.y - dy;];
-        p[2] = ['x' = pos.x;      'y' = rect[1].y;];
-        p[3] = ['x' = pos.x - dx; 'y' = pos.y - dy;];
-    } else {
-        dx = size.x / 4;
-        dy = size.y / 2;
-        p[0] = ['x' = pos.x - dx; 'y' = pos.y - dy;];
-        p[1] = ['x' = pos.x - dx; 'y' = pos.y + dy;];
-        p[2] = ['x' = pos.x + dx * 2; 'y' = pos.y;];
-        p[3] = ['x' = pos.x - dx; 'y' = pos.y - dy;];
-    }
-    if (node.attr.style == 'filled') {
-        polygon (canvas, node, p, ['color' = node.color; 'fill' = 'on';]);
-        polygon (canvas, node, p, ['color' = dotty.outlinecolor;]);
-    } else
-        polygon (canvas, node, p, ['color' = node.color;]);
-    setpick (canvas, node, rect);
-    text (canvas, null, pos, label, node.fontname, node.fontsize,
-            'cc', ['color' = node.fontcolor;]);
-};
-dotty.protogt.movenode = function (gt, node, pos) {
-    local ppos, eid, edge, p, fp, lp;
-
-    ppos = copy (node.pos);
-    gt.undrawnode (gt, gt.views, node);
-    node.pos.x = pos.x;
-    node.pos.y = pos.y;
-    if (node.attr.shape == 'record')
-        gt.moverecordfields (gt, node.fields, pos.x - ppos.x, pos.y - ppos.y);
-    for (eid in node.edges) {
-        edge = node.edges[eid];
-        if (~edge.dir & edge.head ~= edge.tail) {
-            p = edge.tail.pos;
-            fp = edge.points[0];
-            lp = edge.points[tablesize (edge.points)  - 1];
-            if (((p.x - fp.x) * (p.x - fp.x) + (p.y - fp.y) * (p.y - fp.y)) <
-                    ((p.x - lp.x) * (p.x - lp.x) + (p.y - lp.y) * (p.y - lp.y)))
-                edge.dir = 1;
-            else
-                edge.dir = -1;
-        }
-        gt.moveedge (gt, edge, node, ppos, pos);
-    }
-    gt.drawnode (gt, gt.views, node);
-};
-dotty.protogt.moverecordfields = function (gt, fields, dx, dy) {
-    local fid, field;
-
-    for (fid in fields) {
-        field = fields[fid];
-        if (field.fields)
-            gt.moverecordfields (gt, field.fields, dx, dy);
-        else {
-            field.rect[0].x = field.rect[0].x + dx;
-            field.rect[0].y = field.rect[0].y + dy;
-            field.rect[1].x = field.rect[1].x + dx;
-            field.rect[1].y = field.rect[1].y + dy;
-        }
-    }
-};
-dotty.protogt.drawedge = function (gt, views, edge) {
-    local vid, canvas;
-
-    edge.drawn = 1;
-    if (~edge.points)
-        return;
-    if (edge.attr.style == 'invis') {
-        if (gt.edgehandles == 0)
-            return;
-        for (vid in views) {
-            arc (views[vid].canvas, edge, [
-                'x' = (edge.points[1].x + edge.points[2].x) / 2;
-                'y' = (edge.points[1].y + edge.points[2].y) / 2;
-            ], ['x' = 5; 'y' = 5;], ['color' = 1;]);
-        }
-        return;
-    }
-    for (vid in views) {
-        canvas = views[vid].canvas;
-        if (edge.attr.style == 'bold')
-            setgfxattr (canvas, ['width' = 3;]);
-        splinegon (canvas, null, edge.points,
-                ['color' = edge.color; 'style' = edge.attr.style;]);
-        if (edge.sp)
-            arrow (canvas, null, edge.points[0],
-                    edge.sp, ['color' = edge.color;]);
-        if (edge.ep)
-            arrow (canvas, null, edge.points[tablesize (edge.points) - 1],
-                    edge.ep, ['color' = edge.color;]);
-        if (edge.attr.style == 'bold')
-            setgfxattr (canvas, ['width' = 0;]);
-        if (edge.lp)
-            text (canvas, null, edge.lp, edge.attr.label, edge.fontname,
-                    edge.fontsize, 'cc', ['color' = edge.fontcolor;]);
-        if (gt.edgehandles == 0)
-            continue;
-        arc (canvas, edge, [
-            'x' = (edge.points[1].x + edge.points[2].x) / 2;
-            'y' = (edge.points[1].y + edge.points[2].y) / 2;
-        ], ['x' = 5; 'y' = 5;], ['color' = 1;]);
-    }
-};
-dotty.protogt.undrawedge = function (gt, views, edge) {
-    local vid, canvas;
-
-    if (~edge.drawn)
-        return;
-    edge.drawn = 0;
-    if (~edge.points)
-        return;
-    if (edge.attr.style == 'invis') {
-        if (gt.edgehandles == 0)
-            return;
-        for (vid in views) {
-            arc (views[vid].canvas, edge, [
-                'x' = (edge.points[1].x + edge.points[2].x) / 2;
-                'y' = (edge.points[1].y + edge.points[2].y) / 2;
-            ], ['x' = 5; 'y' = 5;], ['color' = 0;]);
-            clearpick (views[vid].canvas, edge);
-        }
-        return;
-    }
-    for (vid in views) {
-        canvas = views[vid].canvas;
-        if (edge.attr.style == 'bold')
-            setgfxattr (canvas, ['width' = 3;]);
-        splinegon (canvas, null, edge.points, ['color' = 0;]);
-        if (edge.sp)
-            arrow (canvas, null, edge.points[0],
-                    edge.sp, ['color' = 0;]);
-        if (edge.ep)
-            arrow (canvas, null, edge.points[tablesize (edge.points) - 1],
-                    edge.ep, ['color' = 0;]);
-        if (edge.attr.style == 'bold')
-            setgfxattr (canvas, ['width' = 0;]);
-        if (edge.lp)
-            text (canvas, null, edge.lp, edge.attr.label, edge.fontname,
-                    edge.fontsize, 'cc', ['color' = 0;]);
-        if (gt.edgehandles == 0)
-            continue;
-        arc (canvas, edge, [
-            'x' = (edge.points[1].x + edge.points[2].x) / 2;
-            'y' = (edge.points[1].y + edge.points[2].y) / 2;
-        ], ['x' = 5; 'y' = 5;], ['color' = 0;]);
-        clearpick (canvas, edge);
-    }
-};
-dotty.protogt.moveedge = function (gt, edge, node, pb, pc) {
-    local dx, dy, tp, hp, pid, p, pa, da, lab, lac, s, ce, se, n, x, y, dir;
-
-    gt.undrawedge (gt, gt.views, edge);
-    dx = pc.x - pb.x; dy = pc.y - pb.y;
-    tp = edge.sp;
-    hp = edge.ep;
-    if (edge.tail == node) {
-        if (edge.head == node) {
-            for (pid in edge.points) {
-                p = edge.points[pid];
-                p.x = p.x + dx; p.y = p.y + dy;
-            }
-            if (tp) {
-                tp.x = tp.x + dx; tp.y = tp.y + dy;
-            }
-            if (hp) {
-                hp.x = hp.x + dx; hp.y = hp.y + dy;
-            }
-            if (edge.lp) {
-                edge.lp.x = edge.lp.x + dx;
-                edge.lp.y = edge.lp.y + dy;
-            }
-            gt.drawedge (gt, gt.views, edge);
-            return;
-        }
-        pa = edge.head.pos;
-        dir = 1;
-    } else {
-        pa = edge.tail.pos;
-        dir = -1;
-    }
-    dir = edge.dir * dir;
-    da = atan (pc.y - pa.y, pc.x - pa.x) - atan (pb.y - pa.y, pb.x - pa.x);
-    lab = sqrt ((pb.y - pa.y) * (pb.y - pa.y) +
-            (pb.x - pa.x) * (pb.x - pa.x));
-    lac = sqrt ((pc.y - pa.y) * (pc.y - pa.y) +
-            (pc.x - pa.x) * (pc.x - pa.x));
-    s = lac / lab;
-    ce = cos (da);
-    se = sin (da);
-    n = tablesize (edge.points);
-    for (pid = 1; pid < n - 1; pid = pid + 1) {
-        p = edge.points[pid];
-        x = p.x - pa.x;
-        y = p.y - pa.y;
-        p.x = pa.x + (ce * x - se * y) * s;
-        p.y = pa.y + (se * x + ce * y) * s;
-    }
-    if (dir == 1) {
-        p = edge.points[0];
-        p.x = p.x + dx; p.y = p.y + dy;
-        if (tp) {
-            tp.x = tp.x + dx; tp.y = tp.y + dy;
-        }
-    } else {
-        p = edge.points[n - 1];
-        p.x = p.x + dx; p.y = p.y + dy;
-        if (hp) {
-            hp.x = hp.x + dx; hp.y = hp.y + dy;
-        }
-    }
-    if (edge.lp) {
-        x = edge.lp.x - pa.x;
-        y = edge.lp.y - pa.y;
-        edge.lp.x = pa.x + (ce * x - se * y) * s;
-        edge.lp.y = pa.y + (se * x + ce * y) * s;
-    }
-    gt.drawedge (gt, gt.views, edge);
-};
-dotty.protogt.getcolor = function (views, name) {
-    local vid, vt, color, t;
-
-    for (vid in views) {
-        vt = views[vid];
-        if (~(color >= 0)) {
-            if (~(vt.colors[name] >= 0))
-                color = (vt.colors[name] = vt.colorn);
-            else {
-                color = vt.colors[name];
-                break;
-            }
-        } else if (~(vt.colors[name] >= 0))
-            vt.colors[name] = color;
-        else if (vt.colors[name] ~= color)
-            dotty.message (0, concat ('inconsistent color ids for ', name));
-        if (setwidgetattr (vt.canvas, ['color' = [color = name;];]) ~= 1) {
-            t = split (name, ' ');
-            if (tablesize (t) ~= 3 |
-                    setwidgetattr (vt.canvas, ['color' = [color = [
-                        'h' = ston (t[0]); 's' = ston (t[1]); 'v' = ston (t[2]);
-                    ];];]) ~= 1) {
-                dotty.message (0, concat ('unknown color ', name, ' using #1'));
-                return 1;
-            }
-        }
-        vt.colorn = color + 1;
-    }
-    return color;
-};
-dotty.protogt.unpacksgraphattr = function (gt, sgraph) {
-    local attr;
-
-    attr = sgraph.graphattr;
-    if (dotty.fontmap[attr.fontname])
-        sgraph[dotty.keys.fname] = dotty.fontmap[attr.fontname];
-    else
-        sgraph[dotty.keys.fname] = attr.fontname;
-    sgraph[dotty.keys.fsize] = ston (attr.fontsize);
-    sgraph[dotty.keys.fcolor] = gt.getcolor (gt.views, attr.fontcolor);
-    if (attr.style == 'filled' & attr.color == 'black')
-        sgraph[dotty.keys.color] = gt.getcolor (gt.views, 'grey');
-    else
-        sgraph[dotty.keys.color] = gt.getcolor (gt.views, attr.color);
-};
-dotty.protogt.unpacknodeattr = function (gt, node) {
-    local attr;
-
-    attr = node.attr;
-    if (dotty.fontmap[attr.fontname])
-        node[dotty.keys.fname] = dotty.fontmap[attr.fontname];
-    else
-        node[dotty.keys.fname] = attr.fontname;
-    node[dotty.keys.fsize] = ston (attr.fontsize);
-    node[dotty.keys.fcolor] = gt.getcolor (gt.views, attr.fontcolor);
-    if (attr.style == 'filled' & attr.color == 'black')
-        node[dotty.keys.color] = gt.getcolor (gt.views, 'grey');
-    else
-        node[dotty.keys.color] = gt.getcolor (gt.views, attr.color);
-};
-dotty.protogt.unpackedgeattr = function (gt, edge) {
-    local attr, n;
-
-    attr = edge.attr;
-    if (dotty.fontmap[attr.fontname])
-        edge[dotty.keys.fname] = dotty.fontmap[attr.fontname];
-    else
-        edge[dotty.keys.fname] = attr.fontname;
-    edge[dotty.keys.fsize] = ston (attr.fontsize);
-    edge[dotty.keys.fcolor] = gt.getcolor (gt.views, attr.fontcolor);
-    if (attr.style == 'filled' & attr.color == 'black')
-        edge[dotty.keys.color] = gt.getcolor (gt.views, 'grey');
-    else
-        edge[dotty.keys.color] = gt.getcolor (gt.views, attr.color);
-    if (attr.label & attr.label ~= '' & ~edge.lp & edge.points) {
-        if ((n = tablesize (edge.points)) > 4)
-            edge.lp = [
-                'x' = edge.points[toint (n / 2)].x + 5;
-                'y' = edge.points[toint (n / 2)].y + 5;
-            ];
-        else
-            edge.lp = [
-                'x' = (edge.points[1].x + edge.points[2].x) / 2 + 5;
-                'y' = (edge.points[1].y + edge.points[2].y) / 2 + 5;
-            ];
-    }
-};
-dotty.protogt.unpackattr = function (gt) {
-    local gid, sgraph, nid, node, eid, edge, graph, attr;
-
-    graph = gt.graph;
-    attr = graph.graphattr;
-    if (dotty.fontmap[attr.fontname])
-        graph[dotty.keys.fname] = dotty.fontmap[attr.fontname];
-    else
-        graph[dotty.keys.fname] = attr.fontname;
-    graph[dotty.keys.fsize] = ston (attr.fontsize);
-    graph[dotty.keys.fcolor] = gt.getcolor (gt.views, attr.fontcolor);
-    if (attr.style == 'filled' & attr.color == 'black')
-        graph[dotty.keys.color] = gt.getcolor (gt.views, 'grey');
-    else
-        graph[dotty.keys.color] = gt.getcolor (gt.views, attr.color);
-
-#  Does this work?  Want to save/restore parent filename in graph specification
-    graph[dotty.keys.parent] = attr.parent;
-    for (gid in graph.graphdict) {
-        sgraph = graph.graphs[graph.graphdict[gid]];
-        attr = sgraph.graphattr;
-        if (dotty.fontmap[attr.fontname])
-            sgraph[dotty.keys.fname] = dotty.fontmap[attr.fontname];
-        else
-            sgraph[dotty.keys.fname] = attr.fontname;
-        sgraph[dotty.keys.fsize] = ston (attr.fontsize);
-        sgraph[dotty.keys.fcolor] = gt.getcolor (gt.views, attr.fontcolor);
-        if (attr.style == 'filled' & attr.color == 'black')
-            sgraph[dotty.keys.color] = gt.getcolor (gt.views, 'grey');
-        else
-            sgraph[dotty.keys.color] = gt.getcolor (gt.views, attr.color);
-    }
-    for (nid in graph.nodedict) {
-        node = graph.nodes[graph.nodedict[nid]];
-        attr = node.attr;
-        if (dotty.fontmap[attr.fontname])
-            node[dotty.keys.fname] = dotty.fontmap[attr.fontname];
-        else
-            node[dotty.keys.fname] = attr.fontname;
-        node[dotty.keys.fsize] = ston (attr.fontsize);
-        node[dotty.keys.fcolor] = gt.getcolor (gt.views, attr.fontcolor);
-        if (attr.style == 'filled' & attr.color == 'black')
-            node[dotty.keys.color] = gt.getcolor (gt.views, 'grey');
-        else
-            node[dotty.keys.color] = gt.getcolor (gt.views, attr.color);
-    }
-    for (eid in graph.edges) {
-        edge = graph.edges[eid];
-        attr = edge.attr;
-        if (dotty.fontmap[attr.fontname])
-            edge[dotty.keys.fname] = dotty.fontmap[attr.fontname];
-        else
-            edge[dotty.keys.fname] = attr.fontname;
-        edge[dotty.keys.fsize] = ston (attr.fontsize);
-        edge[dotty.keys.fcolor] = gt.getcolor (gt.views, attr.fontcolor);
-        edge[dotty.keys.color] = gt.getcolor (gt.views, attr.color);
-    }
-};
diff --git a/contrib/lefty-grace/dotty_edit.lefty b/contrib/lefty-grace/dotty_edit.lefty
deleted file mode 100644 (file)
index ed75015..0000000
+++ /dev/null
@@ -1,705 +0,0 @@
-#
-# dotty_edit: editing functions and data structures
-#
-dotty.protogt.getnodesbyattr = function (gt, key, val) {
-    local nid, node, nlist;
-
-    nlist = [];
-    for (nid in gt.graph.nodes) {
-        node = gt.graph.nodes[nid];
-        if (node.attr[key] == val)
-            nlist[nid] = node;
-    }
-    return nlist;
-};
-dotty.protogt.reachablenodes = function (gt, node) {
-    local nlist, stack, eid, edge, i;
-
-    stack[0] = node;
-    i = 1;
-    while (i > 0) {
-        node = stack[i - 1];
-        i = i - 1;
-        nlist[node.nid] = node;
-        for (eid in node.edges) {
-            edge = node.edges[eid];
-            if (~nlist[edge.head.nid]) {
-                nlist[edge.head.nid] = edge.head;
-                stack[i] = edge.head;
-                i = i + 1;
-            }
-        }
-    }
-    return nlist;
-};
-dotty.protogt.mergegraph = function (gt, graph, show) {
-    local nameid, onode, pos, size, eid, eid2, tnode, hnode, oedge;
-
-    if (~gt.noundo)
-        gt.startadd2undo (gt);
-    for (nameid in graph.nodedict) {
-        pos = null;
-        size = null;
-        onode = graph.nodes[graph.nodedict[nameid]];
-        if (onode.pos)
-            pos = node.pos;
-        if (onode.size)
-            size = node.size;
-        if (~(gt.graph.nodedict[nameid] >= 0)) {
-            pos = null;
-            size = null;
-            if (onode.pos)
-                pos = node.pos;
-            if (onode.size)
-                size = node.size;
-            gt.insertnode (gt, pos, size, nameid, onode.attr, show);
-        }
-    }
-    for (eid in graph.edges) {
-        oedge = graph.edges[eid];
-        tnode = gt.graph.nodes[gt.graph.nodedict[oedge.tail.name]];
-        hnode = gt.graph.nodes[gt.graph.nodedict[oedge.head.name]];
-        for (eid2 in tnode.edges)
-            if (tnode.edges[eid2].tail == tnode &
-                    tnode.edges[eid2].head == hnode) {
-                oedge = null;
-                break;
-            }
-        if (oedge)
-            gt.insertedge (gt, tnode, null, hnode, null, oedge.attr, show);
-    }
-    if (~gt.noundo)
-        gt.endadd2undo (gt);
-};
-dotty.protogt.insertsgraph = function (gt, name, attr, show) {
-    local gid, sgraph, aid;
-
-    if (~gt)
-        return null;
-    gid = gt.graph.maxgid;
-    if (~name) {
-        while (gt.graph.graphdict[(name = concat ('g', gid))] >= 0)
-            gid = gid + 1;
-    } else if (gt.graph.graphdict[name]) {
-        dotty.message (0, concat ('graph: ', name, ' exists'));
-        return null;
-    }
-    gt.graph.graphdict[name] = gid;
-    gt.graph.maxgid = gid + 1;
-    gt.graph.graphs[gid] = [
-        dotty.keys.gid   = gid;
-        dotty.keys.name  = name;
-        dotty.keys.gattr = copy (gt.graph.graphattr);
-        dotty.keys.nattr = copy (gt.graph.nodeattr);
-        dotty.keys.eattr = copy (gt.graph.edgeattr);
-    ];
-    sgraph = gt.graph.graphs[gid];
-    if (~attr)
-        attr = [];
-    if (~attr.label)
-        attr.label = '\N';
-    for (aid in attr)
-        sgraph.graphattr[aid] = attr[aid];
-    gt.unpacksgraphattr (gt, sgraph);
-    if (show)
-        gt.drawsgraph (gt, gt.views, sgraph);
-    return sgraph;
-};
-dotty.protogt.removesgraph = function (gt, sgraph) {
-    gt.undrawsgraph (gt, gt.views, sgraph);
-    remove (sgraph.name, gt.graph.graphdict);
-    remove (sgraph.gid, gt.graph.graphs);
-};
-dotty.protogt.insertnode = function (gt, pos, size, name, attr, show) {
-    local nid, node, aid;
-
-    nid = gt.graph.maxnid;
-    if (~name) {
-        while (gt.graph.nodedict[(name = concat ('n', nid))] >= 0)
-            nid = nid + 1;
-    } else if (gt.graph.nodedict[name] >= 0) {
-        dotty.message (0, concat ('node: ', name, ' exists'));
-        return null;
-    }
-    gt.graph.nodedict[name] = nid;
-    gt.graph.maxnid = nid + 1;
-    gt.graph.nodes[nid] = [
-        dotty.keys.nid   = nid;
-        dotty.keys.name  = name;
-        dotty.keys.attr  = copy (gt.graph.nodeattr);
-        dotty.keys.edges = [];
-    ];
-    node = gt.graph.nodes[nid];
-    if (~attr)
-        attr = [];
-    if (~attr.label)
-        attr.label = '\N';
-    for (aid in attr)
-        node.attr[aid] = attr[aid];
-    gt.unpacknodeattr (gt, node);
-    if (~pos)
-        pos = ['x' = 10; 'y' = 10;];
-    node[dotty.keys.pos] = copy (pos);
-    if (~size)
-        size = ['x' = strlen (attr.label) * 30; 'y' = 30;];
-    if (size.x == 0)
-        size.x = 30;
-    node[dotty.keys.size] = copy (size);
-    if (show)
-        gt.drawnode (gt, gt.views, node);
-    if (~gt.noundo) {
-        gt.startadd2undo (gt);
-        gt.currundo.inserted.nodes[nid] = node;
-        gt.endadd2undo (gt);
-    }
-    return node;
-};
-dotty.protogt.removenode = function (gt, node) {
-    local eid, list, edge, gid;
-
-    if (~gt.noundo)
-        gt.startadd2undo (gt);
-    for (eid in node.edges)
-        list[eid] = node.edges[eid];
-    for (eid in list)
-        gt.removeedge (gt, list[eid]);
-    gt.undrawnode (gt, gt.views, node);
-    for (gid in gt.graph.graphs)
-        remove (node.nid, gt.graph.graphs[gid].nodes);
-    remove (node.name, gt.graph.nodedict);
-    remove (node.nid, gt.graph.nodes);
-    if (~gt.noundo) {
-        gt.currundo.deleted.nodes[node.nid] = node;
-        gt.endadd2undo (gt);
-    }
-};
-dotty.protogt.insertedge =
-        function (gt, nodea, porta, nodeb, portb, attr, show) {
-    local eid, edge, aid, tport, hport;
-
-    if (~nodea | ~nodeb)
-        return null;
-    if (porta)
-        tport = porta;
-    if (portb)
-        hport = portb;
-    eid = gt.graph.maxeid;
-    while (gt.graph.edges[eid])
-        eid = eid + 1;
-    gt.graph.maxeid = eid + 1;
-    gt.graph.edges[eid] = [
-        dotty.keys.eid   = eid;
-        dotty.keys.tail  = nodea;
-        dotty.keys.tport = porta;
-        dotty.keys.head  = nodeb;
-        dotty.keys.hport = portb;
-        dotty.keys.attr  = copy (gt.graph.edgeattr);
-    ];
-    edge = gt.graph.edges[eid];
-    if (~attr)
-        attr = [];
-    for (aid in attr)
-        edge.attr[aid] = attr[aid];
-    nodea.edges[eid] = edge;
-    nodeb.edges[eid] = edge;
-    edge[dotty.keys.points] = [
-        0 = copy (nodea.pos);
-        1 = copy (nodea.pos);
-        2 = copy (nodeb.pos);
-        3 = copy (nodeb.pos);
-    ];
-    gt.unpackedgeattr (gt, edge);
-    if (show)
-        gt.drawedge (gt, gt.views, edge);
-    if (~gt.noundo) {
-        gt.startadd2undo (gt);
-        gt.currundo.inserted.edges[eid] = edge;
-        gt.endadd2undo (gt);
-    }
-    return edge;
-};
-dotty.protogt.removeedge = function (gt, edge) {
-    local head, tail;
-
-    if (~gt.noundo)
-        gt.startadd2undo (gt);
-    if (edge.head.attr.support == 1)
-        head = edge.head;
-    if (edge.tail.attr.support == 1)
-        if (head ~= edge.tail)
-            tail = edge.tail;
-    gt.undrawedge (gt, gt.views, edge);
-    remove (edge.eid, edge.head.edges);
-    remove (edge.eid, edge.tail.edges);
-    remove (edge.eid, gt.graph.edges);
-    if (head & tablesize (head.edges) == 0)
-        gt.removenode (gt, head);
-    if (tail & tablesize (tail.edges) == 0)
-        gt.removenode (gt, tail);
-    if (~gt.noundo) {
-        gt.currundo.deleted.edges[edge.eid] = edge;
-        gt.endadd2undo (gt);
-    }
-};
-dotty.protogt.swapedgeids = function (gt, edge1, edge2) {
-    local eid1, eid2;
-
-    if (edge1.eid == edge2.eid)
-        return;
-    if (~gt.noundo)
-        gt.startadd2undo (gt);
-    eid1 = edge1.eid;
-    eid2 = edge2.eid;
-    gt.graph.edges[eid1] = edge2;
-    gt.graph.edges[eid2] = edge1;
-    remove (eid1, edge1.tail.edges);
-    remove (eid1, edge1.head.edges);
-    remove (eid2, edge2.tail.edges);
-    remove (eid2, edge2.head.edges);
-    edge1.tail.edges[eid2] = edge1;
-    edge1.head.edges[eid2] = edge1;
-    edge2.tail.edges[eid1] = edge2;
-    edge2.head.edges[eid1] = edge2;
-    edge1.eid = eid2;
-    edge2.eid = eid1;
-    if (~gt.noundo) {
-        gt.currundo.swapped.edges[eid1] = edge1;
-        gt.currundo.swapped.edges[eid2] = edge2;
-        gt.endadd2undo (gt);
-    }
-};
-dotty.protogt.foldsubtree = function (gt, obj) {
-    local nlist, stnode, node, head, tail, nid, edge, eid, n, entry, chfl;
-
-    chfl = 0;
-
-    if (obj.nid >= 0) {
-        stnode = obj;
-        node = stnode;
-    }
-    else {
-        echo('Invalid node');
-        return;
-    }
-
-    if ( obj.attr.folded == '1' )  {
-        echo('Already folded');
-        return;
-    }
-
-    if (node.pos)
-        node.attr.pos = concat (node.pos.x, ',', node.pos.y);
-
-    gt.startadd2undo (gt);
-
-    nlist = [node.nid = node;];
-
-    while (node) {       
-        for (eid in node.edges) {       
-            head = node.edges[eid].head;
-            if (head ~= node) {
-                nlist[head.nid] = head;
-            }
-        }
-
-        if ( node.nid ~= stnode.nid ) {
-            gt.removenode (gt, node);
-            gt.undrawnode (gt, gt.views, node);
-            chfl = 1;
-        }
-        remove (node.nid, nlist);
-        node = null;
-        for (nid in nlist) {
-            node = nlist[nid];
-            for (eid in node.edges) {
-                edge = node.edges[eid];
-                if (edge.head == node & edge.tail ~= node & edge.tail ~= stnode) {
-                    node = null;
-                    break;
-                }
-            }
-            if (node)
-                break;
-        }
-    }
-    gt.endadd2undo (gt);
-
-    if ( chfl ) {
-        obj.attr.folded = '1';
-
-        # Move undo list locally to node that is folded
-        if ((n = tablesize (gt.undoarray.entries)) < 1)
-            return;
-        entry = gt.undoarray.entries[n - 1];
-        remove (n - 1, gt.undoarray.entries);
-        remove ('currundo', gt);
-        gt.noundo = 1;
-        if ( ~obj.attr.foldinfo )
-            obj.attr.foldinfo = [];
-        obj.attr.foldinfo = entry;
-        gt.noundo = 0;
-    }
-    return chfl;
-};
-dotty.protogt.unfoldsubtree = function (gt, obj) {
-    local n;
-
-    if ( obj.nid < 0) {
-        echo('Invalid node');
-        return;
-    }
-
-    if ( obj.attr.folded ~= '1' )
-        return;
-
-    gt.undrawnode (gt, gt.views, obj);
-    obj.attr.folded = '0';
-    gt.drawnode (gt, gt.views, obj);
-
-    # Restore undo info
-    gt.noundo = 1;
-
-    if ( ~obj.attr.foldinfo ) {
-        echo('Error:  No fold info for folded node');
-        return;
-    }       
-
-    gt.startadd2undo( gt );
-    n = tablesize (gt.undoarray.entries);
-
-    gt.undoarray.entries[n - 1] = obj.attr.foldinfo;
-    gt.endadd2undo( gt );
-
-    gt.noundo = 0;
-    gt.undo( gt, 1);
-};
-dotty.protogt.unfoldall = function (gt ) {
-    local nid, node, cleared, vt;
-
-    cleared = 0;
-    while (~cleared) {
-        cleared = 1;
-        for (nid in gt.graph.nodes) {
-            node = gt.graph.nodes[nid];
-            if ( node.attr.folded == '1' ) {
-                cleared = 0;
-                gt.unfoldsubtree( gt, node );
-            }
-        }
-    }
-    gt.layoutgraph (gt);
-    gt.redrawgraph (gt, [vt.vtid = vt;]);
-};
-dotty.protogt.removesubtree = function (gt, obj) {
-    local nlist, node, head, nid, edge, eid;
-
-    if (~gt.noundo)
-        gt.startadd2undo (gt);
-    if (obj.nid >= 0)
-        node = obj;
-    else if (obj.eid >= 0) {
-        node = obj.head;
-        gt.removeedge (gt, obj);
-        if (~gt.graph.nodes[node.nid]) {
-            if (~gt.noundo)
-                gt.endadd2undo (gt);
-            return;
-        }
-        for (eid in node.edges) {
-            edge = node.edges[eid];
-            if (edge.head == node & edge.tail ~= node) {
-                if (~gt.noundo)
-                    gt.endadd2undo (gt);
-                return;
-            }
-        }
-    } else {
-        dotty.message (0, 'bad object type in gt.removesubtree');
-        return;
-    }
-    nlist = [node.nid = node;];
-    while (node) {
-        for (eid in node.edges) {
-            head = node.edges[eid].head;
-            if (head ~= node)
-                nlist[head.nid] = head;
-        }
-        gt.removenode (gt, node);
-        remove (node.nid, nlist);
-        node = null;
-        for (nid in nlist) {
-            node = nlist[nid];
-            for (eid in node.edges) {
-                edge = node.edges[eid];
-                if (edge.head == node & edge.tail ~= node) {
-                    node = null;
-                    break;
-                }
-            }
-            if (node)
-                break;
-        }
-    }
-    if (~gt.noundo)
-        gt.endadd2undo (gt);
-};
-dotty.protogt.removenodesbyattr = function (gt, key, val) {
-    local nlist, nid;
-
-    if (~gt.noundo)
-        gt.startadd2undo (gt);
-    nlist = gt.getnodesbyattr (gt, key, val);
-    for (nid in nlist)
-        gt.removenode (gt, nlist[nid]);
-    if (~gt.noundo)
-        gt.endadd2undo (gt);
-};
-dotty.protogt.removesubtreesbyattr = function (gt, key, val) {
-    local nlist, nid;
-
-    if (~gt.noundo)
-        gt.startadd2undo (gt);
-    nlist = gt.getnodesbyattr (gt, key, val);
-    for (nid in nlist)
-        if (gt.graph.nodes[nid])
-            gt.removesubtree (gt, nlist[nid]);
-    if (~gt.noundo)
-        gt.endadd2undo (gt);
-};
-dotty.protogt.groupnodes = function (gt, nlist, gnode, pos, size, attr,
-        keepmulti, show) {
-    local nid, node, elist, eid, edge, nodea, nodeb, inlist, outlist;
-
-    if (~nlist | tablesize (nlist) == 0)
-        return;
-    if (gnode.attr.support) {
-        dotty.message (0, 'cannot group nodes in a support node');
-        return;
-    }
-    if (~gt.noundo)
-        gt.startadd2undo (gt);
-    if (~gnode)
-        gnode = gt.insertnode (gt, pos, size, null, attr, show);
-    inlist = [];
-    outlist = [];
-    for (nid in nlist) {
-        if ((node = nlist[nid]) == gnode)
-            continue;
-        elist = [];
-        for (eid in node.edges)
-            elist[eid] = node.edges[eid];
-        for (eid in elist) {
-            edge = elist[eid];
-            if (edge.head == node) {
-                nodea = edge.tail;
-                nodeb = gnode;
-                if (~keepmulti) {
-                    if (inlist[nodea.nid])
-                        continue;
-                    inlist[nodea.nid] = nodea;
-                    if (nodea == gnode)
-                        outlist[nodea.nid] = nodea;
-                }
-            } else {
-                nodea = gnode;
-                nodeb = edge.head;
-                if (~keepmulti) {
-                    if (outlist[nodeb.nid])
-                        continue;
-                    outlist[nodeb.nid] = nodeb;
-                    if (nodeb == gnode)
-                        inlist[nodeb.nid] = nodeb;
-                }
-            }
-            gt.insertedge (gt, nodea, null, nodeb, null, edge.attr, show);
-        }
-        gt.removenode (gt, node);
-    }
-    if (~gt.noundo)
-        gt.endadd2undo (gt);
-    return gnode;
-};
-dotty.protogt.groupnodesbyattr =
-        function (gt, key, val, attr, keepmulti, show) {
-    local nlist, nid, pos, size;
-
-    pos = null;
-    size = null;
-    nlist = gt.getnodesbyattr (gt, key, val);
-    if (show)
-        for (nid in nlist) {
-            pos = nlist[nid].pos;
-            size = nlist[nid].size;
-            break;
-        }
-    return gt.groupnodes (gt, nlist, null, pos, size, attr, keepmulti, show);
-};
-dotty.protogt.cut = function (gt, obj, set, mode, op) {
-    local clipgt, list, node, nid, edge, eid, clipnode;
-
-    clipgt = dotty.clipgt;
-    clipgt.graph = copy (dotty.protogt.graph);
-    if (obj.eid >= 0) { # it's an edge
-        list.edges[obj.eid] = obj;
-        node = obj.head;
-    } else if (obj.nid >= 0) {
-        list.nodes[obj.nid] = obj;
-        node = obj;
-        for (eid in node.edges)
-            list.edges[eid] = node.edges[eid];
-    } else {
-        dotty.message (0, 'unknown object type in gt.cut');
-        return;
-    }
-    if (set == 'reachable') {
-        list.nodes = gt.reachablenodes (gt, node);
-        for (nid in list.nodes) {
-            node = list.nodes[nid];
-            for (eid in node.edges) {
-                edge = node.edges[eid];
-                list.edges[edge.eid] = edge;
-            }
-        }
-    }
-    if (mode == 'support') {
-        for (eid in list.edges) {
-            edge = list.edges[eid];
-            if (~list.nodes[edge.tail.nid]) {
-                list.support[edge.tail.nid] = edge.tail;
-                list.nodes[edge.tail.nid] = edge.tail;
-            }
-            if (~list.nodes[edge.head.nid]) {
-                list.support[edge.head.nid] = edge.head;
-                list.nodes[edge.head.nid] = edge.head;
-            }
-        }
-    }
-    for (nid = 0; nid < gt.graph.maxnid; nid = nid + 1) {
-        if (~list.nodes[nid])
-            continue;
-        node = list.nodes[nid];
-        clipnode = gt.insertnode (clipgt, null, null, node.name, node.attr, 0);
-        if (list.support[nid])
-            clipnode.support = 1;
-        list.clipnodes[nid] = clipnode;
-    }
-    for (eid = 0; eid < gt.graph.maxeid; eid = eid + 1) {
-        if (~list.edges[eid])
-            continue;
-        edge = list.edges[eid];
-        if (~list.nodes[edge.tail.nid] | ~list.nodes[edge.head.nid])
-            continue;
-        gt.insertedge (clipgt, list.clipnodes[edge.tail.nid], null,
-                list.clipnodes[edge.head.nid], null, edge.attr, 0);
-    }
-    if (op ~= 'cut')
-        return;
-    if (~gt.noundo)
-        gt.startadd2undo (gt);
-    for (eid in list.edges)
-        gt.removeedge (gt, list.edges[eid]);
-    for (nid in list.nodes)
-        if (~list.support[nid] & gt.graph.nodes[nid])
-            gt.removenode (gt, list.nodes[nid]);
-    if (~gt.noundo)
-        gt.endadd2undo (gt);
-};
-dotty.protogt.paste = function (gt, pos, show) {
-    local clipgt, offset, center, nid, node, eid, edge, nodes;
-
-    if (~gt.noundo)
-        gt.startadd2undo (gt);
-    clipgt = dotty.clipgt;
-    if (clipgt.graph.rect)
-        center = [
-            'x' = (clipgt.graph.rect[1].x + clipgt.graph.rect[0].x) / 2;
-            'y' = (clipgt.graph.rect[1].y + clipgt.graph.rect[0].y) / 2;
-        ];
-    else
-        center = pos;
-    offset = [
-        'x' = center.x - pos.x;
-        'y' = center.y - pos.y;
-    ];
-    for (nid = 0; clipgt.graph.nodes[nid]; nid = nid + 1) {
-        node = clipgt.graph.nodes[nid];
-        if (node.attr.label == '\N' | ~node.attr.label)
-            node.attr.label = node.name;
-        if (node.support == 1)
-            nodes[nid] = gt.insertnode (gt, [
-                'x' = node.pos.x - offset.x;
-                'y' = node.pos.y - offset.y;
-            ], null, null, [
-                'support' = 1; 'shape' = 'circle';
-                'label' = ''; 'width' = 0.2;
-            ], show);
-        else
-            nodes[nid] = gt.insertnode (gt, [
-                'x' = node.pos.x - offset.x;
-                'y' = node.pos.y - offset.y;
-            ], node.size, null, node.attr, show);
-    }
-    for (eid = 0; clipgt.graph.edges[eid]; eid = eid + 1) {
-        edge = clipgt.graph.edges[eid];
-        gt.insertedge (gt, nodes[edge.tail.nid], null,
-                nodes[edge.head.nid], null, edge.attr, show);
-    }
-    if (~gt.noundo)
-        gt.endadd2undo (gt);
-};
-dotty.protogt.startadd2undo = function (gt) {
-    if (~gt.undoarray.level)
-        gt.currundo =
-                (gt.undoarray.entries[tablesize (gt.undoarray.entries)] = []);
-    gt.undoarray.level = gt.undoarray.level + 1;
-};
-dotty.protogt.endadd2undo = function (gt) {
-    gt.undoarray.level = gt.undoarray.level - 1;
-};
-dotty.protogt.undo = function (gt, show) {
-    local entry, n, eid, edge, nid, node, edges;
-
-    if ((n = tablesize (gt.undoarray.entries)) < 1)
-        return;
-    entry = gt.undoarray.entries[n - 1];
-    remove (n - 1, gt.undoarray.entries);
-    remove ('currundo', gt);
-    gt.noundo = 1;
-    # hardwire nodes and edges back with the same id's as the originals
-    for (nid in entry.deleted.nodes) {
-        node = entry.deleted.nodes[nid];
-        gt.graph.nodedict[node.name] = node.nid;
-        gt.graph.nodes[node.nid] = node;
-        node.edges = [];
-        if (show)
-            gt.drawnode (gt, gt.views, node);
-    }
-    for (eid in entry.deleted.edges) {
-        edge = entry.deleted.edges[eid];
-        gt.graph.edges[edge.eid] = edge;
-        edge.head.edges[edge.eid] = edge;
-        edge.tail.edges[edge.eid] = edge;
-        if (show)
-            gt.drawedge (gt, gt.views, edge);
-    }
-    if (entry.swapped.edges) {
-        if (tablesize (entry.swapped.edges) == 2) {
-            n = 0;
-            for (eid in entry.swapped.edges) {
-                edges[n] = entry.swapped.edges[eid];
-                n = n + 1;
-            }
-            gt.swapedgeids (gt, edges[0], edges[1]);
-        } else
-            dotty.message (0, 'cannot handle undoing swap of > 2 edges');
-    }
-    for (eid in entry.inserted.edges) {
-        edge = entry.inserted.edges[eid];
-        gt.removeedge (gt, edge);
-    }
-    for (nid in entry.inserted.nodes) {
-        node = entry.inserted.nodes[nid];
-        gt.removenode (gt, node);
-    }
-    gt.noundo = 0;
-};
diff --git a/contrib/lefty-grace/dotty_ui.lefty b/contrib/lefty-grace/dotty_ui.lefty
deleted file mode 100644 (file)
index d3bcf62..0000000
+++ /dev/null
@@ -1,607 +0,0 @@
-#
-# dotty_ui: user interface functions and data structures
-#
-dotty.protogt.doaction = function (data, s) {
-    local vt, gt;
-
-    vt = dotty.views[data.widget];
-    gt = dotty.graphs[vt.gtid];
-    if (data.obj.nid >= 0) {
-        if (gt.actions.node[s]) {
-            gt.actions.node[s] (gt, vt, data.obj, data);
-            return;
-        }
-    } else if (data.obj.eid >= 0) {
-        if (gt.actions.edge[s]) {
-            gt.actions.edge[s] (gt, vt, data.obj, data);
-            return;
-        }
-    }
-    if (gt.actions.general[s])
-        gt.actions.general[s] (gt, vt, data);
-};
-dotty.protogt.actions.general = [
-    "undo" = function (gt, vt, data) {
-        gt.undo (gt, 1);
-    };
-    "paste" = function (gt, vt, data) {
-        gt.paste (gt, data.pos, 1);
-    };
-    "do layout" = function (gt, vt, data) {
-        gt.layoutgraph (gt);
-    };
-    "cancel layout" = function (gt, vt, data) {
-        gt.cancellayout (gt);
-    };
-    "redraw" = function (gt, vt, data) {
-        gt.redrawgraph (gt, [vt.vtid = vt;]);
-    };
-    "new graph" = function (gt, vt, data) {
-        gt.erasegraph (gt, null, null);
-    };
-    "load graph" = function (gt, vt, data) {
-        if ( gt.name ~= '') {
-            gt.unfoldall (gt);
-            gt.savegraph (gt, gt.name, 'file', 1);
-        }
-        gt.loadgraph (gt, null, 'file', dotty.protogt.graph, 1);
-    };
-    "load parent" = function (gt, vt, data) {
-        local pname;
-        pname = gt.graph['parent'];
-
-        if ( ~pname | pname == '' )
-            return;
-     
-        if ( gt.name ~= '') {
-            gt.unfoldall (gt);
-            gt.savegraph (gt, gt.name, gt.type, 1);
-        }
-
-        gt.loadgraph (gt, pname, 'file', dotty.protogt.graph, 1);
-    };
-    "reload graph" = function (gt, vt, data) {
-        gt.loadgraph (gt, gt.name, gt.type, gt.graph, 1);
-    };
-    "save graph" = function (gt, vt, data) {
-        gt.unfoldall (gt);
-        gt.savegraph (gt, gt.name, gt.type, 1);
-    };
-    "save graph as" = function (gt, vt, data) {
-        gt.unfoldall (gt);
-        gt.savegraph (gt, null, 'file', 1);
-    };
-    "open view" = function (gt, vt, data) {
-        gt = dotty.protogt.creategraph (null);
-        gt.createview (gt, null);
-    };
-    "copy view" = function (gt, vt, data) {
-        gt = gt.copygraph (gt);
-        gt.createview (gt, vt);
-    };
-    "birdseye view" = function (gt, vt, data) {
-        gt.createview (gt, dotty.protovt.birdseye);
-    };
-    "clone view" = function (gt, vt, data) {
-        gt.createview (gt, vt);
-    };
-    "close view" = function (gt, vt, data) {
-        gt.destroyview (gt, vt);
-        if (tablesize (gt.views) == 0)
-            gt.destroygraph (gt);
-    };
-    "set graph attr" = function (gt, vt, data) {
-        gt.setattr (gt, ['attr' = gt.graph.graphattr;]);
-    };
-    "set node attr" = function (gt, vt, data) {
-        gt.setattr (gt, ['attr' = gt.graph.nodeattr;]);
-    };
-    "set edge attr" = function (gt, vt, data) {
-        gt.setattr (gt, ['attr' = gt.graph.edgeattr;]);
-    };
-    "zoom in" = function (gt, vt, data) {
-        gt.zoom (gt, vt, 0.5, data.pos);
-    };
-    "zoom out" = function (gt, vt, data) {
-        gt.zoom (gt, vt, 2, data.pos);
-    };
-    "zoom in slowly" = function (gt, vt, data) {
-        gt.zoom (gt, vt, 0.9, data.pos);
-    };
-    "zoom out slowly" = function (gt, vt, data) {
-        gt.zoom (gt, vt, 1.1, data.pos);
-    };
-    "move interactively" = function (gt, vt, data) {
-        gt.setviewcenter ([vt.vtid = vt;], data.pos);
-#        gt.redrawgraph (gt, [vt.vtid = vt;]);
-    };      
-    "fold subgraph" = function (gt, vt, data) {
-        local grph;
-
-        grph = ask( 'Name of subgraph to fold');
-        if ( ~grph | grph == '' | grph == ' ' | ~gt.graph.graphdict[grph] ) {
-            echo('No subgraph found');
-            return;
-        }
-        gt.foldsgraph(gt, gt.views, grph );
-    };
-    "add node" = function( gt, vt, data) {
-        local newname;
-
-        newname = ask('Give new node name');
-        if (newname == '' | newname == ' ')
-            newname=0;
-        gt.insertnode (gt, data.pos, null, newname, null, 1);
-    };
-    "add subgraph" = function (gt, vt, data) {
-        local newname;
-
-        newname = ask('Give new subgraph name');
-        if (newname == '' | newname == ' ')
-            newname=0;
-        gt.insertsgraph (gt, newname, null, 1);
-    };
-
-    "find node" = function (gt, vt, data) {
-        gt.findnode (gt, vt);
-    };
-    "print graph" = function (gt, vt, data) {
-        gt.printorsave (gt, vt, null, null, null, null);
-    };
-    "text view" = function (gt, vt, data) {
-        if (dotty.txtview == 'on')
-            dotty.txtview = 'off';
-        else
-            dotty.txtview = 'on';
-        txtview (dotty.txtview);
-    };
-    "quit" = function (gt, vt, data) {
-        local reply;
-
-        reply = ask ('Save graph', 'choice', 'Yes|No|Cancel');
-        if (reply == "Cancel")
-            return;
-        else if ( reply == "Yes") {
-            gt.unfoldall (gt);
-            gt.savegraph (gt, gt.name, gt.type, 1);
-        }
-        exit ();
-    };
-];
-dotty.protogt.actions.node = [
-    "cut" = function (gt, vt, obj, data) {
-        gt.cut (gt, obj, 'one', 'support', 'cut');
-        dotty.clipgt.layoutgraph (dotty.clipgt);
-    };
-    "Cut" = function (gt, vt, obj, data) {
-        gt.cut (gt, obj, 'reachable', 'support', 'cut');
-        dotty.clipgt.layoutgraph (dotty.clipgt);
-    };
-    "copy" = function (gt, vt, obj, data) {
-        gt.cut (gt, obj, 'one', 'support', 'copy');
-        dotty.clipgt.layoutgraph (dotty.clipgt);
-    };
-    "Copy" = function (gt, vt, obj, data) {
-        gt.cut (gt, obj, 'reachable', 'support', 'copy');
-        dotty.clipgt.layoutgraph (dotty.clipgt);
-    };
-    "group" = function (gt, vt, obj, data) {
-        local kv;
-
-        if ((kv = gt.getattr (gt, obj)))
-            gt.groupnodesbyattr (gt, kv.key, kv.val, [
-                'label' = kv.val; kv.key = kv.val;
-            ], 1, 1);
-    };
-    "Group" = function (gt, vt, obj, data) {
-        local kv;
-
-        if ((kv = gt.getattr (gt, obj)))
-            gt.groupnodesbyattr (gt, kv.key, kv.val, [
-                'label' = kv.val; kv.key = kv.val;
-            ], 0, 1);
-    };
-    "delete" = function (gt, vt, obj, data) {
-        if (obj.eid >= 0)
-            gt.removeedge (gt, obj);
-        else
-            gt.removenode (gt, obj);
-    };
-    "Delete" = function (gt, vt, obj, data) {
-        gt.removesubtree (gt, obj);
-    };
-    "remove" = function (gt, vt, obj, data) {
-        if (obj.nid >= 0)
-            if ((kv = gt.getattr (gt, obj)))
-                gt.removenodesbyattr (gt, kv.key, kv.val);
-    };
-    "Remove" = function (gt, vt, obj, data) {
-        if (obj.nid >= 0)
-            if ((kv = gt.getattr (gt, obj)))
-                gt.removesubtreesbyattr (gt, kv.key, kv.val);
-    };
-    "fold/unfold subtree" = function (gt, vt, obj, data) {
-        if ( obj.nid >= 0 ) {
-            if ( obj.attr.folded == '0' ) {
-                if ( gt.foldsubtree( gt, obj )) {        
-#                    gt.layoutgraph (gt);
-#                    gt.setviewcenter ([vt.vtid = vt;], obj.pos);
-                    gt.drawgraph (gt, [vt.vtid = vt;]);
-                }
-            }       
-            else if ( obj.attr.folded == '1' ) {
-                gt.unfoldsubtree( gt, obj );
-                gt.layoutgraph (gt);
-#  Recentering on base node doesn't seem to work in every case...
-#                gt.setviewcenter ([vt.vtid = vt;], obj.pos);
-#                gt.redrawgraph (gt, [vt.vtid = vt;]);
-            }
-        }
-        else
-            echo('Not on a node');
-    };
-    "unfold subtree" = function (gt, vt, obj, data) {
-        if ( obj.nid >= 0 ) {
-            gt.unfoldsubtree( gt, obj );        
-            gt.drawgraph (gt, [vt.vtid = vt;]);
-        }
-        else
-            echo('Not on a node', obj.nid);
-    };
-    "set attr" = function (gt, vt, obj, data) {
-        gt.setattr (gt, obj);
-    };
-    "print attr" = function (gt, vt, obj, data) {
-        local attname;
-        if (obj.nid >= 0) {
-            attname = ask('What attribute?');
-            echo (obj.attr[attname]);
-        }
-        dump (obj.attr);
-    };
-    "change label" = function (gt, vt, obj, data) {
-        local newname;
-        newname = ask('Enter new label name');
-        if ( newname == 1 )
-            return;
-        if (newname == '' | newname == ' ')
-            newname=0;
-        if ( obj.nid >= 0 ) {
-            gt.undrawnode (gt, gt.views, obj);
-            obj.attr['label'] = newname;
-            gt.unpacknodeattr (gt, obj);
-            gt.drawnode (gt, gt.views, obj);
-        }
-        else if (obj.eid >= 0) {
-            gt.undrawedge (gt, gt.views, obj);
-            obj.attr['label'] = newname;
-            gt.unpackedgeattr (gt, obj);
-            gt.drawedge (gt, gt.views, obj);
-        }
-        gt.layoutgraph (gt);
-    };
-    "load subgraph" = function (gt, vt, obj, data) {
-         local node, fname, pname, ngt, rtn, fd, graph;
-
-         if ( obj.nid < 0) {
-             echo('Invalid node');
-             return;
-         }
-
-         gt.unfoldall (gt);
-         gt.savegraph (gt, gt.name, gt.type, 1);
-
-         pname = gt.name;
-         fname = concat (obj.name, '.gv');
-         ngt = dotty.protogt.creategraph (null);
-         ngt.graphattr.parent = pname;
-
-         if (~((fd = dotty.openio (fname, 'file', 'r')) >= 0)) {
-             gt.erasegraph (gt, null, null);
-             ngt.createview (ngt, null);
-             gt.destroyview (gt, vt);
-             ngt.redrawgraph (ngt, ngt.views);
-             ngt.insertnode (ngt, ['x' = 30; 'y' = 0;], null, obj.name, null, 1);
-             ngt.savegraph (ngt, fname, 'file', 1);
-         }
-         else 
-             gt.loadgraph (gt, fname, 'file', ngt, 1);
-     };
-];
-dotty.protogt.actions.edge = dotty.protogt.actions.node;
-dotty.protovt.normal.menus = [
-    'general' = [
-        0 = "undo";
-        1 = "paste";
-        2 = "do layout";
-        3 = "cancel layout";
-        4 = "redraw";
-        5 = "new graph";
-        6 = "load graph";
-        7 = "reload graph";
-       8 = "load parent";
-        9 = "save graph";
-        10 = "save graph as";
-        11 = "open view";
-        12 = "copy view";
-        13 = "clone view";
-        14 = "birdseye view";
-        15 = "close view";
-        16 = "set graph attr";
-        17 = "set node attr";
-        18 = "set edge attr";
-        19 = "zoom in";
-        20 = "zoom out";
-        21 = "find node";
-        22 = "print graph";
-        23 = "text view";
-        24 = "quit";
-    ];
-    'node' = [
-        0 = "cut";
-        1 = "Cut";
-        2 = "copy";
-        3 = "Copy";
-        4 = "group";
-        5 = "Group";
-        6 = "delete";
-        7 = "Delete";
-        8 = "remove";
-        9 = "Remove";
-        10 = "set attr";
-        11 = "print attr";
-        12 = "change label";
-       13 = "load subgraph";
-    ];
-    'edge' = [
-        0 = "cut";
-        1 = "Cut";
-        2 = "copy";
-        3 = "Copy";
-        4 = "delete";
-        5 = "Delete";
-        6 = "set attr";
-        7 = "print attr";
-        8 = "change label";
-    ];
-];
-dotty.protovt.normal.keys = [
-    'general' = [
-        'u' = "undo";
-        'p' = "paste";
-        'l' = "do layout";
-        'k' = "cancel layout";
-        ' ' = "redraw";
-        'R' = "reload graph";
-        'L' = "load graph";
-       '+' = "load parent";
-        's' = "save graph";
-        'Z' = "zoom in";
-        'z' = "zoom out";
-        '.' = "zoom in slowly";
-        ',' = "zoom out slowly";
-        '      ' = "move interactively";
-        'n' = "add node";
-        'Q' = "quit";
-    ];
-    'node' = [
-        'c' = "copy";
-        'C' = "Copy";
-        'g' = "group";
-        'G' = "Group";
-        'd' = "delete";
-        'D' = "Delete";
-        'r' = "remove";
-        'R' = "Remove";
-        'f' = "fold/unfold subtree";
-        'a' = "set attr";
-        '/'  = "change label";
-       '-' = "load subgraph";
-    ];
-    'edge' = [
-        'c' = "copy";
-        'C' = "Copy";
-        'd' = "delete";
-        'D' = "Delete";
-        'a' = "set attr";
-        '/'  = "change label";
-    ];
-];
-dotty.protovt.birdseye.menus = dotty.protovt.normal.menus;
-dotty.protovt.birdseye.keys = dotty.protovt.normal.keys;
-dotty.protovt.normal.uifuncs = [
-    'leftdown' = function (data) {
-        local gt;
-
-        gt = dotty.graphs[vt.gtid];
-        vt = dotty.views[data.widget];
-        if (dotty.node2move) {
-# &        (dotty.rp2.x ~= data.pos.x | dotty.rp2.y ~= data.pos.y)) {
-            gt.movenode (gt, dotty.node2move, data.pos);
-            gt.redrawgraph (gt, [vt.vtid = vt;]);
-#            gt.redrawgraph (gt, gt.views);
-            dotty.rp2 = data.pos;
-        }
-        else if (data.obj.nid >= 0) {
-            dotty.node2move = data.obj;
-            dotty.movewidget = data.widget;
-            dotty.rp2 = data.pos;
-        }
-    };
-    'leftmove' = function (data) {
-        local vt,gt;
-
-        gt = dotty.graphs[vt.gtid];
-        vt = dotty.views[data.widget];
-        dotty.node2move = data.obj;
-        dotty.movewidget = data.widget;
-        dotty.rp2 = data.pos;
-
-        if (dotty.node2move ) {
-# &      (dotty.rp2.x ~= data.pos.x | dotty.rp2.y ~= data.pos.y)) {
-            gt.movenode (gt, dotty.node2move, dotty.rp2);
-            gt.setviewscale ([vt.vtid = vt;], 1);
-            gt.redrawgraph (gt, [vt.vtid = vt;]);
-#            gt.redrawgraph (gt, gt.views);
-            dotty.rp2 = data.pos;
-        }
-    };
-    'leftup' = function (data) {
-        local gt;
-
-        gt = dotty.graphs[dotty.views[data.widget].gtid];
-        if (dotty.node2move) {
-            if (dotty.movewidget == data.widget)
-                gt.movenode (gt, dotty.node2move, data.pos);
-            dotty.node2move = 0;
-        }
-# Use 'n' key to create new node
-# else if (~data.obj)
-#            gt.insertnode (gt, data.pos, null, null, null, 1);
-    };
-    'middledown' = function (data) {
-        if (~(data.obj.nid >= 0))
-            return;
-        dotty.rubberband = 1;
-        dotty.movewidget = data.widget;
-        setgfxattr (data.widget, ['mode' = 'xor';]);
-#        setgfxattr (data.widget, ['mode' = 'src';]);
-        dotty.rp1 = data.pos;
-        dotty.rp2 = data.pos;
-        line (data.widget, null, dotty.rp1, dotty.rp2, ['color' = 1;]);
-    };
-    'middlemove' = function (data) {
-        if (dotty.rubberband ~= 1 |
-                (dotty.rp2.x == data.pos.x & dotty.rp2.y == data.pos.y))
-            return;
-        line (data.widget, null, dotty.rp1, dotty.rp2, ['color' = 1;]);
-        dotty.rp2 = data.pos;
-        line (data.widget, null, dotty.rp1, dotty.rp2, ['color' = 1;]);
-    };
-    'middleup' = function (data) {
-        local gt;
-
-        gt = dotty.graphs[dotty.views[data.widget].gtid];
-        if (dotty.rubberband ~= 1)
-            return;
-        dotty.rubberband = 0;
-        line (dotty.movewidget, null, dotty.rp1, dotty.rp2, ['color' = 1;]);
-        setgfxattr (dotty.movewidget, ['mode' = 'src';]);
-        if (dotty.movewidget ~= data.widget |
-                ~(data.pobj.nid >= 0) | ~(data.obj.nid >= 0))
-            return;
-        if (data.pobj.attr.support)
-            gt.groupnodes (gt, [
-                data.obj.nid = data.obj;
-                data.pobj.nid = data.pobj;
-            ], data.obj, null, null, null, 1, 1);
-        else if (data.obj.attr.support)
-            gt.groupnodes (gt, [
-                data.obj.nid = data.obj;
-                data.pobj.nid = data.pobj;
-            ], data.pobj, null, null, null, 1, 1);
-        else
-            gt.insertedge (gt, data.pobj, null, data.obj, null, null, 1);
-    };
-    'rightdown' = function (data) {
-        local vt, gt, menu, i;
-
-        vt = dotty.views[data.widget];
-        gt = dotty.graphs[vt.gtid];
-        if (~data.obj)
-            menu = vt.menus.general;
-        else if (data.obj.nid >= 0)
-            menu = vt.menus.node;
-        else if (data.obj.eid >= 0)
-            menu = vt.menus.edge;
-        if ((i = displaymenu (data.widget, menu)) >= 0)
-            gt.doaction (data, menu[i]);
-    };
-    'keyup' = function (data) {
-        local vt, gt, action;
-
-        vt = dotty.views[data.widget];
-        gt = dotty.graphs[vt.gtid];
-        if (data.obj.nid >= 0) {
-            if (vt.keys.node[data.key])
-                action = vt.keys.node[data.key];
-        } else if (data.obj.eid >= 0) {
-            if (vt.keys.edge[data.key])
-                action = vt.keys.edge[data.key];
-        }
-        if (~action)
-            if (vt.keys.general[data.key])
-                action = vt.keys.general[data.key];
-        if (action)
-            gt.doaction (data, action);
-    };
-    'redraw' = function (data) {
-        local vt, gt;
-
-        vt = dotty.views[data.widget];
-        gt = dotty.graphs[vt.gtid];
-        gt.drawgraph (gt, [vt.vtid = vt;]);
-    };
-    'closeview' = function (data) {
-        local vt, gt;
-
-        vt = dotty.views[data.widget];
-        gt = dotty.graphs[vt.gtid];
-        gt.destroyview (gt, vt);
-        if (tablesize (gt.views) == 0)
-            gt.destroygraph (gt);
-    };
-];
-dotty.protovt.birdseye.uifuncs = [
-    'leftdown' = function (data) {
-        local gt, vid;
-
-        gt = dotty.graphs[dotty.views[data.widget].gtid];
-        for (vid in gt.views) {
-            vt = gt.views[vid];
-            if (vt.type ~= 'birdseye')
-                gt.setviewcenter ([vid = vt;], data.pos);
-        }
-    };
-    'leftmove' = function (data) {
-        local gt, vid;
-
-        gt = dotty.graphs[dotty.views[data.widget].gtid];
-        for (vid in gt.views) {
-            vt = gt.views[vid];
-            if (vt.type ~= 'birdseye')
-                gt.setviewcenter ([vid = vt;], data.pos);
-        }
-    };
-    'leftup' = function (data) {
-        local gt, vid;
-
-        gt = dotty.graphs[dotty.views[data.widget].gtid];
-        for (vid in gt.views) {
-            vt = gt.views[vid];
-            if (vt.type ~= 'birdseye')
-                gt.setviewcenter ([vid = vt;], data.pos);
-        }
-    };
-    'middledown' = dotty.protovt.normal.uifuncs.middledown;
-    'middlemove' = dotty.protovt.normal.uifuncs.middlemove;
-    'middleup' = dotty.protovt.normal.uifuncs.middleup;
-    'rightdown' = dotty.protovt.normal.uifuncs.rightdown;
-    'keyup' = dotty.protovt.normal.uifuncs.keyup;
-    'redraw' = dotty.protovt.normal.uifuncs.redraw;
-    'closeview' = dotty.protovt.normal.uifuncs.closeview;
-];
-dotty.monitorfile = function (data) {
-    local gtid, gt, lpt;
-
-    for (gtid in dotty.layoutpending) {
-        lpt = dotty.layoutpending[gtid];
-        if (lpt.fd == data.fd) {
-            gt = dotty.graphs[lpt.gtid];
-            gt.haveinput = 1;
-            gt.layoutgraph (gt);
-            return 1;
-        }
-    }
-    return 0;
-};