wolffd@0: # wolffd@0: # dotty_edit: editing functions and data structures wolffd@0: # wolffd@0: dotty.protogt.getnodesbyattr = function (gt, key, val) { wolffd@0: local nid, node, nlist; wolffd@0: wolffd@0: nlist = []; wolffd@0: for (nid in gt.graph.nodes) { wolffd@0: node = gt.graph.nodes[nid]; wolffd@0: if (node.attr[key] == val) wolffd@0: nlist[nid] = node; wolffd@0: } wolffd@0: return nlist; wolffd@0: }; wolffd@0: dotty.protogt.reachablenodes = function (gt, node) { wolffd@0: local nlist, stack, eid, edge, i; wolffd@0: wolffd@0: stack[0] = node; wolffd@0: i = 1; wolffd@0: while (i > 0) { wolffd@0: node = stack[i - 1]; wolffd@0: i = i - 1; wolffd@0: nlist[node.nid] = node; wolffd@0: for (eid in node.edges) { wolffd@0: edge = node.edges[eid]; wolffd@0: if (~nlist[edge.head.nid]) { wolffd@0: nlist[edge.head.nid] = edge.head; wolffd@0: stack[i] = edge.head; wolffd@0: i = i + 1; wolffd@0: } wolffd@0: } wolffd@0: } wolffd@0: return nlist; wolffd@0: }; wolffd@0: dotty.protogt.mergegraph = function (gt, graph, show) { wolffd@0: local nameid, onode, pos, size, eid, eid2, tnode, hnode, oedge; wolffd@0: wolffd@0: if (~gt.noundo) wolffd@0: gt.startadd2undo (gt); wolffd@0: for (nameid in graph.nodedict) { wolffd@0: pos = null; wolffd@0: size = null; wolffd@0: onode = graph.nodes[graph.nodedict[nameid]]; wolffd@0: if (onode.pos) wolffd@0: pos = node.pos; wolffd@0: if (onode.size) wolffd@0: size = node.size; wolffd@0: if (~(gt.graph.nodedict[nameid] >= 0)) { wolffd@0: pos = null; wolffd@0: size = null; wolffd@0: if (onode.pos) wolffd@0: pos = node.pos; wolffd@0: if (onode.size) wolffd@0: size = node.size; wolffd@0: gt.insertnode (gt, pos, size, nameid, onode.attr, show); wolffd@0: } wolffd@0: } wolffd@0: for (eid in graph.edges) { wolffd@0: oedge = graph.edges[eid]; wolffd@0: tnode = gt.graph.nodes[gt.graph.nodedict[oedge.tail.name]]; wolffd@0: hnode = gt.graph.nodes[gt.graph.nodedict[oedge.head.name]]; wolffd@0: for (eid2 in tnode.edges) wolffd@0: if ( wolffd@0: tnode.edges[eid2].tail == tnode & wolffd@0: tnode.edges[eid2].head == hnode wolffd@0: ) { wolffd@0: oedge = null; wolffd@0: break; wolffd@0: } wolffd@0: if (oedge) wolffd@0: gt.insertedge (gt, tnode, null, hnode, null, oedge.attr, show); wolffd@0: } wolffd@0: if (~gt.noundo) wolffd@0: gt.endadd2undo (gt); wolffd@0: }; wolffd@0: dotty.protogt.insertsgraph = function (gt, name, attr, show) { wolffd@0: local gid, sgraph, aid; wolffd@0: wolffd@0: if (~gt) wolffd@0: return null; wolffd@0: gid = gt.graph.maxgid; wolffd@0: if (~name) { wolffd@0: while (gt.graph.graphdict[(name = concat ('g', gid))] >= 0) wolffd@0: gid = gid + 1; wolffd@0: } else if (gt.graph.graphdict[name]) { wolffd@0: dotty.message (0, concat ('graph: ', name, ' exists')); wolffd@0: return null; wolffd@0: } wolffd@0: gt.graph.graphdict[name] = gid; wolffd@0: gt.graph.maxgid = gid + 1; wolffd@0: gt.graph.graphs[gid] = [ wolffd@0: dotty.keys.gid = gid; wolffd@0: dotty.keys.name = name; wolffd@0: dotty.keys.gattr = copy (gt.graph.graphattr); wolffd@0: dotty.keys.nattr = copy (gt.graph.nodeattr); wolffd@0: dotty.keys.eattr = copy (gt.graph.edgeattr); wolffd@0: ]; wolffd@0: sgraph = gt.graph.graphs[gid]; wolffd@0: if (~attr) wolffd@0: attr = []; wolffd@0: if (~attr.label) wolffd@0: attr.label = '\N'; wolffd@0: for (aid in attr) wolffd@0: sgraph.graphattr[aid] = attr[aid]; wolffd@0: gt.unpacksgraphattr (gt, sgraph); wolffd@0: if (show) wolffd@0: gt.drawsgraph (gt, gt.views, sgraph); wolffd@0: return sgraph; wolffd@0: }; wolffd@0: dotty.protogt.removesgraph = function (gt, sgraph) { wolffd@0: gt.undrawsgraph (gt, gt.views, sgraph); wolffd@0: remove (sgraph.name, gt.graph.graphdict); wolffd@0: remove (sgraph.gid, gt.graph.graphs); wolffd@0: }; wolffd@0: dotty.protogt.insertnode = function (gt, pos, size, name, attr, show) { wolffd@0: local nid, node, aid; wolffd@0: wolffd@0: nid = gt.graph.maxnid; wolffd@0: if (~name) { wolffd@0: while (gt.graph.nodedict[(name = concat ('n', nid))] >= 0) wolffd@0: nid = nid + 1; wolffd@0: } else if (gt.graph.nodedict[name] >= 0) { wolffd@0: dotty.message (0, concat ('node: ', name, ' exists')); wolffd@0: return null; wolffd@0: } wolffd@0: gt.graph.nodedict[name] = nid; wolffd@0: gt.graph.maxnid = nid + 1; wolffd@0: gt.graph.nodes[nid] = [ wolffd@0: dotty.keys.nid = nid; wolffd@0: dotty.keys.name = name; wolffd@0: dotty.keys.attr = copy (gt.graph.nodeattr); wolffd@0: dotty.keys.edges = []; wolffd@0: ]; wolffd@0: node = gt.graph.nodes[nid]; wolffd@0: if (~attr) wolffd@0: attr = []; wolffd@0: if (~attr.label) wolffd@0: attr.label = '\N'; wolffd@0: for (aid in attr) wolffd@0: node.attr[aid] = attr[aid]; wolffd@0: gt.unpacknodeattr (gt, node); wolffd@0: if (~pos) wolffd@0: pos = ['x' = 10; 'y' = 10;]; wolffd@0: node[dotty.keys.pos] = copy (pos); wolffd@0: if (~size) wolffd@0: size = ['x' = strlen (attr.label) * 30; 'y' = 30;]; wolffd@0: if (size.x == 0) wolffd@0: size.x = 30; wolffd@0: node[dotty.keys.size] = copy (size); wolffd@0: node[dotty.keys.rect] = [ wolffd@0: 0 = ['x' = pos.x - size.x / 2; 'y' = pos.y - size.y / 2;]; wolffd@0: 1 = ['x' = pos.x + size.x / 2; 'y' = pos.y + size.y / 2;]; wolffd@0: ]; wolffd@0: node.draws = gt.simplenodedraw (node, pos, size); wolffd@0: if (show) wolffd@0: gt.drawnode (gt, gt.views, node); wolffd@0: if (~gt.noundo) { wolffd@0: gt.startadd2undo (gt); wolffd@0: gt.currundo.inserted.nodes[nid] = node; wolffd@0: gt.endadd2undo (gt); wolffd@0: } wolffd@0: return node; wolffd@0: }; wolffd@0: dotty.protogt.removenode = function (gt, node) { wolffd@0: local eid, list, edge, gid; wolffd@0: wolffd@0: if (~gt.noundo) wolffd@0: gt.startadd2undo (gt); wolffd@0: for (eid in node.edges) wolffd@0: list[eid] = node.edges[eid]; wolffd@0: for (eid in list) wolffd@0: gt.removeedge (gt, list[eid]); wolffd@0: gt.undrawnode (gt, gt.views, node); wolffd@0: for (gid in gt.graph.graphs) wolffd@0: remove (node.nid, gt.graph.graphs[gid].nodes); wolffd@0: remove (node.name, gt.graph.nodedict); wolffd@0: remove (node.nid, gt.graph.nodes); wolffd@0: if (~gt.noundo) { wolffd@0: gt.currundo.deleted.nodes[node.nid] = node; wolffd@0: gt.endadd2undo (gt); wolffd@0: } wolffd@0: }; wolffd@0: dotty.protogt.insertedge = function ( wolffd@0: gt, nodea, porta, nodeb, portb, attr, show wolffd@0: ) { wolffd@0: local eid, edge, aid, tport, hport; wolffd@0: wolffd@0: if (~nodea | ~nodeb) wolffd@0: return null; wolffd@0: if (porta) wolffd@0: tport = porta; wolffd@0: if (portb) wolffd@0: hport = portb; wolffd@0: eid = gt.graph.maxeid; wolffd@0: while (gt.graph.edges[eid]) wolffd@0: eid = eid + 1; wolffd@0: gt.graph.maxeid = eid + 1; wolffd@0: gt.graph.edges[eid] = [ wolffd@0: dotty.keys.eid = eid; wolffd@0: dotty.keys.tail = nodea; wolffd@0: dotty.keys.tport = porta; wolffd@0: dotty.keys.head = nodeb; wolffd@0: dotty.keys.hport = portb; wolffd@0: dotty.keys.attr = copy (gt.graph.edgeattr); wolffd@0: ]; wolffd@0: edge = gt.graph.edges[eid]; wolffd@0: if (~attr) wolffd@0: attr = []; wolffd@0: for (aid in attr) wolffd@0: edge.attr[aid] = attr[aid]; wolffd@0: nodea.edges[eid] = edge; wolffd@0: nodeb.edges[eid] = edge; wolffd@0: gt.unpackedgeattr (gt, edge); wolffd@0: edge.draws = gt.simpleedgedraw (edge, nodea.pos, nodeb.pos); wolffd@0: if (show) wolffd@0: gt.drawedge (gt, gt.views, edge); wolffd@0: if (~gt.noundo) { wolffd@0: gt.startadd2undo (gt); wolffd@0: gt.currundo.inserted.edges[eid] = edge; wolffd@0: gt.endadd2undo (gt); wolffd@0: } wolffd@0: return edge; wolffd@0: }; wolffd@0: dotty.protogt.removeedge = function (gt, edge) { wolffd@0: local head, tail; wolffd@0: wolffd@0: if (~gt.noundo) wolffd@0: gt.startadd2undo (gt); wolffd@0: if (edge.head.attr.support == 1) wolffd@0: head = edge.head; wolffd@0: if (edge.tail.attr.support == 1) wolffd@0: if (head ~= edge.tail) wolffd@0: tail = edge.tail; wolffd@0: gt.undrawedge (gt, gt.views, edge); wolffd@0: remove (edge.eid, edge.head.edges); wolffd@0: remove (edge.eid, edge.tail.edges); wolffd@0: remove (edge.eid, gt.graph.edges); wolffd@0: if (head & tablesize (head.edges) == 0) wolffd@0: gt.removenode (gt, head); wolffd@0: if (tail & tablesize (tail.edges) == 0) wolffd@0: gt.removenode (gt, tail); wolffd@0: if (~gt.noundo) { wolffd@0: gt.currundo.deleted.edges[edge.eid] = edge; wolffd@0: gt.endadd2undo (gt); wolffd@0: } wolffd@0: }; wolffd@0: dotty.protogt.swapedgeids = function (gt, edge1, edge2) { wolffd@0: local eid1, eid2; wolffd@0: wolffd@0: if (edge1.eid == edge2.eid) wolffd@0: return; wolffd@0: if (~gt.noundo) wolffd@0: gt.startadd2undo (gt); wolffd@0: eid1 = edge1.eid; wolffd@0: eid2 = edge2.eid; wolffd@0: gt.graph.edges[eid1] = edge2; wolffd@0: gt.graph.edges[eid2] = edge1; wolffd@0: remove (eid1, edge1.tail.edges); wolffd@0: remove (eid1, edge1.head.edges); wolffd@0: remove (eid2, edge2.tail.edges); wolffd@0: remove (eid2, edge2.head.edges); wolffd@0: edge1.tail.edges[eid2] = edge1; wolffd@0: edge1.head.edges[eid2] = edge1; wolffd@0: edge2.tail.edges[eid1] = edge2; wolffd@0: edge2.head.edges[eid1] = edge2; wolffd@0: edge1.eid = eid2; wolffd@0: edge2.eid = eid1; wolffd@0: if (~gt.noundo) { wolffd@0: gt.currundo.swapped.edges[eid1] = edge1; wolffd@0: gt.currundo.swapped.edges[eid2] = edge2; wolffd@0: gt.endadd2undo (gt); wolffd@0: } wolffd@0: }; wolffd@0: dotty.protogt.removesubtree = function (gt, obj) { wolffd@0: local nlist, node, head, nid, edge, eid; wolffd@0: wolffd@0: if (~gt.noundo) wolffd@0: gt.startadd2undo (gt); wolffd@0: if (obj.nid >= 0) wolffd@0: node = obj; wolffd@0: else if (obj.eid >= 0) { wolffd@0: node = obj.head; wolffd@0: gt.removeedge (gt, obj); wolffd@0: if (~gt.graph.nodes[node.nid]) { wolffd@0: if (~gt.noundo) wolffd@0: gt.endadd2undo (gt); wolffd@0: return; wolffd@0: } wolffd@0: for (eid in node.edges) { wolffd@0: edge = node.edges[eid]; wolffd@0: if (edge.head == node & edge.tail ~= node) { wolffd@0: if (~gt.noundo) wolffd@0: gt.endadd2undo (gt); wolffd@0: return; wolffd@0: } wolffd@0: } wolffd@0: } else { wolffd@0: dotty.message (0, 'bad object type in gt.removesubtree'); wolffd@0: return; wolffd@0: } wolffd@0: nlist = [node.nid = node;]; wolffd@0: while (node) { wolffd@0: for (eid in node.edges) { wolffd@0: head = node.edges[eid].head; wolffd@0: if (head ~= node) wolffd@0: nlist[head.nid] = head; wolffd@0: } wolffd@0: gt.removenode (gt, node); wolffd@0: remove (node.nid, nlist); wolffd@0: node = null; wolffd@0: for (nid in nlist) { wolffd@0: node = nlist[nid]; wolffd@0: for (eid in node.edges) { wolffd@0: edge = node.edges[eid]; wolffd@0: if (edge.head == node & edge.tail ~= node) { wolffd@0: node = null; wolffd@0: break; wolffd@0: } wolffd@0: } wolffd@0: if (node) wolffd@0: break; wolffd@0: } wolffd@0: } wolffd@0: if (~gt.noundo) wolffd@0: gt.endadd2undo (gt); wolffd@0: }; wolffd@0: dotty.protogt.removenodesbyattr = function (gt, key, val) { wolffd@0: local nlist, nid; wolffd@0: wolffd@0: if (~gt.noundo) wolffd@0: gt.startadd2undo (gt); wolffd@0: nlist = gt.getnodesbyattr (gt, key, val); wolffd@0: for (nid in nlist) wolffd@0: gt.removenode (gt, nlist[nid]); wolffd@0: if (~gt.noundo) wolffd@0: gt.endadd2undo (gt); wolffd@0: }; wolffd@0: dotty.protogt.removesubtreesbyattr = function (gt, key, val) { wolffd@0: local nlist, nid; wolffd@0: wolffd@0: if (~gt.noundo) wolffd@0: gt.startadd2undo (gt); wolffd@0: nlist = gt.getnodesbyattr (gt, key, val); wolffd@0: for (nid in nlist) wolffd@0: if (gt.graph.nodes[nid]) wolffd@0: gt.removesubtree (gt, nlist[nid]); wolffd@0: if (~gt.noundo) wolffd@0: gt.endadd2undo (gt); wolffd@0: }; wolffd@0: dotty.protogt.groupnodes = function ( wolffd@0: gt, nlist, gnode, pos, size, attr, keepmulti, show wolffd@0: ) { wolffd@0: local nid, node, elist, eid, edge, nodea, nodeb, inlist, outlist; wolffd@0: wolffd@0: if (~nlist | tablesize (nlist) == 0) wolffd@0: return; wolffd@0: if (gnode.attr.support) { wolffd@0: dotty.message (0, 'cannot group nodes in a support node'); wolffd@0: return; wolffd@0: } wolffd@0: if (~gt.noundo) wolffd@0: gt.startadd2undo (gt); wolffd@0: if (~gnode) wolffd@0: gnode = gt.insertnode (gt, pos, size, null, attr, show); wolffd@0: inlist = []; wolffd@0: outlist = []; wolffd@0: for (nid in nlist) { wolffd@0: if ((node = nlist[nid]) == gnode) wolffd@0: continue; wolffd@0: elist = []; wolffd@0: for (eid in node.edges) wolffd@0: elist[eid] = node.edges[eid]; wolffd@0: for (eid in elist) { wolffd@0: edge = elist[eid]; wolffd@0: if (edge.head == node) { wolffd@0: nodea = edge.tail; wolffd@0: nodeb = gnode; wolffd@0: if (~keepmulti) { wolffd@0: if (inlist[nodea.nid]) wolffd@0: continue; wolffd@0: inlist[nodea.nid] = nodea; wolffd@0: if (nodea == gnode) wolffd@0: outlist[nodea.nid] = nodea; wolffd@0: } wolffd@0: } else { wolffd@0: nodea = gnode; wolffd@0: nodeb = edge.head; wolffd@0: if (~keepmulti) { wolffd@0: if (outlist[nodeb.nid]) wolffd@0: continue; wolffd@0: outlist[nodeb.nid] = nodeb; wolffd@0: if (nodeb == gnode) wolffd@0: inlist[nodeb.nid] = nodeb; wolffd@0: } wolffd@0: } wolffd@0: gt.insertedge (gt, nodea, null, nodeb, null, edge.attr, show); wolffd@0: } wolffd@0: gt.removenode (gt, node); wolffd@0: } wolffd@0: if (~gt.noundo) wolffd@0: gt.endadd2undo (gt); wolffd@0: return gnode; wolffd@0: }; wolffd@0: dotty.protogt.groupnodesbyattr = function ( wolffd@0: gt, key, val, attr, keepmulti, show wolffd@0: ) { wolffd@0: local nlist, nid, pos, size; wolffd@0: wolffd@0: pos = null; wolffd@0: size = null; wolffd@0: nlist = gt.getnodesbyattr (gt, key, val); wolffd@0: if (show) wolffd@0: for (nid in nlist) { wolffd@0: pos = nlist[nid].pos; wolffd@0: size = nlist[nid].size; wolffd@0: break; wolffd@0: } wolffd@0: return gt.groupnodes (gt, nlist, null, pos, size, attr, keepmulti, show); wolffd@0: }; wolffd@0: dotty.protogt.cut = function (gt, obj, set, mode, op) { wolffd@0: local clipgt, list, node, nid, edge, eid, clipnode; wolffd@0: wolffd@0: clipgt = dotty.clipgt; wolffd@0: clipgt.graph = copy (dotty.protogt.graph); wolffd@0: if (obj.eid >= 0) { # it's an edge wolffd@0: list.edges[obj.eid] = obj; wolffd@0: node = obj.head; wolffd@0: } else if (obj.nid >= 0) { wolffd@0: list.nodes[obj.nid] = obj; wolffd@0: node = obj; wolffd@0: for (eid in node.edges) wolffd@0: list.edges[eid] = node.edges[eid]; wolffd@0: } else { wolffd@0: dotty.message (0, 'unknown object type in gt.cut'); wolffd@0: return; wolffd@0: } wolffd@0: if (set == 'reachable') { wolffd@0: list.nodes = gt.reachablenodes (gt, node); wolffd@0: for (nid in list.nodes) { wolffd@0: node = list.nodes[nid]; wolffd@0: for (eid in node.edges) { wolffd@0: edge = node.edges[eid]; wolffd@0: list.edges[edge.eid] = edge; wolffd@0: } wolffd@0: } wolffd@0: } wolffd@0: if (mode == 'support') { wolffd@0: for (eid in list.edges) { wolffd@0: edge = list.edges[eid]; wolffd@0: if (~list.nodes[edge.tail.nid]) { wolffd@0: list.support[edge.tail.nid] = edge.tail; wolffd@0: list.nodes[edge.tail.nid] = edge.tail; wolffd@0: } wolffd@0: if (~list.nodes[edge.head.nid]) { wolffd@0: list.support[edge.head.nid] = edge.head; wolffd@0: list.nodes[edge.head.nid] = edge.head; wolffd@0: } wolffd@0: } wolffd@0: } wolffd@0: for (nid = 0; nid < gt.graph.maxnid; nid = nid + 1) { wolffd@0: if (~list.nodes[nid]) wolffd@0: continue; wolffd@0: node = list.nodes[nid]; wolffd@0: clipnode = gt.insertnode (clipgt, null, null, node.name, node.attr, 0); wolffd@0: if (list.support[nid]) wolffd@0: clipnode.support = 1; wolffd@0: list.clipnodes[nid] = clipnode; wolffd@0: } wolffd@0: for (eid = 0; eid < gt.graph.maxeid; eid = eid + 1) { wolffd@0: if (~list.edges[eid]) wolffd@0: continue; wolffd@0: edge = list.edges[eid]; wolffd@0: if (~list.nodes[edge.tail.nid] | ~list.nodes[edge.head.nid]) wolffd@0: continue; wolffd@0: gt.insertedge ( wolffd@0: clipgt, list.clipnodes[edge.tail.nid], null, wolffd@0: list.clipnodes[edge.head.nid], null, edge.attr, 0 wolffd@0: ); wolffd@0: } wolffd@0: if (op ~= 'cut') wolffd@0: return; wolffd@0: if (~gt.noundo) wolffd@0: gt.startadd2undo (gt); wolffd@0: for (eid in list.edges) wolffd@0: gt.removeedge (gt, list.edges[eid]); wolffd@0: for (nid in list.nodes) wolffd@0: if (~list.support[nid] & gt.graph.nodes[nid]) wolffd@0: gt.removenode (gt, list.nodes[nid]); wolffd@0: if (~gt.noundo) wolffd@0: gt.endadd2undo (gt); wolffd@0: }; wolffd@0: dotty.protogt.paste = function (gt, pos, show) { wolffd@0: local clipgt, offset, center, nid, node, eid, edge, nodes; wolffd@0: wolffd@0: if (~gt.noundo) wolffd@0: gt.startadd2undo (gt); wolffd@0: clipgt = dotty.clipgt; wolffd@0: if (clipgt.graph.rect) wolffd@0: center = [ wolffd@0: 'x' = (clipgt.graph.rect[1].x + clipgt.graph.rect[0].x) / 2; wolffd@0: 'y' = (clipgt.graph.rect[1].y + clipgt.graph.rect[0].y) / 2; wolffd@0: ]; wolffd@0: else wolffd@0: center = pos; wolffd@0: offset = [ wolffd@0: 'x' = center.x - pos.x; wolffd@0: 'y' = center.y - pos.y; wolffd@0: ]; wolffd@0: for (nid = 0; clipgt.graph.nodes[nid]; nid = nid + 1) { wolffd@0: node = clipgt.graph.nodes[nid]; wolffd@0: if (node.attr.label == '\N' | ~node.attr.label) wolffd@0: node.attr.label = node.name; wolffd@0: if (node.support == 1) wolffd@0: nodes[nid] = gt.insertnode (gt, [ wolffd@0: 'x' = node.pos.x - offset.x; wolffd@0: 'y' = node.pos.y - offset.y; wolffd@0: ], null, null, [ wolffd@0: 'support' = 1; 'shape' = 'circle'; wolffd@0: 'label' = ''; 'width' = 0.2; wolffd@0: ], show); wolffd@0: else wolffd@0: nodes[nid] = gt.insertnode (gt, [ wolffd@0: 'x' = node.pos.x - offset.x; wolffd@0: 'y' = node.pos.y - offset.y; wolffd@0: ], node.size, null, node.attr, show); wolffd@0: } wolffd@0: for (eid = 0; clipgt.graph.edges[eid]; eid = eid + 1) { wolffd@0: edge = clipgt.graph.edges[eid]; wolffd@0: gt.insertedge ( wolffd@0: gt, nodes[edge.tail.nid], null, wolffd@0: nodes[edge.head.nid], null, edge.attr, show wolffd@0: ); wolffd@0: } wolffd@0: if (~gt.noundo) wolffd@0: gt.endadd2undo (gt); wolffd@0: }; wolffd@0: dotty.protogt.startadd2undo = function (gt) { wolffd@0: if (~gt.undoarray.level) wolffd@0: gt.currundo = ( wolffd@0: gt.undoarray.entries[tablesize (gt.undoarray.entries)] = [] wolffd@0: ); wolffd@0: gt.undoarray.level = gt.undoarray.level + 1; wolffd@0: }; wolffd@0: dotty.protogt.endadd2undo = function (gt) { wolffd@0: gt.undoarray.level = gt.undoarray.level - 1; wolffd@0: }; wolffd@0: dotty.protogt.undo = function (gt, show) { wolffd@0: local entry, n, eid, edge, nid, node, edges; wolffd@0: wolffd@0: if ((n = tablesize (gt.undoarray.entries)) < 1) wolffd@0: return; wolffd@0: entry = gt.undoarray.entries[n - 1]; wolffd@0: remove (n - 1, gt.undoarray.entries); wolffd@0: remove ('currundo', gt); wolffd@0: gt.noundo = 1; wolffd@0: # hardwire nodes and edges back with the same id's as the originals wolffd@0: for (nid in entry.deleted.nodes) { wolffd@0: node = entry.deleted.nodes[nid]; wolffd@0: gt.graph.nodedict[node.name] = node.nid; wolffd@0: gt.graph.nodes[node.nid] = node; wolffd@0: node.edges = []; wolffd@0: if (show) wolffd@0: gt.drawnode (gt, gt.views, node); wolffd@0: } wolffd@0: for (eid in entry.deleted.edges) { wolffd@0: edge = entry.deleted.edges[eid]; wolffd@0: gt.graph.edges[edge.eid] = edge; wolffd@0: edge.head.edges[edge.eid] = edge; wolffd@0: edge.tail.edges[edge.eid] = edge; wolffd@0: if (show) wolffd@0: gt.drawedge (gt, gt.views, edge); wolffd@0: } wolffd@0: if (entry.swapped.edges) { wolffd@0: if (tablesize (entry.swapped.edges) == 2) { wolffd@0: n = 0; wolffd@0: for (eid in entry.swapped.edges) { wolffd@0: edges[n] = entry.swapped.edges[eid]; wolffd@0: n = n + 1; wolffd@0: } wolffd@0: gt.swapedgeids (gt, edges[0], edges[1]); wolffd@0: } else wolffd@0: dotty.message (0, 'cannot handle undoing swap of > 2 edges'); wolffd@0: } wolffd@0: for (eid in entry.inserted.edges) { wolffd@0: edge = entry.inserted.edges[eid]; wolffd@0: gt.removeedge (gt, edge); wolffd@0: } wolffd@0: for (nid in entry.inserted.nodes) { wolffd@0: node = entry.inserted.nodes[nid]; wolffd@0: gt.removenode (gt, node); wolffd@0: } wolffd@0: gt.noundo = 0; wolffd@0: };