1 /* $Id$ $Revision$ */
2 /* vim:set shiftwidth=4 ts=8: */
3 
4 /*************************************************************************
5  * Copyright (c) 2011 AT&T Intellectual Property
6  * All rights reserved. This program and the accompanying materials
7  * are made available under the terms of the Eclipse Public License v1.0
8  * which accompanies this distribution, and is available at
9  * http://www.eclipse.org/legal/epl-v10.html
10  *
11  * Contributors: See CVS logs. Details at http://www.graphviz.org/
12  *************************************************************************/
13 
14 
15 #include "tcldot.h"
16 
Tcldot_string_writer(GVJ_t * job,const char * s,size_t len)17 size_t Tcldot_string_writer(GVJ_t *job, const char *s, size_t len)
18 {
19     Tcl_AppendResult((Tcl_Interp*)(job->context), s, NULL);
20     return len;
21 }
22 
Tcldot_channel_writer(GVJ_t * job,const char * s,size_t len)23 size_t Tcldot_channel_writer(GVJ_t *job, const char *s, size_t len)
24 {
25     return Tcl_Write((Tcl_Channel)(job->output_file), s, len);
26 }
27 
28 /* handles (tcl commands) to obj* */
29 
cmd2g(char * cmd)30 Agraph_t *cmd2g(char *cmd) {
31     Agraph_t *g = NULL;
32 
33     if (sscanf(cmd, "graph%p", &g) != 1 || !g)
34         return NULL;
35     return g;
36 }
cmd2n(char * cmd)37 Agnode_t *cmd2n(char *cmd) {
38     Agnode_t *n = NULL;
39 
40     if (sscanf(cmd, "node%p", &n) != 1 || !n)
41         return NULL;
42     return n;
43 }
cmd2e(char * cmd)44 Agedge_t *cmd2e(char *cmd) {
45     Agedge_t *e = NULL;
46 
47     if (sscanf(cmd, "edge%p", &e) != 1 || !e)
48         return NULL;
49     return e;
50 }
51 
52 
53 /* obj* to handles (tcl commands) */
54 
obj2cmd(void * obj)55 char *obj2cmd (void *obj) {
56     static char buf[32];
57 
58     switch (AGTYPE(obj)) {
59         case AGRAPH: sprintf(buf,"graph%p",obj); break;
60         case AGNODE: sprintf(buf,"node%p",obj); break;
61         case AGINEDGE:
62         case AGOUTEDGE: sprintf(buf,"edge%p",obj); break;
63     }
64     return buf;
65 }
66 
67 
deleteEdge(gctx_t * gctx,Agraph_t * g,Agedge_t * e)68 void deleteEdge(gctx_t *gctx, Agraph_t * g, Agedge_t *e)
69 {
70     char *hndl;
71 
72     hndl = obj2cmd(e);
73     agdelete(gctx->g, e);  /* delete edge from root graph */
74     Tcl_DeleteCommand(gctx->ictx->interp, hndl);
75 }
deleteNodeEdges(gctx_t * gctx,Agraph_t * g,Agnode_t * n)76 static void deleteNodeEdges(gctx_t *gctx, Agraph_t *g, Agnode_t *n)
77 {
78     Agedge_t *e, *e1;
79 
80     e = agfstedge(g, n);
81     while (e) {
82 	e1 = agnxtedge(g, e, n);
83 	deleteEdge(gctx, g, e);
84 	e = e1;
85     }
86 }
deleteNode(gctx_t * gctx,Agraph_t * g,Agnode_t * n)87 void deleteNode(gctx_t * gctx, Agraph_t *g, Agnode_t *n)
88 {
89     char *hndl;
90 
91     deleteNodeEdges(gctx, gctx->g, n); /* delete all edges to/from node in root graph */
92 
93     hndl = obj2cmd(n);
94     agdelete(gctx->g, n); /* delete node from root graph */
95     Tcl_DeleteCommand(gctx->ictx->interp, hndl);
96 }
deleteGraphNodes(gctx_t * gctx,Agraph_t * g)97 static void deleteGraphNodes(gctx_t * gctx, Agraph_t *g)
98 {
99     Agnode_t *n, *n1;
100 
101     n = agfstnode(g);
102     while (n) {
103 	n1 = agnxtnode(g, n);
104 	deleteNode(gctx, g, n);
105 	n = n1;
106     }
107 }
deleteGraph(gctx_t * gctx,Agraph_t * g)108 void deleteGraph(gctx_t * gctx, Agraph_t *g)
109 {
110     Agraph_t *sg;
111     char *hndl;
112 
113     for (sg = agfstsubg (g); sg; sg = agnxtsubg (sg)) {
114 	deleteGraph(gctx, sg);
115     }
116     deleteGraphNodes(gctx, g);
117 
118     hndl = obj2cmd(g);
119     if (g == agroot(g)) {
120 	agclose(g);
121     } else {
122 	agdelsubg(agroot(g), g);
123     }
124     Tcl_DeleteCommand(gctx->ictx->interp, hndl);
125 }
126 
myagxset(void * obj,Agsym_t * a,char * val)127 static void myagxset(void *obj, Agsym_t *a, char *val)
128 {
129     int len;
130     char *hs;
131 
132     if (a->name[0] == 'l' && val[0] == '<' && strcmp(a->name, "label") == 0) {
133         len = strlen(val);
134         if (val[len-1] == '>') {
135             hs = strdup(val+1);
136                 *(hs+len-2) = '\0';
137             val = agstrdup_html(agraphof(obj),hs);
138             free(hs);
139         }
140     }
141     agxset(obj, a, val);
142 }
setgraphattributes(Agraph_t * g,char * argv[],int argc)143 void setgraphattributes(Agraph_t * g, char *argv[], int argc)
144 {
145     int i;
146     Agsym_t *a;
147 
148     for (i = 0; i < argc; i++) {
149 	if (!(a = agfindgraphattr(agroot(g), argv[i])))
150 	    a = agattr(agroot(g), AGRAPH, argv[i], "");
151 	myagxset(g, a, argv[++i]);
152     }
153 }
154 
setedgeattributes(Agraph_t * g,Agedge_t * e,char * argv[],int argc)155 void setedgeattributes(Agraph_t * g, Agedge_t * e, char *argv[], int argc)
156 {
157     int i;
158     Agsym_t *a;
159 
160     for (i = 0; i < argc; i++) {
161 	/* silently ignore attempts to modify "key" */
162 	if (strcmp(argv[i], "key") == 0) {
163 	    i++;
164 	    continue;
165 	}
166 	if (e) {
167 	    if (!(a = agfindedgeattr(g, argv[i])))
168 		a = agattr(agroot(g), AGEDGE, argv[i], "");
169 	    myagxset(e, a, argv[++i]);
170 	}
171 	else {
172 	    agattr(g, AGEDGE, argv[i], argv[i+1]);
173 	    i++;
174 	}
175     }
176 }
177 
setnodeattributes(Agraph_t * g,Agnode_t * n,char * argv[],int argc)178 void setnodeattributes(Agraph_t * g, Agnode_t * n, char *argv[], int argc)
179 {
180     int i;
181     Agsym_t *a;
182 
183     for (i = 0; i < argc; i++) {
184 	if (n) {
185 	    if (!(a = agfindnodeattr(g, argv[i])))
186 		a = agattr(agroot(g), AGNODE, argv[i], "");
187 	    myagxset(n, a, argv[++i]);
188 	}
189 	else {
190 	    agattr(g, AGNODE, argv[i], argv[i+1]);
191 	    i++;
192 	}
193     }
194 }
195 
listGraphAttrs(Tcl_Interp * interp,Agraph_t * g)196 void listGraphAttrs (Tcl_Interp * interp, Agraph_t* g)
197 {
198     Agsym_t *a = NULL;
199     while ((a = agnxtattr(g, AGRAPH, a))) {
200 	Tcl_AppendElement(interp, a->name);
201     }
202 }
listNodeAttrs(Tcl_Interp * interp,Agraph_t * g)203 void listNodeAttrs (Tcl_Interp * interp, Agraph_t* g)
204 {
205     Agsym_t *a = NULL;
206     while ((a = agnxtattr(g, AGNODE, a))) {
207 	Tcl_AppendElement(interp, a->name);
208     }
209 }
listEdgeAttrs(Tcl_Interp * interp,Agraph_t * g)210 void listEdgeAttrs (Tcl_Interp * interp, Agraph_t* g)
211 {
212     Agsym_t *a = NULL;
213     while ((a = agnxtattr(g, AGEDGE, a))) {
214 	Tcl_AppendElement(interp, a->name);
215     }
216 }
217 
tcldot_layout(GVC_t * gvc,Agraph_t * g,char * engine)218 void tcldot_layout(GVC_t *gvc, Agraph_t * g, char *engine)
219 {
220     char buf[256];
221     Agsym_t *a;
222     int rc;
223 
224     gvFreeLayout(gvc, g);               /* in case previously drawn */
225 
226 /* support old behaviors if engine isn't specified*/
227     if (!engine || *engine == '\0') {
228 	if (agisdirected(g))
229 	    rc = gvlayout_select(gvc, "dot");
230 	else
231 	    rc = gvlayout_select(gvc, "neato");
232     }
233     else {
234 	if (strcasecmp(engine, "nop") == 0) {
235 	    Nop = 2;
236 	    PSinputscale = POINTS_PER_INCH;
237 	    rc = gvlayout_select(gvc, "neato");
238 	}
239 	else {
240 	    rc = gvlayout_select(gvc, engine);
241 	}
242 	if (rc == NO_SUPPORT)
243 	    rc = gvlayout_select(gvc, "dot");
244     }
245     if (rc == NO_SUPPORT) {
246         fprintf(stderr, "Layout type: \"%s\" not recognized. Use one of:%s\n",
247                 engine, gvplugin_list(gvc, API_layout, engine));
248         return;
249     }
250     gvLayoutJobs(gvc, g);
251 
252 /* set bb attribute for basic layout.
253  * doesn't yet include margins, scaling or page sizes because
254  * those depend on the renderer being used. */
255     if (GD_drawing(g)->landscape)
256 	sprintf(buf, "%d %d %d %d",
257 		ROUND(GD_bb(g).LL.y), ROUND(GD_bb(g).LL.x),
258 		ROUND(GD_bb(g).UR.y), ROUND(GD_bb(g).UR.x));
259     else
260 	sprintf(buf, "%d %d %d %d",
261 		ROUND(GD_bb(g).LL.x), ROUND(GD_bb(g).LL.y),
262 		ROUND(GD_bb(g).UR.x), ROUND(GD_bb(g).UR.y));
263     if (!(a = agattr(g, AGRAPH, "bb", NULL)))
264 	a = agattr(g, AGRAPH, "bb", "");
265     agxset(g, a, buf);
266 }
267