Daniel@0: # Daniel@0: # DOTTY Daniel@0: # Daniel@0: dotty = [ Daniel@0: 'keys' = [ Daniel@0: 'nid' = 'nid'; Daniel@0: 'eid' = 'eid'; Daniel@0: 'gid' = 'gid'; Daniel@0: 'name' = 'name'; Daniel@0: 'attr' = 'attr'; Daniel@0: 'gattr' = 'graphattr'; Daniel@0: 'eattr' = 'edgeattr'; Daniel@0: 'nattr' = 'nodeattr'; Daniel@0: 'edges' = 'edges'; Daniel@0: 'tail' = 'tail'; Daniel@0: 'tport' = 'tport'; Daniel@0: 'head' = 'head'; Daniel@0: 'hport' = 'hport'; Daniel@0: 'pos' = 'pos'; Daniel@0: 'size' = 'size'; Daniel@0: 'rect' = 'rect'; Daniel@0: 'fname' = 'fontname'; Daniel@0: 'fsize' = 'fontsize'; Daniel@0: 'fcolor' = 'fontcolor'; Daniel@0: 'dcolor' = 'drawcolor'; Daniel@0: 'bcolor' = 'fillcolor'; Daniel@0: ]; Daniel@0: 'maps' = [ Daniel@0: 'X11' = [ Daniel@0: 'fontmap' = [ Daniel@0: 'Times-Roman' = '-*-times-medium-r-*--%d-*-*-*-*-*-*-1'; Daniel@0: 'Times-Italic' = '-*-times-medium-i-*--%d-*-*-*-*-*-*-1'; Daniel@0: 'Times-Bold' = '-*-times-bold-r-*--%d-*-*-*-*-*-*-1'; Daniel@0: 'Courier' = '-*-courier-bold-r-*--%d-*-*-*-*-*-*-1'; Daniel@0: 'Courier-Bold' = '-*-courier-bold-r-*--%d-*-*-*-*-*-*-1'; Daniel@0: 'Helvetica' = ( Daniel@0: '-*-helvetica-medium-r-normal--%d-*-*-*-p-*-iso8859-1' Daniel@0: ); Daniel@0: 'Helvetica-Bold' = ( Daniel@0: '-*-helvetica-bold-r-normal--%d-*-*-*-p-*-iso8859-1' Daniel@0: ); Daniel@0: ]; Daniel@0: 'psfontmap' = [ Daniel@0: 'Times-Roman' = 'Times-Roman'; Daniel@0: 'Times-Italic' = 'Times-Italic'; Daniel@0: 'Times-Bold' = 'Times-Bold'; Daniel@0: 'Courier' = 'Courier'; Daniel@0: 'Courier-Bold' = 'Courier-Bold'; Daniel@0: 'Helvetica' = 'Helvetica'; Daniel@0: 'Helvetica-Bold' = 'Helvetica-Bold'; Daniel@0: ]; Daniel@0: ]; Daniel@0: 'mswin' = [ Daniel@0: 'fontmap' = [ Daniel@0: 'Times-Roman' = 'Times New Roman'; Daniel@0: 'Times-Italic' = 'Times New Roman Italic'; Daniel@0: 'Times-Bold' = 'Times New Roman Bold'; Daniel@0: 'Courier' = 'Courier New'; Daniel@0: 'Courier-Bold' = 'Courier New Bold'; Daniel@0: 'Helvetica' = 'Arial'; Daniel@0: 'Helvetica-Bold' = 'Arial Bold'; Daniel@0: ]; Daniel@0: 'psfontmap' = [ Daniel@0: 'Times-Roman' = 'Times New Roman'; Daniel@0: 'Times-Italic' = 'Times New Roman Italic'; Daniel@0: 'Times-Bold' = 'Times New Roman Bold'; Daniel@0: 'Courier' = 'Courier New'; Daniel@0: 'Courier-Bold' = 'Courier New Bold'; Daniel@0: 'Helvetica' = 'Arial'; Daniel@0: 'Helvetica-Bold' = 'Arial Bold'; Daniel@0: ]; Daniel@0: ]; Daniel@0: ]; Daniel@0: 'protogt' = [ Daniel@0: 'graph' = [ Daniel@0: 'graphattr' = [ Daniel@0: 'fontsize' = '14'; Daniel@0: 'fontname' = 'Times-Roman'; Daniel@0: 'fontcolor' = 'black'; Daniel@0: ]; Daniel@0: 'nodeattr' = [ Daniel@0: 'shape' = 'ellipse'; Daniel@0: 'fontsize' = '14'; Daniel@0: 'fontname' = 'Times-Roman'; Daniel@0: 'fontcolor' = 'black'; Daniel@0: 'style' = 'solid'; Daniel@0: ]; Daniel@0: 'edgeattr' = [ Daniel@0: 'fontsize' = '14'; Daniel@0: 'fontname' = 'Times-Roman'; Daniel@0: 'fontcolor' = 'black'; Daniel@0: 'style' = 'solid'; Daniel@0: ]; Daniel@0: 'graphdict' = []; Daniel@0: 'nodedict' = []; Daniel@0: 'graphs' = []; Daniel@0: 'nodes' = []; Daniel@0: 'edges' = []; Daniel@0: 'maxgid' = 0; Daniel@0: 'maxnid' = 0; Daniel@0: 'maxeid' = 0; Daniel@0: 'type' = 'digraph'; Daniel@0: ]; Daniel@0: 'layoutmode' = 'sync'; Daniel@0: 'lserver' = 'dot'; Daniel@0: 'edgehandles' = 1; Daniel@0: 'noundo' = 0; Daniel@0: ]; Daniel@0: 'lservers' = []; Daniel@0: 'mlevel' = 0; Daniel@0: 'graphs' = []; Daniel@0: 'views' = []; Daniel@0: 'protovt' = [ Daniel@0: 'normal' = [ Daniel@0: 'name' = 'DOTTY'; Daniel@0: 'orig' = ['x' = 1; 'y' = 1;]; Daniel@0: 'size' = ['x' = 420; 'y' = 520;]; Daniel@0: 'wrect' = [ Daniel@0: 0 = ['x' = 0; 'y' = 0;]; Daniel@0: 1 = ['x' = 400; 'y' = 500;]; Daniel@0: ]; Daniel@0: 'vsize' = ['x' = 400; 'y' = 500;]; Daniel@0: 'w2v' = 1; Daniel@0: ]; Daniel@0: 'birdseye' = [ Daniel@0: 'type' = 'birdseye'; Daniel@0: 'name' = 'DOTTY birdseye view'; Daniel@0: 'orig' = ['x' = 1; 'y' = 1;]; Daniel@0: 'size' = ['x' = 220; 'y' = 260;]; Daniel@0: 'wrect' = [ Daniel@0: 0 = ['x' = 0; 'y' = 0;]; Daniel@0: 1 = ['x' = 200; 'y' = 250;]; Daniel@0: ]; Daniel@0: 'vsize' = ['x' = 200; 'y' = 250;]; Daniel@0: 'w2v' = 1; Daniel@0: ]; Daniel@0: ]; Daniel@0: 'pagesizes' = [ Daniel@0: '8.5x11' = ['x' = 8; 'y' = 10.5;]; Daniel@0: '11x17' = ['x' = 10.5; 'y' = 16.5;]; Daniel@0: '36x50' = ['x' = 35.5; 'y' = 49.5;]; Daniel@0: ]; Daniel@0: ]; Daniel@0: load ('dotty_draw.lefty'); Daniel@0: load ('dotty_edit.lefty'); Daniel@0: load ('dotty_layout.lefty'); Daniel@0: load ('dotty_ui.lefty'); Daniel@0: # Daniel@0: # initialization functions Daniel@0: # Daniel@0: dotty.init = function () { Daniel@0: dotty.fontmap = dotty.maps[getenv ('LEFTYWINSYS')].fontmap; Daniel@0: dotty.clipgt = dotty.protogt.creategraph (['noundo' = 1;]); Daniel@0: dotty.inited = 1; Daniel@0: }; Daniel@0: dotty.simple = function (file) { Daniel@0: if (dotty.inited ~= 1) Daniel@0: dotty.init (); Daniel@0: dotty.createviewandgraph (file, 'file', null, null); Daniel@0: txtview ('off'); Daniel@0: }; Daniel@0: # Daniel@0: # main operations Daniel@0: # Daniel@0: dotty.protogt.creategraph = function (protogt) { Daniel@0: local gt, id, gtid; Daniel@0: Daniel@0: if (~protogt) Daniel@0: protogt = dotty.protogt; Daniel@0: for (gtid = 0; dotty.graphs[gtid]; gtid = gtid + 1) Daniel@0: ; Daniel@0: gt = (dotty.graphs[gtid] = []); Daniel@0: if (protogt.mode ~= 'replace') { Daniel@0: for (id in dotty.protogt) Daniel@0: gt[id] = copy (dotty.protogt[id]); Daniel@0: } Daniel@0: for (id in protogt) Daniel@0: gt[id] = copy (protogt[id]); Daniel@0: gt.gtid = gtid; Daniel@0: gt.views = []; Daniel@0: gt.undoarray = ['level' = 0; 'entries' = [];]; Daniel@0: gt.busy = 0; Daniel@0: return gt; Daniel@0: }; Daniel@0: dotty.protogt.copygraph = function (ogt) { Daniel@0: local gt, gtid, id; Daniel@0: Daniel@0: for (gtid = 0; dotty.graphs[gtid]; gtid = gtid + 1) Daniel@0: ; Daniel@0: gt = (dotty.graphs[gtid] = []); Daniel@0: for (id in ogt) Daniel@0: gt[id] = copy (ogt[id]); Daniel@0: gt.gtid = gtid; Daniel@0: gt.views = []; Daniel@0: gt.undoarray = ['level' = 0; 'entries' = [];]; Daniel@0: gt.busy = 0; Daniel@0: return gt; Daniel@0: }; Daniel@0: dotty.protogt.destroygraph = function (gt) { Daniel@0: local vid, vlist; Daniel@0: Daniel@0: if (gt.layoutpending > 0) Daniel@0: gt.cancellayout (gt); Daniel@0: for (vid in gt.views) Daniel@0: vlist[vid] = gt.views[vid]; Daniel@0: for (vid in gt.views) Daniel@0: gt.destroyview (gt, vlist[vid]); Daniel@0: remove (gt.gtid, dotty.graphs); Daniel@0: }; Daniel@0: dotty.protogt.loadgraph = function (gt, name, type, protograph, layoutflag) { Daniel@0: local fd, vid, vt, graph, nid, eid, gid; Daniel@0: Daniel@0: if (gt.layoutpending > 0) Daniel@0: gt.cancellayout (gt); Daniel@0: if (~name) Daniel@0: if (~(name = ask ('file name:', 'file', ''))) Daniel@0: return; Daniel@0: dotty.pushbusy (gt, gt.views); Daniel@0: dotty.message (1, 'loading'); Daniel@0: if (~protograph) Daniel@0: protograph = dotty.protogt.graph; Daniel@0: if ( Daniel@0: ~((fd = dotty.openio (name, type, 'r')) >= 0) | Daniel@0: ~(graph = readgraph (fd, protograph)) Daniel@0: ) { Daniel@0: dotty.message (0, 'cannot load graph'); Daniel@0: dotty.popbusy (gt, gt.views); Daniel@0: return; Daniel@0: } Daniel@0: for (vid in gt.views) { Daniel@0: vt = gt.views[vid]; Daniel@0: vt.colors = []; Daniel@0: vt.colorn = 2; Daniel@0: } Daniel@0: gt.graph = graph; Daniel@0: gt.name = name; Daniel@0: gt.type = type; Daniel@0: gt.undoarray = ['level' = 0; 'entries' = [];]; Daniel@0: if (~(type == 'file' & name == '-')) Daniel@0: closeio (fd); Daniel@0: graph.maxgid = tablesize (graph.graphs); Daniel@0: graph.maxnid = tablesize (graph.nodes); Daniel@0: graph.maxeid = tablesize (graph.edges); Daniel@0: for (nid in graph.nodes) Daniel@0: graph.nodes[nid][dotty.keys.nid] = nid; Daniel@0: for (eid in graph.edges) Daniel@0: graph.edges[eid][dotty.keys.eid] = eid; Daniel@0: for (gid in graph.graphs) Daniel@0: graph.graphs[gid][dotty.keys.gid] = gid; Daniel@0: gt.unpackattr (gt); Daniel@0: if (layoutflag) { Daniel@0: dotty.message (1, 'generating layout'); Daniel@0: gt.layoutgraph (gt); Daniel@0: } Daniel@0: dotty.popbusy (gt, gt.views); Daniel@0: return gt.graph; Daniel@0: }; Daniel@0: dotty.protogt.savegraph = function (gt, name, type) { Daniel@0: local fd; Daniel@0: Daniel@0: if (~name) Daniel@0: if (~(name = ask ('file name:', 'file', ''))) Daniel@0: return; Daniel@0: if ( Daniel@0: ~((fd = dotty.openio (name, type, 'w')) >= 0) | Daniel@0: ~writegraph (fd, gt.graph, 0) Daniel@0: ) { Daniel@0: dotty.message (0, 'cannot save graph'); Daniel@0: return; Daniel@0: } Daniel@0: if (~(type == 'file' & name == '-')) Daniel@0: closeio (fd); Daniel@0: }; Daniel@0: dotty.protogt.setgraph = function (gt, graph) { Daniel@0: local vid, vt, nid, eid, gid; Daniel@0: Daniel@0: if (gt.layoutpending > 0) Daniel@0: gt.cancellayout (gt); Daniel@0: for (vid in gt.views) { Daniel@0: vt = gt.views[vid]; Daniel@0: vt.colors = []; Daniel@0: vt.colorn = 2; Daniel@0: } Daniel@0: gt.graph = copy (graph); Daniel@0: gt.undoarray = ['level' = 0; 'entries' = [];]; Daniel@0: gt.unpackattr (gt); Daniel@0: gt.graph.maxgid = tablesize (graph.graphs); Daniel@0: gt.graph.maxnid = tablesize (graph.nodes); Daniel@0: gt.graph.maxeid = tablesize (graph.edges); Daniel@0: for (nid in gt.graph.nodes) Daniel@0: gt.graph.nodes[nid][dotty.keys.nid] = nid; Daniel@0: for (eid in gt.graph.edges) Daniel@0: gt.graph.edges[eid][dotty.keys.eid] = eid; Daniel@0: for (gid in gt.graph.graphs) Daniel@0: gt.graph.graphs[gid][dotty.keys.gid] = gid; Daniel@0: gt.unpackattr (gt); Daniel@0: dotty.message (1, 'generating layout'); Daniel@0: gt.layoutgraph (gt); Daniel@0: return gt.graph; Daniel@0: }; Daniel@0: dotty.protogt.erasegraph = function (gt, protogt, protovt) { Daniel@0: local vid, vt; Daniel@0: Daniel@0: if (gt.layoutpending > 0) Daniel@0: gt.cancellayout (gt); Daniel@0: for (vid in gt.views) { Daniel@0: vt = gt.views[vid]; Daniel@0: vt.colors = []; Daniel@0: vt.colorn = 2; Daniel@0: clear (vt.canvas); Daniel@0: } Daniel@0: if (~protogt) Daniel@0: protogt = dotty.protogt; Daniel@0: gt.graph = copy (protogt.graph); Daniel@0: gt.undoarray = ['level' = 0; 'entries' = [];]; Daniel@0: }; Daniel@0: dotty.protogt.layoutgraph = function (gt) { Daniel@0: if (gt.graph.graphattr.xdotversion) { Daniel@0: gt.unpacklayout (gt, gt.graph); Daniel@0: gt.setviewsize (gt.views, gt.graph.rect); Daniel@0: gt.redrawgraph (gt, gt.views); Daniel@0: return; Daniel@0: } Daniel@0: if (gt.layoutmode == 'async') { Daniel@0: if (~gt.haveinput) { Daniel@0: gt.startlayout (gt); Daniel@0: return; Daniel@0: } Daniel@0: if (~gt.finishlayout (gt)) Daniel@0: return; Daniel@0: gt.setviewsize (gt.views, gt.graph.rect); Daniel@0: gt.redrawgraph (gt, gt.views); Daniel@0: } else { Daniel@0: if (~gt.startlayout (gt)) Daniel@0: return; Daniel@0: else Daniel@0: while (~gt.finishlayout (gt)) Daniel@0: ; Daniel@0: gt.setviewsize (gt.views, gt.graph.rect); Daniel@0: gt.redrawgraph (gt, gt.views); Daniel@0: } Daniel@0: }; Daniel@0: dotty.protogt.createview = function (gt, protovt) { Daniel@0: local vt, ovt, id, t; Daniel@0: Daniel@0: vt = []; Daniel@0: vt.colors = []; Daniel@0: vt.colorn = 2; Daniel@0: if (~protovt) Daniel@0: protovt = dotty.protovt.normal; Daniel@0: if (protovt.mode ~= 'replace') { Daniel@0: for (id in dotty.protovt[protovt.type]) Daniel@0: vt[id] = copy (dotty.protovt[protovt.type][id]); Daniel@0: } Daniel@0: for (id in protovt) Daniel@0: vt[id] = copy (protovt[id]); Daniel@0: if (~(vt.parent >= 0)) { Daniel@0: vt.view = createwidget (-1, [ Daniel@0: 'type' = 'view'; Daniel@0: 'name' = vt.name; Daniel@0: 'origin' = vt.orig; Daniel@0: 'size' = vt.size; Daniel@0: ]); Daniel@0: vt.scroll = createwidget (vt.view, ['type' = 'scroll';]); Daniel@0: } else { Daniel@0: vt.view = -1; Daniel@0: vt.scroll = createwidget (vt.parent, [ Daniel@0: 'type' = 'scroll'; Daniel@0: 'size' = vt.size; Daniel@0: ]); Daniel@0: } Daniel@0: vt.canvas = createwidget (vt.scroll, [ Daniel@0: 'type' = 'canvas'; Daniel@0: 'color' = [0 = protovt.bgcolor; 1 = protovt.fgcolor;]; Daniel@0: ]); Daniel@0: setwidgetattr (vt.canvas, [ Daniel@0: 'window' = vt.wrect; Daniel@0: 'viewport' = vt.vsize; Daniel@0: ]); Daniel@0: clear (vt.canvas); Daniel@0: dotty.views[vt.canvas] = vt; Daniel@0: vt.vtid = vt.canvas; Daniel@0: vt.gtid = gt.gtid; Daniel@0: gt.views[vt.vtid] = vt; Daniel@0: dotty.views[vt.scroll] = vt; Daniel@0: if (vt.view ~= -1) Daniel@0: dotty.views[vt.view] = vt; Daniel@0: if (protovt.colors & tablesize (protovt.colors) > 0) { Daniel@0: for (id in protovt.colors) Daniel@0: if (id == '_bgcolor_') Daniel@0: setwidgetattr (vt.canvas, [ Daniel@0: 'color' = [0 = protovt.colors[id];]; Daniel@0: ]); Daniel@0: else if (setwidgetattr (vt.canvas, ['color' = [ Daniel@0: protovt.colors[id] = id; Daniel@0: ];]) ~= 1) { Daniel@0: t = split (id, ' '); Daniel@0: if (tablesize (t) ~= 3 | setwidgetattr (vt.canvas, [ Daniel@0: 'color' = [protovt.colors[id] = [ Daniel@0: 'h' = ston (t[0]); 's' = ston (t[1]); 'v' = ston (t[2]); Daniel@0: ];]; Daniel@0: ]) ~= 1) { Daniel@0: dotty.message ( Daniel@0: 0, concat ('unknown color ', id, ' using #1') Daniel@0: ); Daniel@0: } Daniel@0: } Daniel@0: vt.colors = copy (protovt.colors); Daniel@0: vt.colorn = protovt.colorn; Daniel@0: } else if (tablesize (gt.views) > 1) { Daniel@0: for (id in gt.views) Daniel@0: if (gt.views[id] ~= vt) Daniel@0: break; Daniel@0: ovt = gt.views[id]; Daniel@0: for (id in ovt.colors) Daniel@0: if (id == '_bgcolor_') Daniel@0: setwidgetattr (vt.canvas, ['color' = [0 = ovt.colors[id];];]); Daniel@0: else if (setwidgetattr (vt.canvas, ['color' = [ Daniel@0: ovt.colors[id] = id; Daniel@0: ];]) ~= 1) { Daniel@0: t = split (id, ' '); Daniel@0: if (tablesize (t) ~= 3 | setwidgetattr (vt.canvas, [ Daniel@0: 'color' = [ovt.colors[id] = [ Daniel@0: 'h' = ston (t[0]); 's' = ston (t[1]); 'v' = ston (t[2]); Daniel@0: ];]; Daniel@0: ]) ~= 1) { Daniel@0: dotty.message ( Daniel@0: 0, concat ('unknown color ', id, ' using #1') Daniel@0: ); Daniel@0: } Daniel@0: } Daniel@0: vt.colors = copy (ovt.colors); Daniel@0: vt.colorn = ovt.colorn; Daniel@0: } Daniel@0: if (gt.graph.rect) Daniel@0: gt.setviewsize ([vt.vtid = vt;], gt.graph.rect); Daniel@0: gt.drawgraph (gt, [vt.vtid = vt;]); Daniel@0: for (id in vt.uifuncs) Daniel@0: if (id == 'closeview') Daniel@0: widgets[vt.view][id] = vt.uifuncs[id]; Daniel@0: else Daniel@0: widgets[vt.canvas][id] = vt.uifuncs[id]; Daniel@0: return vt; Daniel@0: }; Daniel@0: dotty.protogt.destroyview = function (gt, vt) { Daniel@0: destroywidget (vt.canvas); Daniel@0: destroywidget (vt.scroll); Daniel@0: if (vt.view ~= -1) { Daniel@0: destroywidget (vt.view); Daniel@0: remove (vt.view, dotty.views); Daniel@0: } Daniel@0: remove (vt.scroll, dotty.views); Daniel@0: remove (vt.canvas, dotty.views); Daniel@0: if (vt.gtid >= 0) Daniel@0: remove (vt.vtid, gt.views); Daniel@0: if (tablesize (dotty.views) == 0) Daniel@0: exit (); Daniel@0: }; Daniel@0: dotty.protogt.zoom = function (gt, vt, factor, pos) { Daniel@0: gt.setviewscale ([vt.vtid = vt;], factor); Daniel@0: if (pos) Daniel@0: gt.setviewcenter ([vt.vtid = vt;], pos); Daniel@0: gt.redrawgraph (gt, [vt.vtid = vt;]); Daniel@0: }; Daniel@0: dotty.protogt.findnode = function (gt, vt) { Daniel@0: local key, node, node1, nid; Daniel@0: Daniel@0: if (~(key = ask ('give node name or label'))) Daniel@0: return; Daniel@0: if (gt.graph.nodedict[key] >= 0) Daniel@0: node = gt.graph.nodes[gt.graph.nodedict[key]]; Daniel@0: else if (gt.graph.nodedict[ston (key)] >= 0) Daniel@0: node = gt.graph.nodes[gt.graph.nodedict[ston (key)]]; Daniel@0: else { Daniel@0: for (nid in gt.graph.nodes) { Daniel@0: node1 = gt.graph.nodes[nid]; Daniel@0: if (node1.attr.label == key | node1.attr.label == ston (key)) { Daniel@0: node = node1; Daniel@0: break; Daniel@0: } Daniel@0: } Daniel@0: } Daniel@0: if (~node) { Daniel@0: dotty.message (0, concat ('cannot find node: ', key)); Daniel@0: return; Daniel@0: } Daniel@0: gt.setviewcenter ([vt.vtid = vt;], node.pos); Daniel@0: }; Daniel@0: dotty.protogt.setattr = function (gt, obj) { Daniel@0: local kv, t, attr, value; Daniel@0: Daniel@0: if (~(kv = ask ('give attr/value, eg. color=blue'))) Daniel@0: return; Daniel@0: t = split (kv, '='); Daniel@0: attr = t[0]; Daniel@0: value = t[1]; Daniel@0: if ( Daniel@0: obj.attr == gt.graph.graphattr | Daniel@0: obj.attr == gt.graph.edgeattr | Daniel@0: obj.attr == gt.graph.nodeattr Daniel@0: ) { Daniel@0: obj.attr[attr] = value; Daniel@0: return; Daniel@0: } Daniel@0: if (obj.nid >= 0) { Daniel@0: gt.undrawnode (gt, gt.views, obj); Daniel@0: obj.attr[attr] = value; Daniel@0: gt.unpacknodeattr (gt, obj); Daniel@0: gt.drawnode (gt, gt.views, obj); Daniel@0: } else if (obj.eid >= 0) { Daniel@0: gt.undrawedge (gt, gt.views, obj); Daniel@0: obj.attr[attr] = value; Daniel@0: gt.unpackedgeattr (gt, obj); Daniel@0: gt.drawedge (gt, gt.views, obj); Daniel@0: } Daniel@0: }; Daniel@0: dotty.protogt.getattr = function (gt, node) { Daniel@0: local kv; Daniel@0: Daniel@0: if (~(kv.key = ask ('give attr name'))) Daniel@0: return null; Daniel@0: if ((kv.val = node.attr[kv.key])) Daniel@0: return kv; Daniel@0: return null; Daniel@0: }; Daniel@0: # Daniel@0: # utilities Daniel@0: # Daniel@0: dotty.createviewandgraph = function (name, type, protogt, protovt) { Daniel@0: local vt, gt; Daniel@0: Daniel@0: if (~protogt) Daniel@0: protogt = dotty.protogt; Daniel@0: if (protogt.creategraph) Daniel@0: gt = protogt.creategraph (protogt); Daniel@0: else Daniel@0: gt = dotty.protogt.creategraph (protogt); Daniel@0: vt = gt.createview (gt, protovt); Daniel@0: if (~protogt.graph) Daniel@0: protogt.graph = copy (dotty.protogt.graph); Daniel@0: if (name) Daniel@0: gt.loadgraph (gt, name, type, protogt.graph, 1); Daniel@0: return ['gt' = gt; 'vt' = vt;]; Daniel@0: }; Daniel@0: dotty.openio = function (name, type, mode) { Daniel@0: local fd; Daniel@0: Daniel@0: if (~name) Daniel@0: return null; Daniel@0: if (type == 'file') { Daniel@0: if (name == '-') { Daniel@0: if (mode == 'r' | mode == 'r+') Daniel@0: fd = 0; Daniel@0: else Daniel@0: fd = 1; Daniel@0: } else if (~((fd = openio ('file', name, mode)) >= 0)) { Daniel@0: dotty.message (0, concat ('cannot open file: ', name)); Daniel@0: return null; Daniel@0: } Daniel@0: } else if (type == 'pipe') { Daniel@0: if (~((fd = openio ( Daniel@0: 'pipe', 'ksh', mode, concat ("%e ", name) Daniel@0: )) >= 0)) { Daniel@0: dotty.message (0, concat ('cannot run command: ', name)); Daniel@0: return null; Daniel@0: } Daniel@0: } else Daniel@0: return null; Daniel@0: return fd; Daniel@0: }; Daniel@0: dotty.pushbusy = function (gt, views) { Daniel@0: local vid; Daniel@0: Daniel@0: if (gt.busy == 0) Daniel@0: for (vid in gt.views) Daniel@0: setwidgetattr (vid, ['cursor' = 'watch';]); Daniel@0: gt.busy = gt.busy + 1; Daniel@0: }; Daniel@0: dotty.popbusy = function (gt, views) { Daniel@0: local vid; Daniel@0: Daniel@0: gt.busy = gt.busy - 1; Daniel@0: if (gt.busy == 0) Daniel@0: for (vid in gt.views) Daniel@0: setwidgetattr (vid, ['cursor' = 'default';]); Daniel@0: }; Daniel@0: dotty.message = function (level, text) { Daniel@0: if (level <= dotty.mlevel) Daniel@0: echo ('dotty.lefty: ', text); Daniel@0: }; Daniel@0: # Daniel@0: # printing or saving to file Daniel@0: # Daniel@0: dotty.protogt.printorsave = function (gt, vt, otype, name, mode, ptype) { Daniel@0: local pr, wrect, vsize, xy, psize, canvas, pscanvas, cid, cname, t; Daniel@0: local graph, edgehandles, fontmap, eid, edge, nid, node, gid, sgraph; Daniel@0: local did, draw, i; Daniel@0: Daniel@0: if (~otype) Daniel@0: if (~(otype = ask ('print to', 'choice', 'file|printer'))) Daniel@0: return; Daniel@0: if (otype == 'printer') { Daniel@0: if (~getenv ('TMPDIR')) Daniel@0: name = concat (getenv ('HOME'), '/.dottyout.ps'); Daniel@0: else Daniel@0: name = concat (getenv ('TMPDIR'), '/.dottyout.ps', random (10000)); Daniel@0: if (getenv ('LEFTYWINSYS') ~= 'mswin' & ~pr) Daniel@0: if (~(pr = ask ('printer command', 'string', 'lpr'))) Daniel@0: return; Daniel@0: } Daniel@0: if (~name) Daniel@0: if (~(name = ask ('postscript file', 'file', 'out.ps'))) Daniel@0: return; Daniel@0: if (~ptype) Daniel@0: if (~(ptype = ask ('page size', 'choice', '8.5x11|11x17|36x50'))) Daniel@0: return; Daniel@0: if (~mode) Daniel@0: if (~(mode = ask ('mode', 'choice', 'portrait|landscape|best fit'))) Daniel@0: return; Daniel@0: wrect = copy (vt.wrect); Daniel@0: wrect[0].x = wrect[0].x - 1; Daniel@0: wrect[1].x = wrect[1].x + 1; Daniel@0: wrect[0].y = wrect[0].y - 1; Daniel@0: wrect[1].y = wrect[1].y + 1; Daniel@0: vsize = copy (vt.vsize); Daniel@0: if (vsize.x == 0) Daniel@0: vsize.x = 1; Daniel@0: if (vsize.y == 0) Daniel@0: vsize.y = 1; Daniel@0: xy = vsize.x / vsize.y; Daniel@0: if (mode == 'best fit') { Daniel@0: if (xy < 1) Daniel@0: mode = 'portrait'; Daniel@0: else Daniel@0: mode = 'landscape'; Daniel@0: } Daniel@0: psize = dotty.pagesizes[ptype]; Daniel@0: if (mode == 'portrait') { Daniel@0: if (xy < psize.x / psize.y) { Daniel@0: vsize.y = psize.y * 300; Daniel@0: vsize.x = vsize.y * xy; Daniel@0: } else { Daniel@0: vsize.x = psize.x * 300; Daniel@0: vsize.y = vsize.x / xy; Daniel@0: } Daniel@0: } else { Daniel@0: if (xy < psize.y / psize.x) { Daniel@0: vsize.y = psize.x * 300; Daniel@0: vsize.x = vsize.y * xy; Daniel@0: } else { Daniel@0: vsize.x = psize.y * 300; Daniel@0: vsize.y = vsize.x / xy; Daniel@0: } Daniel@0: } Daniel@0: if (~((pscanvas = createwidget (-1, [ Daniel@0: 'type' = 'ps'; Daniel@0: 'origin' = ['x' = 0; 'y' = 0;]; Daniel@0: 'size' = vsize; Daniel@0: 'mode' = mode; Daniel@0: 'name' = name; Daniel@0: ])) >= 0)) { Daniel@0: dotty.message (0, 'cannot open printer device'); Daniel@0: return; Daniel@0: } Daniel@0: for (cname in vt.colors) { Daniel@0: cid = vt.colors[cname]; Daniel@0: if (cname == '_bgcolor_') Daniel@0: setwidgetattr (pscanvas, ['color' = [0 = cid;];]); Daniel@0: else if (setwidgetattr (pscanvas, ['color' = [cid = cname;];]) ~= 1) { Daniel@0: t = split (cname, ' '); Daniel@0: if (tablesize (t) ~= 3 | setwidgetattr (pscanvas, [ Daniel@0: 'color' = [cid = [ Daniel@0: 'h' = ston (t[0]); 's' = ston (t[1]); 'v' = ston (t[2]); Daniel@0: ];]; Daniel@0: ]) ~= 1) { Daniel@0: dotty.message ( Daniel@0: 0, concat ('unknown color ', cname, ' using #1') Daniel@0: ); Daniel@0: } Daniel@0: } Daniel@0: } Daniel@0: setwidgetattr (pscanvas, ['window' = wrect;]); Daniel@0: graph = copy (gt.graph); Daniel@0: canvas = vt.canvas; Daniel@0: vt.canvas = pscanvas; Daniel@0: edgehandles = gt.edgehandles; Daniel@0: gt.edgehandles = 0; Daniel@0: fontmap = dotty.maps[getenv ('LEFTYWINSYS')].psfontmap; Daniel@0: for (eid in graph.edges) { Daniel@0: edge = graph.edges[eid]; Daniel@0: edge.fontname = fontmap[edge.attr.fontname]; Daniel@0: for (did in edge.draws) { Daniel@0: if (did == 'ep') Daniel@0: continue; Daniel@0: draw = edge.draws[did]; Daniel@0: for (i = 0; draw[i]; i = i + 1) Daniel@0: if (draw[i].type == 'F') Daniel@0: draw[i].fn = fontmap[draw[i].ofn]; Daniel@0: } Daniel@0: gt.drawedge (gt, [0 = vt;], edge); Daniel@0: } Daniel@0: for (nid in graph.nodes) { Daniel@0: node = graph.nodes[nid]; Daniel@0: node.fontname = fontmap[node.attr.fontname]; Daniel@0: for (did in node.draws) { Daniel@0: if (did == 'ep') Daniel@0: continue; Daniel@0: draw = node.draws[did]; Daniel@0: for (i = 0; draw[i]; i = i + 1) Daniel@0: if (draw[i].type == 'F') Daniel@0: draw[i].fn = fontmap[draw[i].ofn]; Daniel@0: } Daniel@0: gt.drawnode (gt, [0 = vt;], node); Daniel@0: } Daniel@0: for (gid in graph.graphs) { Daniel@0: sgraph = graph.graphs[gid]; Daniel@0: sgraph.fontname = fontmap[sgraph.graphattr.fontname]; Daniel@0: for (did in sgraph.draws) { Daniel@0: if (did == 'ep') Daniel@0: continue; Daniel@0: draw = sgraph.draws[did]; Daniel@0: for (i = 0; draw[i]; i = i + 1) Daniel@0: if (draw[i].type == 'F') Daniel@0: draw[i].fn = fontmap[draw[i].ofn]; Daniel@0: } Daniel@0: gt.drawsgraph (gt, [0 = vt;], sgraph); Daniel@0: } Daniel@0: graph.fontname = fontmap[graph.graphattr.fontname]; Daniel@0: gt.drawsgraph (gt, [0 = vt;], graph); Daniel@0: gt.edgehandles = edgehandles; Daniel@0: vt.canvas = canvas; Daniel@0: destroywidget (pscanvas); Daniel@0: if (otype == 'printer' & getenv ('LEFTYWINSYS') ~= 'mswin') Daniel@0: system (concat (pr, ' ', name, '; rm ',name)); Daniel@0: };