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