| 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 | #include "config.h" |
| 15 | |
| 16 | #include <stdlib.h> |
| 17 | |
| 18 | #include "gvplugin_render.h" |
| 19 | #include "gvplugin_device.h" |
| 20 | #include "gvio.h" |
| 21 | |
| 22 | extern char *xml_string(char *str); |
| 23 | extern char *xml_url_string(char *str); |
| 24 | |
| 25 | typedef enum { FORMAT_IMAP, FORMAT_ISMAP, FORMAT_CMAP, FORMAT_CMAPX, } format_type; |
| 26 | |
| 27 | static void map_output_shape (GVJ_t *job, map_shape_t map_shape, pointf * AF, int nump, |
| 28 | char* url, char *tooltip, char *target, char *id) |
| 29 | { |
| 30 | int i; |
| 31 | |
| 32 | static point *A; |
| 33 | static int size_A; |
| 34 | |
| 35 | if (!AF || !nump) |
| 36 | return; |
| 37 | |
| 38 | if (size_A < nump) { |
| 39 | size_A = nump + 10; |
| 40 | A = realloc(A, size_A * sizeof(point)); |
| 41 | } |
| 42 | for (i = 0; i < nump; i++) |
| 43 | PF2P(AF[i], A[i]); |
| 44 | |
| 45 | if (job->render.id == FORMAT_IMAP && url && url[0]) { |
| 46 | switch (map_shape) { |
| 47 | case MAP_RECTANGLE: |
| 48 | /* Y_GOES_DOWN so need UL to LR */ |
| 49 | gvprintf(job, "rect %s %d,%d %d,%d\n" , url, |
| 50 | A[0].x, A[1].y, A[1].x, A[0].y); |
| 51 | break; |
| 52 | case MAP_CIRCLE: |
| 53 | gvprintf(job, "circle %s %d,%d,%d\n" , url, |
| 54 | A[0].x, A[0].y, A[1].x-A[0].x); |
| 55 | break; |
| 56 | case MAP_POLYGON: |
| 57 | gvprintf(job, "poly %s" , url); |
| 58 | for (i = 0; i < nump; i++) |
| 59 | gvprintf(job, " %d,%d" , A[i].x, A[i].y); |
| 60 | gvputs(job, "\n" ); |
| 61 | break; |
| 62 | default: |
| 63 | assert(0); |
| 64 | break; |
| 65 | } |
| 66 | |
| 67 | } else if (job->render.id == FORMAT_ISMAP && url && url[0]) { |
| 68 | switch (map_shape) { |
| 69 | case MAP_RECTANGLE: |
| 70 | /* Y_GOES_DOWN so need UL to LR */ |
| 71 | gvprintf(job, "rectangle (%d,%d) (%d,%d) %s %s\n" , |
| 72 | A[0].x, A[1].y, A[1].x, A[0].y, url, tooltip); |
| 73 | break; |
| 74 | default: |
| 75 | assert(0); |
| 76 | break; |
| 77 | } |
| 78 | |
| 79 | } else if (job->render.id == FORMAT_CMAP || job->render.id == FORMAT_CMAPX) { |
| 80 | switch (map_shape) { |
| 81 | case MAP_CIRCLE: |
| 82 | gvputs(job, "<area shape=\"circle\"" ); |
| 83 | break; |
| 84 | case MAP_RECTANGLE: |
| 85 | gvputs(job, "<area shape=\"rect\"" ); |
| 86 | break; |
| 87 | case MAP_POLYGON: |
| 88 | gvputs(job, "<area shape=\"poly\"" ); |
| 89 | break; |
| 90 | default: |
| 91 | assert(0); |
| 92 | break; |
| 93 | } |
| 94 | if (id && id[0]) { |
| 95 | gvputs(job, " id=\"" ); |
| 96 | gvputs(job, xml_url_string(id)); |
| 97 | gvputs(job, "\"" ); |
| 98 | } |
| 99 | if (url && url[0]) { |
| 100 | gvputs(job, " href=\"" ); |
| 101 | gvputs(job, xml_url_string(url)); |
| 102 | gvputs(job, "\"" ); |
| 103 | } |
| 104 | if (target && target[0]) { |
| 105 | gvputs(job, " target=\"" ); |
| 106 | gvputs(job, xml_string(target)); |
| 107 | gvputs(job, "\"" ); |
| 108 | } |
| 109 | if (tooltip && tooltip[0]) { |
| 110 | gvputs(job, " title=\"" ); |
| 111 | gvputs(job, xml_string(tooltip)); |
| 112 | gvputs(job, "\"" ); |
| 113 | } |
| 114 | /* |
| 115 | * alt text is intended for the visually impaired, but such |
| 116 | * folk are not likely to be clicking around on a graph anyway. |
| 117 | * IE on the PC platform (but not on Macs) incorrectly |
| 118 | * uses (non-empty) alt strings instead of title strings for tooltips. |
| 119 | * To make tooltips work and avoid this IE issue, |
| 120 | * while still satisfying usability guidelines |
| 121 | * that require that there is always an alt string, |
| 122 | * we generate just an empty alt string. |
| 123 | */ |
| 124 | gvputs(job, " alt=\"\"" ); |
| 125 | |
| 126 | gvputs(job, " coords=\"" ); |
| 127 | switch (map_shape) { |
| 128 | case MAP_CIRCLE: |
| 129 | gvprintf(job, "%d,%d,%d" , A[0].x, A[0].y, A[1].x-A[0].x); |
| 130 | break; |
| 131 | case MAP_RECTANGLE: |
| 132 | /* Y_GOES_DOWN so need UL to LR */ |
| 133 | gvprintf(job, "%d,%d,%d,%d" , A[0].x, A[1].y, A[1].x, A[0].y); |
| 134 | break; |
| 135 | case MAP_POLYGON: |
| 136 | gvprintf(job, "%d,%d" , A[0].x, A[0].y); |
| 137 | for (i = 1; i < nump; i++) |
| 138 | gvprintf(job, ",%d,%d" , A[i].x, A[i].y); |
| 139 | break; |
| 140 | default: |
| 141 | break; |
| 142 | } |
| 143 | if (job->render.id == FORMAT_CMAPX) |
| 144 | gvputs(job, "\"/>\n" ); |
| 145 | else |
| 146 | gvputs(job, "\">\n" ); |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | static void map_begin_page(GVJ_t * job) |
| 151 | { |
| 152 | obj_state_t *obj = job->obj; |
| 153 | char *s; |
| 154 | |
| 155 | switch (job->render.id) { |
| 156 | case FORMAT_IMAP: |
| 157 | gvputs(job, "base referer\n" ); |
| 158 | if (obj->url && obj->url[0]) { |
| 159 | gvputs(job, "default " ); |
| 160 | gvputs(job, xml_string(obj->url)); |
| 161 | gvputs(job, "\n" ); |
| 162 | } |
| 163 | break; |
| 164 | case FORMAT_ISMAP: |
| 165 | if (obj->url && obj->url[0]) { |
| 166 | gvputs(job, "default " ); |
| 167 | gvputs(job, xml_string(obj->url)); |
| 168 | gvputs(job, " " ); |
| 169 | gvputs(job, xml_string(agnameof(obj->u.g))); |
| 170 | gvputs(job, "\n" ); |
| 171 | } |
| 172 | break; |
| 173 | case FORMAT_CMAPX: |
| 174 | s = xml_string(agnameof(obj->u.g)); |
| 175 | gvputs(job, "<map id=\"" ); |
| 176 | gvputs(job, s); |
| 177 | gvputs(job, "\" name=\"" ); |
| 178 | gvputs(job, s); |
| 179 | gvputs(job, "\">\n" ); |
| 180 | break; |
| 181 | default: |
| 182 | break; |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | static void map_end_page(GVJ_t * job) |
| 187 | { |
| 188 | obj_state_t *obj = job->obj; |
| 189 | |
| 190 | switch (job->render.id) { |
| 191 | case FORMAT_CMAP: |
| 192 | map_output_shape(job, obj->url_map_shape, |
| 193 | obj->url_map_p,obj->url_map_n, |
| 194 | obj->url, obj->tooltip, obj->target, obj->id); |
| 195 | break; |
| 196 | case FORMAT_CMAPX: |
| 197 | map_output_shape(job, obj->url_map_shape, |
| 198 | obj->url_map_p,obj->url_map_n, |
| 199 | obj->url, obj->tooltip, obj->target, obj->id); |
| 200 | gvputs(job, "</map>\n" ); |
| 201 | break; |
| 202 | default: |
| 203 | break; |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | #if 0 |
| 208 | static void map_begin_cluster(GVJ_t * job) |
| 209 | { |
| 210 | obj_state_t *obj = job->obj; |
| 211 | |
| 212 | gvprintf(job, "%% %s\n" , obj->u.sg->name); |
| 213 | |
| 214 | map_output_shape(job, obj->url_map_shape, obj->url_map_p, obj->url_map_n, |
| 215 | obj->url, obj->tooltip, obj->target); |
| 216 | } |
| 217 | |
| 218 | static void map_begin_node(GVJ_t * job) |
| 219 | { |
| 220 | obj_state_t *obj = job->obj; |
| 221 | |
| 222 | map_output_shape(job, obj->url_map_shape, obj->url_map_p,obj->url_map_n, |
| 223 | obj->url, obj->tooltip, obj->target); |
| 224 | } |
| 225 | |
| 226 | static void |
| 227 | map_begin_edge(GVJ_t * job) |
| 228 | { |
| 229 | obj_state_t *obj = job->obj; |
| 230 | int i, j = 0; |
| 231 | |
| 232 | map_output_shape(job, obj->url_map_shape, obj->url_map_p, obj->url_map_n, |
| 233 | obj->url, obj->tooltip, obj->target); |
| 234 | map_output_shape(job, MAP_RECTANGLE, obj->tailurl_map_p, 2, |
| 235 | obj->tailurl, obj->tailtooltip, obj->tailtarget); |
| 236 | map_output_shape(job, MAP_RECTANGLE, obj->headurl_map_p, 2, |
| 237 | obj->headurl, obj->headtooltip, obj->headtarget); |
| 238 | map_output_shape(job, MAP_RECTANGLE, obj->tailendurl_map_p,2, |
| 239 | obj->url, obj->tooltip, obj->target); |
| 240 | map_output_shape(job, MAP_RECTANGLE, obj->headendurl_map_p, 2, |
| 241 | obj->url, obj->tooltip, obj->target); |
| 242 | for (i = 0; i < obj->url_bsplinemap_poly_n; i++) { |
| 243 | map_output_shape(job, MAP_POLYGON, |
| 244 | obj->url_bsplinemap_p+j, obj->url_bsplinemap_n[i], |
| 245 | obj->url, obj->tooltip, obj->target); |
| 246 | j += obj->url_bsplinemap_n[i]; |
| 247 | } |
| 248 | } |
| 249 | #endif |
| 250 | |
| 251 | static void map_begin_anchor(GVJ_t * job, char *url, char *tooltip, char *target, char *id) |
| 252 | { |
| 253 | obj_state_t *obj = job->obj; |
| 254 | |
| 255 | map_output_shape(job, obj->url_map_shape, |
| 256 | obj->url_map_p, obj->url_map_n, |
| 257 | url, tooltip, target, id); |
| 258 | } |
| 259 | |
| 260 | static gvrender_engine_t map_engine = { |
| 261 | 0, /* map_begin_job */ |
| 262 | 0, /* map_end_job */ |
| 263 | 0, /* map_begin_graph */ |
| 264 | 0, /* map_end_graph */ |
| 265 | 0, /* map_begin_layer */ |
| 266 | 0, /* map_end_layer */ |
| 267 | map_begin_page, |
| 268 | map_end_page, |
| 269 | 0, /* map_begin_cluster */ |
| 270 | 0, /* map_end_cluster */ |
| 271 | 0, /* map_begin_nodes */ |
| 272 | 0, /* map_end_nodes */ |
| 273 | 0, /* map_begin_edges */ |
| 274 | 0, /* map_end_edges */ |
| 275 | 0, /* map_begin_node */ |
| 276 | 0, /* map_end_node */ |
| 277 | 0, /* map_begin_edge */ |
| 278 | 0, /* map_end_edge */ |
| 279 | map_begin_anchor, |
| 280 | 0, /* map_end_anchor */ |
| 281 | 0, /* map_begin_label */ |
| 282 | 0, /* map_end_label */ |
| 283 | 0, /* map_textpara */ |
| 284 | 0, /* map_resolve_color */ |
| 285 | 0, /* map_ellipse */ |
| 286 | 0, /* map_polygon */ |
| 287 | 0, /* map_bezier */ |
| 288 | 0, /* map_polyline */ |
| 289 | 0, /* map_comment */ |
| 290 | 0, /* map_library_shape */ |
| 291 | }; |
| 292 | |
| 293 | static gvrender_features_t render_features_map = { |
| 294 | EMIT_CLUSTERS_LAST |
| 295 | | GVRENDER_Y_GOES_DOWN |
| 296 | | GVRENDER_DOES_MAPS |
| 297 | | GVRENDER_DOES_LABELS |
| 298 | | GVRENDER_DOES_TOOLTIPS |
| 299 | | GVRENDER_DOES_TARGETS |
| 300 | | GVRENDER_DOES_MAP_RECTANGLE, /* flags */ |
| 301 | 4., /* default pad - graph units */ |
| 302 | NULL, /* knowncolors */ |
| 303 | 0, /* sizeof knowncolors */ |
| 304 | 0, /* color_type */ |
| 305 | }; |
| 306 | |
| 307 | static gvdevice_features_t device_features_map = { |
| 308 | GVRENDER_DOES_MAP_CIRCLE |
| 309 | | GVRENDER_DOES_MAP_POLYGON, /* flags */ |
| 310 | {0.,0.}, /* default margin - points */ |
| 311 | {0.,0.}, /* default page width, height - points */ |
| 312 | {96.,96.}, /* default dpi */ |
| 313 | }; |
| 314 | |
| 315 | static gvdevice_features_t device_features_map_nopoly = { |
| 316 | 0, /* flags */ |
| 317 | {0.,0.}, /* default margin - points */ |
| 318 | {0.,0.}, /* default page width, height - points */ |
| 319 | {96.,96.}, /* default dpi */ |
| 320 | }; |
| 321 | |
| 322 | gvplugin_installed_t gvrender_map_types[] = { |
| 323 | {FORMAT_ISMAP, "map" , 1, &map_engine, &render_features_map}, |
| 324 | {0, NULL, 0, NULL, NULL} |
| 325 | }; |
| 326 | |
| 327 | gvplugin_installed_t gvdevice_map_types[] = { |
| 328 | {FORMAT_ISMAP, "ismap:map" , 1, NULL, &device_features_map_nopoly}, |
| 329 | {FORMAT_CMAP, "cmap:map" , 1, NULL, &device_features_map}, |
| 330 | {FORMAT_IMAP, "imap:map" , 1, NULL, &device_features_map}, |
| 331 | {FORMAT_CMAPX, "cmapx:map" , 1, NULL, &device_features_map}, |
| 332 | {FORMAT_IMAP, "imap_np:map" , 1, NULL, &device_features_map_nopoly}, |
| 333 | {FORMAT_CMAPX, "cmapx_np:map" , 1, NULL, &device_features_map_nopoly}, |
| 334 | {0, NULL, 0, NULL, NULL} |
| 335 | }; |
| 336 | |