|
Graphviz
2.29.20120524.0446
|
00001 /* $Id$ $Revision$ */ 00002 /* vim:set shiftwidth=4 ts=8: */ 00003 00004 /************************************************************************* 00005 * Copyright (c) 2011 AT&T Intellectual Property 00006 * All rights reserved. This program and the accompanying materials 00007 * are made available under the terms of the Eclipse Public License v1.0 00008 * which accompanies this distribution, and is available at 00009 * http://www.eclipse.org/legal/epl-v10.html 00010 * 00011 * Contributors: See CVS logs. Details at http://www.graphviz.org/ 00012 *************************************************************************/ 00013 00014 #ifdef HAVE_CONFIG_H 00015 #include "config.h" 00016 #endif 00017 00018 #include <string.h> 00019 #include <stdlib.h> 00020 #include <math.h> 00021 00022 #include "gvplugin_layout.h" 00023 #include "gvcint.h" 00024 #include "gvcproc.h" 00025 00026 extern char *strdup_and_subst_obj(char *str, void * n); 00027 extern void emit_graph(GVJ_t * job, graph_t * g); 00028 extern boolean overlap_edge(edge_t *e, boxf b); 00029 extern boolean overlap_node(node_t *n, boxf b); 00030 extern int gvLayout(GVC_t *gvc, graph_t *g, const char *engine); 00031 extern int gvRenderFilename(GVC_t *gvc, graph_t *g, const char *format, const char *filename); 00032 extern void graph_cleanup(graph_t *g); 00033 00034 #define PANFACTOR 10 00035 #define ZOOMFACTOR 1.1 00036 #define EPSILON .0001 00037 00038 static char *s_digraph = "digraph"; 00039 static char *s_graph = "graph"; 00040 static char *s_subgraph = "subgraph"; 00041 static char *s_node = "node"; 00042 static char *s_edge = "edge"; 00043 static char *s_tooltip = "tooltip"; 00044 static char *s_href = "href"; 00045 static char *s_URL = "URL"; 00046 static char *s_tailport = "tailport"; 00047 static char *s_headport = "headport"; 00048 static char *s_key = "key"; 00049 00050 static void gv_graph_state(GVJ_t *job, graph_t *g) 00051 { 00052 #ifndef WITH_CGRAPH 00053 int i; 00054 #endif 00055 int j; 00056 Agsym_t *a; 00057 gv_argvlist_t *list; 00058 00059 list = &(job->selected_obj_type_name); 00060 j = 0; 00061 if (g == agroot(g)) { 00062 if (agisdirected(g)) 00063 gv_argvlist_set_item(list, j++, s_digraph); 00064 else 00065 gv_argvlist_set_item(list, j++, s_graph); 00066 } 00067 else { 00068 gv_argvlist_set_item(list, j++, s_subgraph); 00069 } 00070 gv_argvlist_set_item(list, j++, agnameof(g)); 00071 list->argc = j; 00072 00073 list = &(job->selected_obj_attributes); 00074 #ifndef WITH_CGRAPH 00075 for (i = 0, j = 0; i < dtsize(g->univ->globattr->dict); i++) { 00076 a = g->univ->globattr->list[i]; 00077 #else 00078 a = NULL; 00079 while ((a = agnxtattr(g, AGRAPH, a))) { 00080 #endif 00081 gv_argvlist_set_item(list, j++, a->name); 00082 #ifndef WITH_CGRAPH 00083 gv_argvlist_set_item(list, j++, agxget(g, a->index)); 00084 #else 00085 gv_argvlist_set_item(list, j++, agxget(g, a)); 00086 #endif 00087 gv_argvlist_set_item(list, j++, (char*)GVATTR_STRING); 00088 } 00089 list->argc = j; 00090 00091 a = agfindgraphattr(g, s_href); 00092 if (!a) 00093 a = agfindgraphattr(g, s_URL); 00094 if (a) 00095 #ifndef WITH_CGRAPH 00096 job->selected_href = strdup_and_subst_obj(agxget(g, a->index), (void*)g); 00097 #else 00098 job->selected_href = strdup_and_subst_obj(agxget(g, a), (void*)g); 00099 #endif 00100 } 00101 00102 static void gv_node_state(GVJ_t *job, node_t *n) 00103 { 00104 #ifndef WITH_CGRAPH 00105 int i; 00106 #endif 00107 int j; 00108 Agsym_t *a; 00109 Agraph_t *g; 00110 gv_argvlist_t *list; 00111 00112 list = &(job->selected_obj_type_name); 00113 j = 0; 00114 gv_argvlist_set_item(list, j++, s_node); 00115 gv_argvlist_set_item(list, j++, agnameof(n)); 00116 list->argc = j; 00117 00118 list = &(job->selected_obj_attributes); 00119 g = agroot(agraphof(n)); 00120 #ifndef WITH_CGRAPH 00121 for (i = 0, j = 0; i < dtsize(g->univ->nodeattr->dict); i++) { 00122 a = g->univ->nodeattr->list[i]; 00123 #else 00124 a = NULL; 00125 while ((a = agnxtattr(g, AGNODE, a))) { 00126 #endif 00127 gv_argvlist_set_item(list, j++, a->name); 00128 #ifndef WITH_CGRAPH 00129 gv_argvlist_set_item(list, j++, agxget(n, a->index)); 00130 #else 00131 gv_argvlist_set_item(list, j++, agxget(n, a)); 00132 #endif 00133 } 00134 list->argc = j; 00135 00136 a = agfindnodeattr(agraphof(n), s_href); 00137 if (!a) 00138 a = agfindnodeattr(agraphof(n), s_URL); 00139 if (a) 00140 #ifndef WITH_CGRAPH 00141 job->selected_href = strdup_and_subst_obj(agxget(n, a->index), (void*)n); 00142 #else 00143 job->selected_href = strdup_and_subst_obj(agxget(n, a), (void*)n); 00144 #endif 00145 } 00146 00147 static void gv_edge_state(GVJ_t *job, edge_t *e) 00148 { 00149 #ifndef WITH_CGRAPH 00150 int i; 00151 #endif 00152 int j; 00153 Agsym_t *a; 00154 Agraph_t *g; 00155 gv_argvlist_t *nlist, *alist; 00156 00157 nlist = &(job->selected_obj_type_name); 00158 00159 /* only tail, head, and key are strictly identifying properties, 00160 * but we commonly alse use edge kind (e.g. "->") and tailport,headport 00161 * in edge names */ 00162 j = 0; 00163 gv_argvlist_set_item(nlist, j++, s_edge); 00164 gv_argvlist_set_item(nlist, j++, agnameof(agtail(e))); 00165 j++; /* skip tailport slot for now */ 00166 gv_argvlist_set_item(nlist, j++, agisdirected(agraphof(agtail(e)))?"->":"--"); 00167 gv_argvlist_set_item(nlist, j++, agnameof(aghead(e))); 00168 j++; /* skip headport slot for now */ 00169 j++; /* skip key slot for now */ 00170 nlist->argc = j; 00171 00172 alist = &(job->selected_obj_attributes); 00173 g = agroot(agraphof(aghead(e))); 00174 #ifndef WITH_CGRAPH 00175 for (i = 0, j = 0; i < dtsize(g->univ->edgeattr->dict); i++) { 00176 a = g->univ->edgeattr->list[i]; 00177 #else 00178 a = NULL; 00179 while ((a = agnxtattr(g, AGEDGE, a))) { 00180 #endif 00181 00182 /* tailport and headport can be shown as part of the name, but they 00183 * are not identifying properties of the edge so we 00184 * also list them as modifyable attributes. */ 00185 if (strcmp(a->name,s_tailport) == 0) 00186 #ifndef WITH_CGRAPH 00187 gv_argvlist_set_item(nlist, 2, agxget(e, a->index)); 00188 #else 00189 gv_argvlist_set_item(nlist, 2, agxget(e, a)); 00190 #endif 00191 else if (strcmp(a->name,s_headport) == 0) 00192 #ifndef WITH_CGRAPH 00193 gv_argvlist_set_item(nlist, 5, agxget(e, a->index)); 00194 #else 00195 gv_argvlist_set_item(nlist, 5, agxget(e, a)); 00196 #endif 00197 00198 /* key is strictly an identifying property to distinguish multiple 00199 * edges between the same node pair. Its non-writable, so 00200 * no need to list it as an attribute as well. */ 00201 else if (strcmp(a->name,s_key) == 0) { 00202 #ifndef WITH_CGRAPH 00203 gv_argvlist_set_item(nlist, 6, agxget(e, a->index)); 00204 #else 00205 gv_argvlist_set_item(nlist, 6, agxget(e, a)); 00206 #endif 00207 continue; 00208 } 00209 00210 gv_argvlist_set_item(alist, j++, a->name); 00211 #ifndef WITH_CGRAPH 00212 gv_argvlist_set_item(alist, j++, agxget(e, a->index)); 00213 #else 00214 gv_argvlist_set_item(alist, j++, agxget(e, a)); 00215 #endif 00216 } 00217 alist->argc = j; 00218 00219 a = agfindedgeattr(agraphof(aghead(e)), s_href); 00220 if (!a) 00221 a = agfindedgeattr(agraphof(aghead(e)), s_URL); 00222 if (a) 00223 #ifndef WITH_CGRAPH 00224 job->selected_href = strdup_and_subst_obj(agxget(e, a->index), (void*)e); 00225 #else 00226 job->selected_href = strdup_and_subst_obj(agxget(e, a), (void*)e); 00227 #endif 00228 } 00229 00230 static void gvevent_refresh(GVJ_t * job) 00231 { 00232 graph_t *g = job->gvc->g; 00233 00234 if (!job->selected_obj) { 00235 job->selected_obj = g; 00236 GD_gui_state(g) |= GUI_STATE_SELECTED; 00237 gv_graph_state(job, g); 00238 } 00239 emit_graph(job, g); 00240 job->has_been_rendered = TRUE; 00241 } 00242 00243 /* recursively find innermost cluster containing the point */ 00244 static graph_t *gvevent_find_cluster(graph_t *g, boxf b) 00245 { 00246 int i; 00247 graph_t *sg; 00248 boxf bb; 00249 00250 for (i = 1; i <= GD_n_cluster(g); i++) { 00251 sg = gvevent_find_cluster(GD_clust(g)[i], b); 00252 if (sg) 00253 return(sg); 00254 } 00255 B2BF(GD_bb(g), bb); 00256 if (OVERLAP(b, bb)) 00257 return g; 00258 return NULL; 00259 } 00260 00261 static void * gvevent_find_obj(graph_t *g, boxf b) 00262 { 00263 graph_t *sg; 00264 node_t *n; 00265 edge_t *e; 00266 00267 /* edges might overlap nodes, so search them first */ 00268 for (n = agfstnode(g); n; n = agnxtnode(g, n)) 00269 for (e = agfstout(g, n); e; e = agnxtout(g, e)) 00270 if (overlap_edge(e, b)) 00271 return (void *)e; 00272 /* search graph backwards to get topmost node, in case of overlap */ 00273 for (n = aglstnode(g); n; n = agprvnode(g, n)) 00274 if (overlap_node(n, b)) 00275 return (void *)n; 00276 /* search for innermost cluster */ 00277 sg = gvevent_find_cluster(g, b); 00278 if (sg) 00279 return (void *)sg; 00280 00281 /* otherwise - we're always in the graph */ 00282 return (void *)g; 00283 } 00284 00285 static void gvevent_leave_obj(GVJ_t * job) 00286 { 00287 void *obj = job->current_obj; 00288 00289 if (obj) { 00290 switch (agobjkind(obj)) { 00291 #ifndef WITH_CGRAPH 00292 case AGGRAPH: 00293 #else /* WITH_CGRAPH */ 00294 case AGRAPH: 00295 #endif /* WITH_CGRAPH */ 00296 GD_gui_state((graph_t*)obj) &= ~GUI_STATE_ACTIVE; 00297 break; 00298 case AGNODE: 00299 ND_gui_state((node_t*)obj) &= ~GUI_STATE_ACTIVE; 00300 break; 00301 case AGEDGE: 00302 ED_gui_state((edge_t*)obj) &= ~GUI_STATE_ACTIVE; 00303 break; 00304 } 00305 } 00306 job->active_tooltip = NULL; 00307 } 00308 00309 static void gvevent_enter_obj(GVJ_t * job) 00310 { 00311 void *obj; 00312 graph_t *g; 00313 edge_t *e; 00314 node_t *n; 00315 Agsym_t *a; 00316 00317 if (job->active_tooltip) { 00318 free(job->active_tooltip); 00319 job->active_tooltip = NULL; 00320 } 00321 obj = job->current_obj; 00322 if (obj) { 00323 switch (agobjkind(obj)) { 00324 #ifndef WITH_CGRAPH 00325 case AGGRAPH: 00326 #else /* WITH_CGRAPH */ 00327 case AGRAPH: 00328 #endif /* WITH_CGRAPH */ 00329 g = (graph_t*)obj; 00330 GD_gui_state(g) |= GUI_STATE_ACTIVE; 00331 a = agfindgraphattr(g, s_tooltip); 00332 if (a) 00333 #ifndef WITH_CGRAPH 00334 job->active_tooltip = strdup_and_subst_obj(agxget(g, a->index), obj); 00335 #else /* WITH_CGRAPH */ 00336 job->active_tooltip = strdup_and_subst_obj(agxget(g, a), obj); 00337 #endif /* WITH_CGRAPH */ 00338 break; 00339 case AGNODE: 00340 n = (node_t*)obj; 00341 ND_gui_state(n) |= GUI_STATE_ACTIVE; 00342 a = agfindnodeattr(agraphof(n), s_tooltip); 00343 if (a) 00344 #ifndef WITH_CGRAPH 00345 job->active_tooltip = strdup_and_subst_obj(agxget(n, a->index), obj); 00346 #else /* WITH_CGRAPH */ 00347 job->active_tooltip = strdup_and_subst_obj(agxget(n, a), obj); 00348 #endif /* WITH_CGRAPH */ 00349 break; 00350 case AGEDGE: 00351 e = (edge_t*)obj; 00352 ED_gui_state(e) |= GUI_STATE_ACTIVE; 00353 a = agfindedgeattr(agraphof(aghead(e)), s_tooltip); 00354 if (a) 00355 #ifndef WITH_CGRAPH 00356 job->active_tooltip = strdup_and_subst_obj(agxget(e, a->index), obj); 00357 #else /* WITH_CGRAPH */ 00358 job->active_tooltip = strdup_and_subst_obj(agxget(e, a), obj); 00359 #endif /* WITH_CGRAPH */ 00360 break; 00361 } 00362 } 00363 } 00364 00365 static pointf pointer2graph (GVJ_t *job, pointf pointer) 00366 { 00367 pointf p; 00368 00369 /* transform position in device units to position in graph units */ 00370 if (job->rotation) { 00371 p.x = pointer.y / (job->zoom * job->devscale.y) - job->translation.x; 00372 p.y = -pointer.x / (job->zoom * job->devscale.x) - job->translation.y; 00373 } 00374 else { 00375 p.x = pointer.x / (job->zoom * job->devscale.x) - job->translation.x; 00376 p.y = pointer.y / (job->zoom * job->devscale.y) - job->translation.y; 00377 } 00378 return p; 00379 } 00380 00381 /* CLOSEENOUGH is in 1/72 - probably should be a feature... */ 00382 #define CLOSEENOUGH 1 00383 00384 static void gvevent_find_current_obj(GVJ_t * job, pointf pointer) 00385 { 00386 void *obj; 00387 boxf b; 00388 double closeenough; 00389 pointf p; 00390 00391 p = pointer2graph (job, pointer); 00392 00393 /* convert window point to graph coordinates */ 00394 closeenough = CLOSEENOUGH / job->zoom; 00395 00396 b.UR.x = p.x + closeenough; 00397 b.UR.y = p.y + closeenough; 00398 b.LL.x = p.x - closeenough; 00399 b.LL.y = p.y - closeenough; 00400 00401 obj = gvevent_find_obj(job->gvc->g, b); 00402 if (obj != job->current_obj) { 00403 gvevent_leave_obj(job); 00404 job->current_obj = obj; 00405 gvevent_enter_obj(job); 00406 job->needs_refresh = 1; 00407 } 00408 } 00409 00410 static void gvevent_select_current_obj(GVJ_t * job) 00411 { 00412 void *obj; 00413 00414 obj = job->selected_obj; 00415 if (obj) { 00416 switch (agobjkind(obj)) { 00417 #ifndef WITH_CGRAPH 00418 case AGGRAPH: 00419 #else /* WITH_CGRAPH */ 00420 case AGRAPH: 00421 #endif /* WITH_CGRAPH */ 00422 GD_gui_state((graph_t*)obj) |= GUI_STATE_VISITED; 00423 GD_gui_state((graph_t*)obj) &= ~GUI_STATE_SELECTED; 00424 break; 00425 case AGNODE: 00426 ND_gui_state((node_t*)obj) |= GUI_STATE_VISITED; 00427 ND_gui_state((node_t*)obj) &= ~GUI_STATE_SELECTED; 00428 break; 00429 case AGEDGE: 00430 ED_gui_state((edge_t*)obj) |= GUI_STATE_VISITED; 00431 ED_gui_state((edge_t*)obj) &= ~GUI_STATE_SELECTED; 00432 break; 00433 } 00434 } 00435 00436 if (job->selected_href) { 00437 free(job->selected_href); 00438 job->selected_href = NULL; 00439 } 00440 00441 obj = job->selected_obj = job->current_obj; 00442 if (obj) { 00443 switch (agobjkind(obj)) { 00444 #ifndef WITH_CGRAPH 00445 case AGGRAPH: 00446 #else /* WITH_CGRAPH */ 00447 case AGRAPH: 00448 #endif /* WITH_CGRAPH */ 00449 GD_gui_state((graph_t*)obj) |= GUI_STATE_SELECTED; 00450 gv_graph_state(job, (graph_t*)obj); 00451 break; 00452 case AGNODE: 00453 ND_gui_state((node_t*)obj) |= GUI_STATE_SELECTED; 00454 gv_node_state(job, (node_t*)obj); 00455 break; 00456 case AGEDGE: 00457 ED_gui_state((edge_t*)obj) |= GUI_STATE_SELECTED; 00458 gv_edge_state(job, (edge_t*)obj); 00459 break; 00460 } 00461 } 00462 00463 #if 0 00464 for (i = 0; i < job->selected_obj_type_name.argc; i++) 00465 fprintf(stderr,"%s%s", job->selected_obj_type_name.argv[i], 00466 (i==(job->selected_obj_type_name.argc - 1))?"\n":" "); 00467 for (i = 0; i < job->selected_obj_attributes.argc; i++) 00468 fprintf(stderr,"%s%s", job->selected_obj_attributes.argv[i], (i%2)?"\n":" = "); 00469 fprintf(stderr,"\n"); 00470 #endif 00471 } 00472 00473 static void gvevent_button_press(GVJ_t * job, int button, pointf pointer) 00474 { 00475 switch (button) { 00476 case 1: /* select / create in edit mode */ 00477 gvevent_find_current_obj(job, pointer); 00478 gvevent_select_current_obj(job); 00479 job->click = 1; 00480 job->button = button; 00481 job->needs_refresh = 1; 00482 break; 00483 case 2: /* pan */ 00484 job->click = 1; 00485 job->button = button; 00486 job->needs_refresh = 1; 00487 break; 00488 case 3: /* insert node or edge */ 00489 gvevent_find_current_obj(job, pointer); 00490 job->click = 1; 00491 job->button = button; 00492 job->needs_refresh = 1; 00493 break; 00494 case 4: 00495 /* scrollwheel zoom in at current mouse x,y */ 00496 /* FIXME - should code window 0,0 point as feature with Y_GOES_DOWN */ 00497 job->fit_mode = 0; 00498 if (job->rotation) { 00499 job->focus.x -= (pointer.y - job->height / 2.) 00500 * (ZOOMFACTOR - 1.) / (job->zoom * job->devscale.y); 00501 job->focus.y += (pointer.x - job->width / 2.) 00502 * (ZOOMFACTOR - 1.) / (job->zoom * job->devscale.x); 00503 } 00504 else { 00505 job->focus.x += (pointer.x - job->width / 2.) 00506 * (ZOOMFACTOR - 1.) / (job->zoom * job->devscale.x); 00507 job->focus.y += (pointer.y - job->height / 2.) 00508 * (ZOOMFACTOR - 1.) / (job->zoom * job->devscale.y); 00509 } 00510 job->zoom *= ZOOMFACTOR; 00511 job->needs_refresh = 1; 00512 break; 00513 case 5: /* scrollwheel zoom out at current mouse x,y */ 00514 job->fit_mode = 0; 00515 job->zoom /= ZOOMFACTOR; 00516 if (job->rotation) { 00517 job->focus.x += (pointer.y - job->height / 2.) 00518 * (ZOOMFACTOR - 1.) / (job->zoom * job->devscale.y); 00519 job->focus.y -= (pointer.x - job->width / 2.) 00520 * (ZOOMFACTOR - 1.) / (job->zoom * job->devscale.x); 00521 } 00522 else { 00523 job->focus.x -= (pointer.x - job->width / 2.) 00524 * (ZOOMFACTOR - 1.) / (job->zoom * job->devscale.x); 00525 job->focus.y -= (pointer.y - job->height / 2.) 00526 * (ZOOMFACTOR - 1.) / (job->zoom * job->devscale.y); 00527 } 00528 job->needs_refresh = 1; 00529 break; 00530 } 00531 job->oldpointer = pointer; 00532 } 00533 00534 static void gvevent_button_release(GVJ_t *job, int button, pointf pointer) 00535 { 00536 job->click = 0; 00537 job->button = 0; 00538 } 00539 00540 static void gvevent_motion(GVJ_t * job, pointf pointer) 00541 { 00542 /* dx,dy change in position, in device independent points */ 00543 double dx = (pointer.x - job->oldpointer.x) / job->devscale.x; 00544 double dy = (pointer.y - job->oldpointer.y) / job->devscale.y; 00545 00546 if (abs(dx) < EPSILON && abs(dy) < EPSILON) /* ignore motion events with no motion */ 00547 return; 00548 00549 switch (job->button) { 00550 case 0: /* drag with no button - */ 00551 gvevent_find_current_obj(job, pointer); 00552 break; 00553 case 1: /* drag with button 1 - drag object */ 00554 /* FIXME - to be implemented */ 00555 break; 00556 case 2: /* drag with button 2 - pan graph */ 00557 if (job->rotation) { 00558 job->focus.x -= dy / job->zoom; 00559 job->focus.y += dx / job->zoom; 00560 } 00561 else { 00562 job->focus.x -= dx / job->zoom; 00563 job->focus.y -= dy / job->zoom; 00564 } 00565 job->needs_refresh = 1; 00566 break; 00567 case 3: /* drag with button 3 - drag inserted node or uncompleted edge */ 00568 break; 00569 } 00570 job->oldpointer = pointer; 00571 } 00572 00573 static int quit_cb(GVJ_t * job) 00574 { 00575 return 1; 00576 } 00577 00578 static int left_cb(GVJ_t * job) 00579 { 00580 job->fit_mode = 0; 00581 job->focus.x += PANFACTOR / job->zoom; 00582 job->needs_refresh = 1; 00583 return 0; 00584 } 00585 00586 static int right_cb(GVJ_t * job) 00587 { 00588 job->fit_mode = 0; 00589 job->focus.x -= PANFACTOR / job->zoom; 00590 job->needs_refresh = 1; 00591 return 0; 00592 } 00593 00594 static int up_cb(GVJ_t * job) 00595 { 00596 job->fit_mode = 0; 00597 job->focus.y += -(PANFACTOR / job->zoom); 00598 job->needs_refresh = 1; 00599 return 0; 00600 } 00601 00602 static int down_cb(GVJ_t * job) 00603 { 00604 job->fit_mode = 0; 00605 job->focus.y -= -(PANFACTOR / job->zoom); 00606 job->needs_refresh = 1; 00607 return 0; 00608 } 00609 00610 static int zoom_in_cb(GVJ_t * job) 00611 { 00612 job->fit_mode = 0; 00613 job->zoom *= ZOOMFACTOR; 00614 job->needs_refresh = 1; 00615 return 0; 00616 } 00617 00618 static int zoom_out_cb(GVJ_t * job) 00619 { 00620 job->fit_mode = 0; 00621 job->zoom /= ZOOMFACTOR; 00622 job->needs_refresh = 1; 00623 return 0; 00624 } 00625 00626 static int toggle_fit_cb(GVJ_t * job) 00627 { 00628 /*FIXME - should allow for margins */ 00629 /* - similar zoom_to_fit code exists in: */ 00630 /* plugin/gtk/callbacks.c */ 00631 /* plugin/xlib/gvdevice_xlib.c */ 00632 /* lib/gvc/gvevent.c */ 00633 00634 job->fit_mode = !job->fit_mode; 00635 if (job->fit_mode) { 00636 /* FIXME - this code looks wrong */ 00637 int dflt_width, dflt_height; 00638 dflt_width = job->width; 00639 dflt_height = job->height; 00640 job->zoom = 00641 MIN((double) job->width / (double) dflt_width, 00642 (double) job->height / (double) dflt_height); 00643 job->focus.x = 0.0; 00644 job->focus.y = 0.0; 00645 job->needs_refresh = 1; 00646 } 00647 return 0; 00648 } 00649 00650 static void gvevent_modify (GVJ_t * job, const char *name, const char *value) 00651 { 00652 /* FIXME */ 00653 } 00654 00655 static void gvevent_delete (GVJ_t * job) 00656 { 00657 /* FIXME */ 00658 } 00659 00660 static void gvevent_read (GVJ_t * job, const char *filename, const char *layout) 00661 { 00662 FILE *f; 00663 GVC_t *gvc; 00664 Agraph_t *g = NULL; 00665 gvlayout_engine_t *gvle; 00666 00667 gvc = job->gvc; 00668 if (!filename) { 00669 #ifndef WITH_CGRAPH 00670 g = agopen("G", AGDIGRAPH); 00671 #else /* WITH_CGRAPH */ 00672 g = agopen("G", Agdirected, NIL(Agdisc_t *)); 00673 #endif /* WITH_CGRAPH */ 00674 job->output_filename = "new.gv"; 00675 } 00676 else { 00677 f = fopen(filename, "r"); 00678 if (!f) 00679 return; /* FIXME - need some error handling */ 00680 #ifndef WITH_CGRAPH 00681 g = agread(f); 00682 #else /* WITH_CGRAPH */ 00683 g = agread(f,NIL(Agdisc_t *)); 00684 #endif /* WITH_CGRAPH */ 00685 fclose(f); 00686 } 00687 if (!g) 00688 return; /* FIXME - need some error handling */ 00689 if (gvc->g) { 00690 gvle = gvc->layout.engine; 00691 if (gvle && gvle->cleanup) 00692 gvle->cleanup(gvc->g); 00693 graph_cleanup(gvc->g); 00694 agclose(gvc->g); 00695 } 00696 gvc->g = g; 00697 GD_gvc(g) = gvc; 00698 gvLayout(gvc, g, layout); 00699 job->selected_obj = NULL; 00700 job->current_obj = NULL; 00701 job->needs_refresh = 1; 00702 } 00703 00704 static void gvevent_layout (GVJ_t * job, const char *layout) 00705 { 00706 gvLayout(job->gvc, job->gvc->g, layout); 00707 } 00708 00709 static void gvevent_render (GVJ_t * job, const char *format, const char *filename) 00710 { 00711 gvRenderFilename(job->gvc, job->gvc->g, format, filename); 00712 } 00713 00714 00715 gvevent_key_binding_t gvevent_key_binding[] = { 00716 {"Q", quit_cb}, 00717 {"Left", left_cb}, 00718 {"KP_Left", left_cb}, 00719 {"Right", right_cb}, 00720 {"KP_Right", right_cb}, 00721 {"Up", up_cb}, 00722 {"KP_Up", up_cb}, 00723 {"Down", down_cb}, 00724 {"KP_Down", down_cb}, 00725 {"plus", zoom_in_cb}, 00726 {"KP_Add", zoom_in_cb}, 00727 {"minus", zoom_out_cb}, 00728 {"KP_Subtract", zoom_out_cb}, 00729 {"F", toggle_fit_cb}, 00730 }; 00731 00732 int gvevent_key_binding_size = ARRAY_SIZE(gvevent_key_binding); 00733 00734 gvdevice_callbacks_t gvdevice_callbacks = { 00735 gvevent_refresh, 00736 gvevent_button_press, 00737 gvevent_button_release, 00738 gvevent_motion, 00739 gvevent_modify, 00740 gvevent_delete, 00741 gvevent_read, 00742 gvevent_layout, 00743 gvevent_render, 00744 };
1.7.5