2
# dotty_edit: editing functions and data structures
4
dotty.protogt.getnodesbyattr = function (gt, key, val) {
5
local nid, node, nlist;
8
for (nid in gt.graph.nodes) {
9
node = gt.graph.nodes[nid];
10
if (node.attr[key] == val)
15
dotty.protogt.reachablenodes = function (gt, node) {
16
local nlist, stack, eid, edge, i;
23
nlist[node.nid] = node;
24
for (eid in node.edges) {
25
edge = node.edges[eid];
26
if (~nlist[edge.head.nid]) {
27
nlist[edge.head.nid] = edge.head;
35
dotty.protogt.mergegraph = function (gt, graph, show) {
36
local nameid, onode, pos, size, eid, eid2, tnode, hnode, oedge;
39
gt.startadd2undo (gt);
40
for (nameid in graph.nodedict) {
43
onode = graph.nodes[graph.nodedict[nameid]];
48
if (~(gt.graph.nodedict[nameid] >= 0)) {
55
gt.insertnode (gt, pos, size, nameid, onode.attr, show);
58
for (eid in graph.edges) {
59
oedge = graph.edges[eid];
60
tnode = gt.graph.nodes[gt.graph.nodedict[oedge.tail.name]];
61
hnode = gt.graph.nodes[gt.graph.nodedict[oedge.head.name]];
62
for (eid2 in tnode.edges)
63
if (tnode.edges[eid2].tail == tnode &
64
tnode.edges[eid2].head == hnode) {
69
gt.insertedge (gt, tnode, null, hnode, null, oedge.attr, show);
74
dotty.protogt.insertsgraph = function (gt, name, attr, show) {
75
local gid, sgraph, aid;
79
gid = gt.graph.maxgid;
81
while (gt.graph.graphdict[(name = concat ('g', gid))] >= 0)
83
} else if (gt.graph.graphdict[name]) {
84
dotty.message (0, concat ('graph: ', name, ' exists'));
87
gt.graph.graphdict[name] = gid;
88
gt.graph.maxgid = gid + 1;
89
gt.graph.graphs[gid] = [
91
dotty.keys.name = name;
92
dotty.keys.gattr = copy (gt.graph.graphattr);
93
dotty.keys.nattr = copy (gt.graph.nodeattr);
94
dotty.keys.eattr = copy (gt.graph.edgeattr);
96
sgraph = gt.graph.graphs[gid];
102
sgraph.graphattr[aid] = attr[aid];
103
gt.unpacksgraphattr (gt, sgraph);
105
gt.drawsgraph (gt, gt.views, sgraph);
108
dotty.protogt.removesgraph = function (gt, sgraph) {
109
gt.undrawsgraph (gt, gt.views, sgraph);
110
remove (sgraph.name, gt.graph.graphdict);
111
remove (sgraph.gid, gt.graph.graphs);
113
dotty.protogt.insertnode = function (gt, pos, size, name, attr, show) {
114
local nid, node, aid;
116
nid = gt.graph.maxnid;
118
while (gt.graph.nodedict[(name = concat ('n', nid))] >= 0)
120
} else if (gt.graph.nodedict[name] >= 0) {
121
dotty.message (0, concat ('node: ', name, ' exists'));
124
gt.graph.nodedict[name] = nid;
125
gt.graph.maxnid = nid + 1;
126
gt.graph.nodes[nid] = [
127
dotty.keys.nid = nid;
128
dotty.keys.name = name;
129
dotty.keys.attr = copy (gt.graph.nodeattr);
130
dotty.keys.edges = [];
132
node = gt.graph.nodes[nid];
138
node.attr[aid] = attr[aid];
139
gt.unpacknodeattr (gt, node);
141
pos = ['x' = 10; 'y' = 10;];
142
node[dotty.keys.pos] = copy (pos);
144
size = ['x' = strlen (attr.label) * 30; 'y' = 30;];
147
node[dotty.keys.size] = copy (size);
149
gt.drawnode (gt, gt.views, node);
151
gt.startadd2undo (gt);
152
gt.currundo.inserted.nodes[nid] = node;
157
dotty.protogt.removenode = function (gt, node) {
158
local eid, list, edge, gid;
161
gt.startadd2undo (gt);
162
for (eid in node.edges)
163
list[eid] = node.edges[eid];
165
gt.removeedge (gt, list[eid]);
166
gt.undrawnode (gt, gt.views, node);
167
for (gid in gt.graph.graphs)
168
remove (node.nid, gt.graph.graphs[gid].nodes);
169
remove (node.name, gt.graph.nodedict);
170
remove (node.nid, gt.graph.nodes);
172
gt.currundo.deleted.nodes[node.nid] = node;
176
dotty.protogt.insertedge =
177
function (gt, nodea, porta, nodeb, portb, attr, show) {
178
local eid, edge, aid, tport, hport;
186
eid = gt.graph.maxeid;
187
while (gt.graph.edges[eid])
189
gt.graph.maxeid = eid + 1;
190
gt.graph.edges[eid] = [
191
dotty.keys.eid = eid;
192
dotty.keys.tail = nodea;
193
dotty.keys.tport = porta;
194
dotty.keys.head = nodeb;
195
dotty.keys.hport = portb;
196
dotty.keys.attr = copy (gt.graph.edgeattr);
198
edge = gt.graph.edges[eid];
202
edge.attr[aid] = attr[aid];
203
nodea.edges[eid] = edge;
204
nodeb.edges[eid] = edge;
205
edge[dotty.keys.points] = [
206
0 = copy (nodea.pos);
207
1 = copy (nodea.pos);
208
2 = copy (nodeb.pos);
209
3 = copy (nodeb.pos);
211
gt.unpackedgeattr (gt, edge);
213
gt.drawedge (gt, gt.views, edge);
215
gt.startadd2undo (gt);
216
gt.currundo.inserted.edges[eid] = edge;
221
dotty.protogt.removeedge = function (gt, edge) {
225
gt.startadd2undo (gt);
226
if (edge.head.attr.support == 1)
228
if (edge.tail.attr.support == 1)
229
if (head ~= edge.tail)
231
gt.undrawedge (gt, gt.views, edge);
232
remove (edge.eid, edge.head.edges);
233
remove (edge.eid, edge.tail.edges);
234
remove (edge.eid, gt.graph.edges);
235
if (head & tablesize (head.edges) == 0)
236
gt.removenode (gt, head);
237
if (tail & tablesize (tail.edges) == 0)
238
gt.removenode (gt, tail);
240
gt.currundo.deleted.edges[edge.eid] = edge;
244
dotty.protogt.swapedgeids = function (gt, edge1, edge2) {
247
if (edge1.eid == edge2.eid)
250
gt.startadd2undo (gt);
253
gt.graph.edges[eid1] = edge2;
254
gt.graph.edges[eid2] = edge1;
255
remove (eid1, edge1.tail.edges);
256
remove (eid1, edge1.head.edges);
257
remove (eid2, edge2.tail.edges);
258
remove (eid2, edge2.head.edges);
259
edge1.tail.edges[eid2] = edge1;
260
edge1.head.edges[eid2] = edge1;
261
edge2.tail.edges[eid1] = edge2;
262
edge2.head.edges[eid1] = edge2;
266
gt.currundo.swapped.edges[eid1] = edge1;
267
gt.currundo.swapped.edges[eid2] = edge2;
271
dotty.protogt.removesubtree = function (gt, obj) {
272
local nlist, node, head, nid, edge, eid;
275
gt.startadd2undo (gt);
278
else if (obj.eid >= 0) {
280
gt.removeedge (gt, obj);
281
if (~gt.graph.nodes[node.nid]) {
286
for (eid in node.edges) {
287
edge = node.edges[eid];
288
if (edge.head == node & edge.tail ~= node) {
295
dotty.message (0, 'bad object type in gt.removesubtree');
298
nlist = [node.nid = node;];
300
for (eid in node.edges) {
301
head = node.edges[eid].head;
303
nlist[head.nid] = head;
305
gt.removenode (gt, node);
306
remove (node.nid, nlist);
310
for (eid in node.edges) {
311
edge = node.edges[eid];
312
if (edge.head == node & edge.tail ~= node) {
324
dotty.protogt.removenodesbyattr = function (gt, key, val) {
328
gt.startadd2undo (gt);
329
nlist = gt.getnodesbyattr (gt, key, val);
331
gt.removenode (gt, nlist[nid]);
335
dotty.protogt.removesubtreesbyattr = function (gt, key, val) {
339
gt.startadd2undo (gt);
340
nlist = gt.getnodesbyattr (gt, key, val);
342
if (gt.graph.nodes[nid])
343
gt.removesubtree (gt, nlist[nid]);
347
dotty.protogt.groupnodes = function (gt, nlist, gnode, pos, size, attr,
349
local nid, node, elist, eid, edge, nodea, nodeb, inlist, outlist;
351
if (~nlist | tablesize (nlist) == 0)
353
if (gnode.attr.support) {
354
dotty.message (0, 'cannot group nodes in a support node');
358
gt.startadd2undo (gt);
360
gnode = gt.insertnode (gt, pos, size, null, attr, show);
364
if ((node = nlist[nid]) == gnode)
367
for (eid in node.edges)
368
elist[eid] = node.edges[eid];
371
if (edge.head == node) {
375
if (inlist[nodea.nid])
377
inlist[nodea.nid] = nodea;
379
outlist[nodea.nid] = nodea;
385
if (outlist[nodeb.nid])
387
outlist[nodeb.nid] = nodeb;
389
inlist[nodeb.nid] = nodeb;
392
gt.insertedge (gt, nodea, null, nodeb, null, edge.attr, show);
394
gt.removenode (gt, node);
400
dotty.protogt.groupnodesbyattr =
401
function (gt, key, val, attr, keepmulti, show) {
402
local nlist, nid, pos, size;
406
nlist = gt.getnodesbyattr (gt, key, val);
409
pos = nlist[nid].pos;
410
size = nlist[nid].size;
413
return gt.groupnodes (gt, nlist, null, pos, size, attr, keepmulti, show);
415
dotty.protogt.cut = function (gt, obj, set, mode, op) {
416
local clipgt, list, node, nid, edge, eid, clipnode;
418
clipgt = dotty.clipgt;
419
clipgt.graph = copy (dotty.protogt.graph);
420
if (obj.eid >= 0) { # it's an edge
421
list.edges[obj.eid] = obj;
423
} else if (obj.nid >= 0) {
424
list.nodes[obj.nid] = obj;
426
for (eid in node.edges)
427
list.edges[eid] = node.edges[eid];
429
dotty.message (0, 'unknown object type in gt.cut');
432
if (set == 'reachable') {
433
list.nodes = gt.reachablenodes (gt, node);
434
for (nid in list.nodes) {
435
node = list.nodes[nid];
436
for (eid in node.edges) {
437
edge = node.edges[eid];
438
list.edges[edge.eid] = edge;
442
if (mode == 'support') {
443
for (eid in list.edges) {
444
edge = list.edges[eid];
445
if (~list.nodes[edge.tail.nid]) {
446
list.support[edge.tail.nid] = edge.tail;
447
list.nodes[edge.tail.nid] = edge.tail;
449
if (~list.nodes[edge.head.nid]) {
450
list.support[edge.head.nid] = edge.head;
451
list.nodes[edge.head.nid] = edge.head;
455
for (nid = 0; nid < gt.graph.maxnid; nid = nid + 1) {
456
if (~list.nodes[nid])
458
node = list.nodes[nid];
459
clipnode = gt.insertnode (clipgt, null, null, node.name, node.attr, 0);
460
if (list.support[nid])
461
clipnode.support = 1;
462
list.clipnodes[nid] = clipnode;
464
for (eid = 0; eid < gt.graph.maxeid; eid = eid + 1) {
465
if (~list.edges[eid])
467
edge = list.edges[eid];
468
if (~list.nodes[edge.tail.nid] | ~list.nodes[edge.head.nid])
470
gt.insertedge (clipgt, list.clipnodes[edge.tail.nid], null,
471
list.clipnodes[edge.head.nid], null, edge.attr, 0);
476
gt.startadd2undo (gt);
477
for (eid in list.edges)
478
gt.removeedge (gt, list.edges[eid]);
479
for (nid in list.nodes)
480
if (~list.support[nid] & gt.graph.nodes[nid])
481
gt.removenode (gt, list.nodes[nid]);
485
dotty.protogt.paste = function (gt, pos, show) {
486
local clipgt, offset, center, nid, node, eid, edge, nodes;
489
gt.startadd2undo (gt);
490
clipgt = dotty.clipgt;
491
if (clipgt.graph.rect)
493
'x' = (clipgt.graph.rect[1].x + clipgt.graph.rect[0].x) / 2;
494
'y' = (clipgt.graph.rect[1].y + clipgt.graph.rect[0].y) / 2;
499
'x' = center.x - pos.x;
500
'y' = center.y - pos.y;
502
for (nid = 0; clipgt.graph.nodes[nid]; nid = nid + 1) {
503
node = clipgt.graph.nodes[nid];
504
if (node.attr.label == '\N' | ~node.attr.label)
505
node.attr.label = node.name;
506
if (node.support == 1)
507
nodes[nid] = gt.insertnode (gt, [
508
'x' = node.pos.x - offset.x;
509
'y' = node.pos.y - offset.y;
511
'support' = 1; 'shape' = 'circle';
512
'label' = ''; 'width' = 0.2;
515
nodes[nid] = gt.insertnode (gt, [
516
'x' = node.pos.x - offset.x;
517
'y' = node.pos.y - offset.y;
518
], node.size, null, node.attr, show);
520
for (eid = 0; clipgt.graph.edges[eid]; eid = eid + 1) {
521
edge = clipgt.graph.edges[eid];
522
gt.insertedge (gt, nodes[edge.tail.nid], null,
523
nodes[edge.head.nid], null, edge.attr, show);
528
dotty.protogt.startadd2undo = function (gt) {
529
if (~gt.undoarray.level)
531
(gt.undoarray.entries[tablesize (gt.undoarray.entries)] = []);
532
gt.undoarray.level = gt.undoarray.level + 1;
534
dotty.protogt.endadd2undo = function (gt) {
535
gt.undoarray.level = gt.undoarray.level - 1;
537
dotty.protogt.undo = function (gt, show) {
538
local entry, n, eid, edge, nid, node, edges;
540
if ((n = tablesize (gt.undoarray.entries)) < 1)
542
entry = gt.undoarray.entries[n - 1];
543
remove (n - 1, gt.undoarray.entries);
544
remove ('currundo', gt);
546
# hardwire nodes and edges back with the same id's as the originals
547
for (nid in entry.deleted.nodes) {
548
node = entry.deleted.nodes[nid];
549
gt.graph.nodedict[node.name] = node.nid;
550
gt.graph.nodes[node.nid] = node;
553
gt.drawnode (gt, gt.views, node);
555
for (eid in entry.deleted.edges) {
556
edge = entry.deleted.edges[eid];
557
gt.graph.edges[edge.eid] = edge;
558
edge.head.edges[edge.eid] = edge;
559
edge.tail.edges[edge.eid] = edge;
561
gt.drawedge (gt, gt.views, edge);
563
if (entry.swapped.edges) {
564
if (tablesize (entry.swapped.edges) == 2) {
566
for (eid in entry.swapped.edges) {
567
edges[n] = entry.swapped.edges[eid];
570
gt.swapedgeids (gt, edges[0], edges[1]);
572
dotty.message (0, 'cannot handle undoing swap of > 2 edges');
574
for (eid in entry.inserted.edges) {
575
edge = entry.inserted.edges[eid];
576
gt.removeedge (gt, edge);
578
for (nid in entry.inserted.nodes) {
579
node = entry.inserted.nodes[nid];
580
gt.removenode (gt, node);