1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
3 * Copyright (C) Massimo Cora' 2005 <maxcvs@email.it>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Library General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include <graphviz/gvc.h> /* graphviz */
22 #include <libanjuta/anjuta-debug.h>
23 #include <libanjuta/interfaces/ianjuta-document-manager.h>
24 #include <libanjuta/interfaces/ianjuta-symbol-manager.h>
27 #include "class-inherit.h"
28 #include "class-callbacks.h"
30 #define DEFAULT_GRAPH_NAME "Anjuta Graph"
31 #define CANVAS_MIN_SIZE 250
33 /* some macros to access deep graphviz's node structures */
34 #define NODE_LOWER_LEFT(node,main_index,rec_index) \
35 (((field_t*)ND_shape_info (node))->fld[main_index]->fld[rec_index]->b.LL)
37 #define NODE_UPPER_RIGHT(node,main_index,rec_index) \
38 (((field_t*)ND_shape_info (node))->fld[main_index]->fld[rec_index]->b.UR)
40 #define NODE_NUM_FIELDS(node) \
41 ((field_t*)ND_shape_info (node))->n_flds
43 #define NODE_NTH_FIELD(node,nth) \
44 ((field_t*)ND_shape_info (node))->fld[nth]
46 #define NODE_NTH_TEXT(node,main_index,rec_index) \
47 ((field_t*)ND_shape_info (node))->fld[main_index]->fld[rec_index]->lp->text
50 #define INCH_TO_PIXELS_CONVERSION_FACTOR 72
51 #define INCH_TO_PIXELS(inch_size) \
52 INCH_TO_PIXELS_CONVERSION_FACTOR * inch_size
55 /* TODO: check for symbol_updated event, and check in the nodestatus's hashtable
56 for the nodes that are gone. In case remove them.
61 cls_inherit_nodestatus_destroy (NodeExpansionStatus
*node
) {
69 class_inheritance_show_dynamic_class_popup_menu (GdkEvent
*event
,
72 GtkWidget
*item
, *image
;
73 GtkWidget
*checkitem
, *separator
;
75 /* Destroy the old menu before creating a new one */
78 gtk_widget_destroy (nodedata
->menu
);
81 nodedata
->menu
= gtk_menu_new();
82 if (nodedata
->name
&& strlen (nodedata
->name
))
84 IAnjutaSymbolManager
*sm
;
85 IAnjutaIterable
*iter_searched
;
86 IAnjutaIterable
*iter
;
87 IAnjutaSymbol
*symbol_searched
;
88 sm
= anjuta_shell_get_interface (ANJUTA_PLUGIN (nodedata
->plugin
)->shell
,
89 IAnjutaSymbolManager
, NULL
);
93 /* we cannot pass a simple 'name' to get_members () interface. That
94 * wouldn't be enought to identify uniquely the symbol itself.
95 * Think for example at two namespaces with two classes with the same
96 * name... Anyway the situation here wasn't better even before. The problem
97 * persists and to be solved it's needed a re-engineering of class-inherit.
99 iter_searched
= ianjuta_symbol_manager_search (sm
, IANJUTA_SYMBOL_TYPE_CLASS
,
101 IANJUTA_SYMBOL_FIELD_SIMPLE
,
107 if (iter_searched
== NULL
)
110 symbol_searched
= IANJUTA_SYMBOL (iter_searched
);
111 iter
= ianjuta_symbol_manager_get_members (sm
, symbol_searched
,
112 IANJUTA_SYMBOL_FIELD_SIMPLE
,
114 if (iter
&& ianjuta_iterable_get_length (iter
, NULL
) > 0)
116 IAnjutaSymbol
*symbol
= IANJUTA_SYMBOL (iter
);
119 const gchar
*name
, *file
;
120 const GdkPixbuf
*pixbuf
;
123 name
= ianjuta_symbol_get_name (symbol
, NULL
);
124 pixbuf
= ianjuta_symbol_get_icon (symbol
, NULL
);
125 file
= ianjuta_symbol_get_extra_info_string (symbol
,
126 IANJUTA_SYMBOL_FIELD_FILE_PATH
, NULL
);
127 line
= ianjuta_symbol_get_line (symbol
, NULL
);
129 item
= gtk_image_menu_item_new_with_label (name
);
130 image
= gtk_image_new_from_pixbuf ((GdkPixbuf
*)pixbuf
);
131 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM
136 g_object_set_data_full (G_OBJECT (item
), "__file",
137 g_strdup (file
), g_free
);
138 g_object_set_data (G_OBJECT (item
), "__line",
139 GINT_TO_POINTER (line
));
141 gtk_container_add (GTK_CONTAINER (nodedata
->menu
),
143 g_signal_connect (G_OBJECT (item
), "activate",
144 G_CALLBACK (on_member_menuitem_clicked
),
146 } while (ianjuta_iterable_next (iter
, NULL
));
150 g_object_unref (iter
);
154 g_object_unref (iter_searched
);
160 separator
= gtk_separator_menu_item_new ();
161 /* create the check menuitem */
162 checkitem
= gtk_check_menu_item_new_with_label (_("Fixed data-view"));
163 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (checkitem
),
166 g_signal_connect (G_OBJECT (checkitem
), "toggled",
167 G_CALLBACK (on_toggled_menuitem_clicked
),
170 gtk_container_add (GTK_CONTAINER (nodedata
->menu
), separator
);
171 gtk_container_add (GTK_CONTAINER (nodedata
->menu
), checkitem
);
173 gtk_widget_show_all (nodedata
->menu
);
174 gtk_menu_popup (GTK_MENU (nodedata
->menu
), NULL
, NULL
,
175 NULL
, NULL
, event
->button
.button
,
179 /*----------------------------------------------------------------------------
180 * initialize the internal graphviz structure.
183 cls_inherit_graph_init (AnjutaClassInheritance
*plugin
, gchar
* graph_label
)
186 plugin
->graph
= agopen (graph_label
, AGDIGRAPH
);
187 plugin
->gvc
= gvContext();
190 /*----------------------------------------------------------------------------
191 * Perform a dot_cleanup and a graph closing. Call this function at the end of
192 * call to draw_graph.
195 cls_inherit_graph_cleanup (AnjutaClassInheritance
*plugin
)
197 if (plugin
->graph
!= NULL
)
199 gvFreeLayout (plugin
->gvc
, plugin
->graph
);
200 agclose (plugin
->graph
);
203 if (plugin
->gvc
!= NULL
)
205 gvFreeContext (plugin
->gvc
);
208 plugin
->graph
= NULL
;
213 /*----------------------------------------------------------------------------
214 * destroys a NodeData element. All it's resources will be deallocated
215 * and setted to null.
218 cls_inherit_nodedata_destroy (NodeData
*node_data
)
222 g_free (node_data
->name
);
223 node_data
->name
= NULL
;
226 if (node_data
->canvas_item
)
228 gtk_object_destroy (GTK_OBJECT (node_data
->canvas_item
));
229 node_data
->canvas_item
= NULL
;
234 gtk_widget_destroy (node_data
->menu
);
235 node_data
->menu
= NULL
;
237 if (node_data
->sub_item
)
239 g_free (node_data
->sub_item
);
240 node_data
->sub_item
= NULL
;
242 node_data
->anchored
= FALSE
;
245 /*----------------------------------------------------------------------------
246 * clean the canvas and all its painted objects.
249 class_inheritance_clean_canvas (AnjutaClassInheritance
*plugin
)
251 if (plugin
->drawable_list
== NULL
|| plugin
->node_list
== NULL
)
254 /* destroying a gnome_canvas_item will un-paint automatically from
257 g_list_foreach (plugin
->drawable_list
, (GFunc
)gtk_object_destroy
, NULL
);
258 g_list_free(plugin
->drawable_list
);
260 /* the same for the nodes' list */
261 g_list_foreach (plugin
->node_list
, (GFunc
)cls_inherit_nodedata_destroy
,
263 g_list_free(plugin
->node_list
);
265 /* re-initializing the g_list */
266 plugin
->drawable_list
= NULL
;
267 plugin
->node_list
= NULL
;
271 /*----------------------------------------------------------------------------
272 * add a node to an Agraph. Check also if the node is yet in the hash_table so
273 * that we can build the label of the node with the class-data.
276 cls_inherit_add_node (AnjutaClassInheritance
*plugin
, const gchar
* node_name
)
280 NodeExpansionStatus
*node_status
;
282 /* if graph isn't initialized, init it */
284 cls_inherit_graph_init (plugin
, _(DEFAULT_GRAPH_NAME
));
286 /* let's add the node to the graph */
287 if ((node
= agnode (plugin
->graph
, (gchar
*)node_name
)) == NULL
)
290 /* check for the node in the hash_table */
292 (NodeExpansionStatus
*)g_hash_table_lookup
293 (plugin
->expansion_node_list
, node_name
)) != NULL
&&
294 node_status
->expansion_status
!= NODE_NOT_EXPANDED
) {
296 gint max_label_items
= 0;
297 gint real_items_length
= 0;
298 IAnjutaSymbolManager
*sm
;
300 if (!(sym
= agfindattr(plugin
->graph
->proto
->n
, "shape")))
301 sym
= agnodeattr(plugin
->graph
, "shape", "");
302 agxset(node
, sym
->index
, "record");
304 if (!(sym
= agfindattr(plugin
->graph
->proto
->n
, "label")))
305 sym
= agnodeattr(plugin
->graph
, "label", "");
307 label
= g_string_new ("");
308 g_string_printf (label
, "{%s", node_name
);
310 sm
= anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin
)->shell
,
311 IAnjutaSymbolManager
, NULL
);
314 IAnjutaIterable
*iter
;
315 IAnjutaIterable
*iter_searched
;
316 IAnjutaSymbol
*symbol_searched
;
317 iter_searched
= ianjuta_symbol_manager_search (sm
, IANJUTA_SYMBOL_TYPE_CLASS
,
319 IANJUTA_SYMBOL_FIELD_SIMPLE
,
325 if (iter_searched
== NULL
) {
326 g_string_free (label
, TRUE
);
330 symbol_searched
= IANJUTA_SYMBOL (iter_searched
);
332 iter
= ianjuta_symbol_manager_get_members (sm
, symbol_searched
,
333 IANJUTA_SYMBOL_FIELD_SIMPLE
,
335 real_items_length
= ianjuta_iterable_get_length (iter
, NULL
);
337 /* set the max number of items to draw */
338 if (real_items_length
<= NODE_HALF_DISPLAY_ELEM_NUM
||
339 node_status
->expansion_status
== NODE_FULL_EXPANDED
) {
340 max_label_items
= real_items_length
;
341 node_status
->expansion_status
= NODE_FULL_EXPANDED
;
344 max_label_items
= NODE_HALF_DISPLAY_ELEM_NUM
;
347 if (iter
&& real_items_length
> 0)
354 IAnjutaSymbol
*symbol
= IANJUTA_SYMBOL (iter
);
356 name
= ianjuta_symbol_get_name (symbol
, NULL
);
357 g_string_append_printf (label
, "|%s", name
);
359 } while (ianjuta_iterable_next (iter
, NULL
) && i
< max_label_items
);
362 g_object_unref (iter
);
365 g_object_unref (iter_searched
);
368 if (node_status
->expansion_status
== NODE_HALF_EXPANDED
&&
369 real_items_length
> NODE_HALF_DISPLAY_ELEM_NUM
) {
370 g_string_append_printf (label
, "|%s", NODE_SHOW_ALL_MEMBERS_STR
);
373 g_string_append_printf (label
, "|%s }", NODE_SHOW_NORMAL_VIEW_STR
);
374 agxset(node
, sym
->index
, label
->str
);
376 g_string_free (label
, TRUE
);
378 else { /* the node isn't in an expansion status.
379 * Go on setting a regular one */
381 /* Set an attribute - in this case one that affects the visible rendering */
382 if (!(sym
= agfindattr(plugin
->graph
->proto
->n
, "shape")))
383 sym
= agnodeattr(plugin
->graph
, "shape", "");
384 agxset(node
, sym
->index
, "box");
386 if (!(sym
= agfindattr(plugin
->graph
->proto
->n
, "label")))
387 sym
= agnodeattr(plugin
->graph
, "label", "");
388 agxset(node
, sym
->index
, node
->name
);
392 if (!(sym
= agfindattr(plugin
->graph
->proto
->n
, "fontname")))
393 sym
= agnodeattr(plugin
->graph
, "fontname", "");
394 agxset(node
, sym
->index
, "Courier new");
396 /* set the font-size */
397 if (!(sym
= agfindattr(plugin
->graph
->proto
->n
, "fontsize")))
398 sym
= agnodeattr(plugin
->graph
, "fontsize", "");
399 /* hack: set canvas_text_fontsize + 4 points or text would oversize the block */
400 /* add some more points for icons 16x16 space */
401 agxset(node
, sym
->index
, "17");
403 if (!(sym
= agfindattr(plugin
->graph
->proto
->n
, "ratio")))
404 sym
= agnodeattr(plugin
->graph
, "ratio", "");
405 agxset(node
, sym
->index
, "expand");
410 /*----------------------------------------------------------------------------
411 * add an edge to an Agraph.
414 cls_inherit_add_edge (AnjutaClassInheritance
*plugin
,
415 const gchar
* node_from
,
416 const gchar
* node_to
)
419 Agnode_t
*n_from
, *n_to
;
421 /* if we hadn't initialized out graph we return FALSE. Edges require
427 if ((n_from
= agfindnode (plugin
->graph
, (gchar
*)node_from
)) == NULL
)
430 if ((n_to
= agfindnode (plugin
->graph
, (gchar
*)node_to
)) == NULL
)
433 if ((edge
= agedge (plugin
->graph
, n_from
, n_to
)) == NULL
)
440 /*----------------------------------------------------------------------------
441 * Draw an expanded node. Function simplify cls_inherit_draw_graph().
444 cls_inherit_draw_expanded_node (AnjutaClassInheritance
*plugin
, Agnode_t
*node
,
445 point
* node_pos
, gdouble node_width
, gdouble node_height
) {
446 GnomeCanvasItem
*item
;
448 NodeExpansionStatus
*node_status
;
449 gint expansion_status
;
451 IAnjutaSymbolManager
*sm
;
452 IAnjutaIterable
*symbol_iter
= NULL
;
454 sm
= anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin
)->shell
,
455 IAnjutaSymbolManager
, NULL
);
460 IAnjutaIterable
*iter_searched
;
461 IAnjutaSymbol
*symbol_searched
;
462 iter_searched
= ianjuta_symbol_manager_search (sm
, IANJUTA_SYMBOL_TYPE_CLASS
,
464 IANJUTA_SYMBOL_FIELD_SIMPLE
,
470 if (iter_searched
== NULL
) {
474 symbol_searched
= IANJUTA_SYMBOL (iter_searched
);
476 symbol_iter
= ianjuta_symbol_manager_get_members (sm
, symbol_searched
,
477 IANJUTA_SYMBOL_FIELD_SIMPLE
,
480 /* we need to know which label to draw, wether only the "show all" or just
481 * the "normal view" */
483 (NodeExpansionStatus
*)g_hash_table_lookup
484 (plugin
->expansion_node_list
, node
->name
)) == NULL
) {
485 expansion_status
= NODE_NOT_EXPANDED
;
488 expansion_status
= node_status
->expansion_status
;
490 for (i
=0; i
< NODE_NUM_FIELDS (node
); i
++ ) {
491 for (j
=0; j
< NODE_NTH_FIELD (node
,i
)->n_flds
; j
++ ) {
493 y1
= NODE_LOWER_LEFT(node
,i
,j
).y
;
494 y2
= NODE_UPPER_RIGHT(node
,i
,j
).y
;
496 node_data
= g_new0 (NodeData
, 1);
498 /* set the plugin reference */
499 node_data
->plugin
= plugin
;
500 node_data
->anchored
= TRUE
;
501 node_data
->name
= g_strdup (node
->name
);
503 node_data
->sub_item
= g_strdup (NODE_NTH_TEXT (node
,i
,j
));
505 node_data
->canvas_item
=
506 gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (plugin
->canvas
)),
507 gnome_canvas_rect_get_type (),
509 (gdouble
) (node_pos
->x
-INCH_TO_PIXELS (node_width
)/2),
511 (gdouble
) -node_pos
->y
-INCH_TO_PIXELS (node_height
)/2 +
514 (gdouble
) (node_pos
->x
+INCH_TO_PIXELS (node_width
)/2),
516 (gdouble
) -node_pos
->y
-INCH_TO_PIXELS (node_height
)/2 +
519 &plugin
->canvas
->style
->base
[GTK_STATE_ACTIVE
],
522 /* add to the nodelist: we'll set the __uri and __line properites later
523 * on this loop, when we'll parse symbols. */
524 plugin
->node_list
= g_list_prepend (plugin
->node_list
, node_data
);
526 g_signal_connect (GTK_OBJECT (node_data
->canvas_item
), "event",
527 G_CALLBACK (on_nodedata_expanded_event
),
531 item
= gnome_canvas_item_new (gnome_canvas_root
532 (GNOME_CANVAS (plugin
->canvas
)),
533 gnome_canvas_text_get_type (),
534 "text", NODE_NTH_TEXT (node
,i
,j
),
535 "font", NODE_FONT_DEFAULT
,
536 "justification", GTK_JUSTIFY_CENTER
,
537 "style", PANGO_STYLE_ITALIC
,
538 "anchor", GTK_ANCHOR_CENTER
,
540 (gdouble
) (node_pos
->x
-
541 INCH_TO_PIXELS (node_width
)/2 + 20),
542 "y", (gdouble
) -node_pos
->y
-
543 INCH_TO_PIXELS (node_height
)/2+(j
+0.5)*abs(y1
-y2
),
545 &plugin
->canvas
->style
->text
[GTK_STATE_ACTIVE
],
546 "anchor", GTK_ANCHOR_W
,
548 plugin
->drawable_list
= g_list_prepend (plugin
->drawable_list
, item
);
550 if (j
== 0) { /* the Class' name case: make it bold */
551 gnome_canvas_item_set (item
,
552 "weight", PANGO_WEIGHT_BOLD
,
553 "style", PANGO_STYLE_NORMAL
,
557 else /* we need to draw the last 2 elements differently */
558 if (expansion_status
== NODE_HALF_EXPANDED
&&
559 j
> (NODE_NTH_FIELD (node
,i
)->n_flds
-3)) {
560 gnome_canvas_item_set (item
,
561 "weight", PANGO_WEIGHT_HEAVY
,
562 "style", PANGO_STYLE_NORMAL
,
566 else /* only the last one. Usually "Normal view" */
567 if (expansion_status
== NODE_FULL_EXPANDED
&&
568 j
> (NODE_NTH_FIELD (node
,i
)->n_flds
-2)) {
569 gnome_canvas_item_set (item
,
570 "weight", PANGO_WEIGHT_HEAVY
,
571 "style", PANGO_STYLE_NORMAL
,
576 /* go on with the icons */
577 if (symbol_iter
&& ianjuta_iterable_get_length (symbol_iter
, NULL
) > 0) {
578 const GdkPixbuf
*pixbuf
;
581 IAnjutaSymbol
*symbol
= IANJUTA_SYMBOL (symbol_iter
);
583 uri
= ianjuta_symbol_get_uri (symbol
, NULL
);
584 line
= ianjuta_symbol_get_line (symbol
, NULL
);
585 pixbuf
= ianjuta_symbol_get_icon (symbol
, NULL
);
587 item
= gnome_canvas_item_new ( gnome_canvas_root
588 (GNOME_CANVAS (plugin
->canvas
)),
589 gnome_canvas_pixbuf_get_type(),
591 (gdouble
) (node_pos
->x
-
592 INCH_TO_PIXELS (node_width
)/2 +2),
594 (gdouble
) -node_pos
->y
-
595 INCH_TO_PIXELS (node_height
)/2+(j
+0.5)*abs(y1
-y2
)-5,
599 /* set now the object properties on node_data. We still have a
600 * reference to it so we can access its canvas_item */
602 g_object_set_data_full (G_OBJECT (node_data
->canvas_item
), "__uri",
603 g_strdup (uri
), g_free
);
604 g_object_set_data (G_OBJECT (node_data
->canvas_item
), "__line",
605 GINT_TO_POINTER (line
));
608 plugin
->drawable_list
= g_list_prepend (plugin
->drawable_list
, item
);
609 ianjuta_iterable_next (symbol_iter
, NULL
);
614 g_object_unref (symbol_iter
);
617 g_object_unref (iter_searched
);
619 /* make the outline bounds */
621 gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (plugin
->canvas
)),
622 gnome_canvas_rect_get_type (),
624 (gdouble
) (node_pos
->x
-INCH_TO_PIXELS (node_width
)/2-1 ),
626 (gdouble
) -node_pos
->y
-INCH_TO_PIXELS (node_height
)/2 -1,
628 (gdouble
) node_pos
->x
+INCH_TO_PIXELS (node_width
)/2+1,
630 (gdouble
) -node_pos
->y
+INCH_TO_PIXELS (node_height
)/2 -1,
632 &plugin
->canvas
->style
->text
[GTK_STATE_ACTIVE
],
636 plugin
->drawable_list
= g_list_prepend (plugin
->drawable_list
, item
);
640 cls_inherit_draw_single_node (AnjutaClassInheritance
*plugin
, Agnode_t
*node
,
641 point
*node_pos
, gdouble node_width
, gdouble node_height
) {
643 GnomeCanvasItem
*item
;
644 gdouble text_width_value
;
646 node_data
= g_new0 (NodeData
, 1);
648 /* set the plugin reference */
649 node_data
->plugin
= plugin
;
650 node_data
->anchored
= FALSE
;
651 node_data
->name
= g_strdup (node
->name
);
652 node_data
->sub_item
= NULL
;
654 node_data
->canvas_item
=
655 gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (plugin
->canvas
)),
656 gnome_canvas_rect_get_type (),
658 (gdouble
) (node_pos
->x
- INCH_TO_PIXELS (node_width
)/2),
660 (gdouble
) -(node_pos
->y
- INCH_TO_PIXELS (node_height
)/2),
662 (gdouble
) (node_pos
->x
+ INCH_TO_PIXELS (node_width
)/2),
664 (gdouble
) -(node_pos
->y
+ INCH_TO_PIXELS (node_height
)/2),
666 &plugin
->canvas
->style
->base
[GTK_STATE_NORMAL
],
668 &plugin
->canvas
->style
->text
[GTK_STATE_NORMAL
],
672 plugin
->node_list
= g_list_prepend (plugin
->node_list
, node_data
);
674 g_signal_connect (GTK_OBJECT (node_data
->canvas_item
), "event",
675 G_CALLBACK (on_nodedata_event
),
679 item
= gnome_canvas_item_new (gnome_canvas_root
680 (GNOME_CANVAS (plugin
->canvas
)),
681 gnome_canvas_text_get_type (),
683 "font", NODE_FONT_DEFAULT
,
684 "justification", GTK_JUSTIFY_CENTER
,
685 "anchor", GTK_ANCHOR_CENTER
,
687 (gdouble
) (node_pos
->x
- INCH_TO_PIXELS (node_width
)/2),
688 "y", (gdouble
) -node_pos
->y
,
690 &plugin
->canvas
->style
->text
[GTK_STATE_NORMAL
],
691 "anchor", GTK_ANCHOR_W
,
694 /* center the text in the node... */
695 g_object_get (item
, "text_width", &text_width_value
, NULL
);
697 gnome_canvas_item_set (item
, "x",
698 (gdouble
)((node_pos
->x
- text_width_value
/2)), NULL
);
700 plugin
->drawable_list
= g_list_prepend (plugin
->drawable_list
, item
);
704 /*----------------------------------------------------------------------------
705 * draw the graph on the canvas. So nodes, edges, arrows, texts..
706 * If something is found already drawn on the canvas it is cleaned before
710 cls_inherit_draw_graph (AnjutaClassInheritance
*plugin
)
713 gdouble max_canvas_size_x
, max_canvas_size_y
;
714 GnomeCanvasItem
*item
;
718 if (plugin
->graph
== NULL
)
721 DEBUG_PRINT ("======== going to draw graph ========");
722 num_nodes
= agnnodes (plugin
->graph
);
723 g_return_if_fail (num_nodes
> 0);
725 /* compiles nodes/edges informations, such as positions, coordinates etc */
726 gvLayout (plugin
->gvc
, plugin
->graph
, "dot");
727 //dot_layout (plugin->graph);
729 /* set the size of the canvas. We need this to set the scrolling.. */
730 max_canvas_size_x
= max_canvas_size_y
= CANVAS_MIN_SIZE
;
732 /* check whether we had already drawn something on the canvas.
733 * In case remove the items so we can clean up the canvas ready
736 if (g_list_length (plugin
->drawable_list
) > 0 ||
737 g_list_length (plugin
->node_list
) > 0)
739 class_inheritance_clean_canvas (plugin
);
742 /* first of all draw the nodes */
743 for (node
= agfstnode (plugin
->graph
); node
;
744 node
= agnxtnode (plugin
->graph
, node
))
750 /* get some infos from the node */
751 node_pos
= ND_coord_i(node
);
752 node_width
= ND_width (node
);
753 node_height
= ND_height (node
);
755 if (strcmp ("record", ND_shape (node
)->name
) == 0 ) {
756 cls_inherit_draw_expanded_node (plugin
, node
, &node_pos
, node_width
, node_height
);
758 else /* it's a normal single node */
759 cls_inherit_draw_single_node (plugin
, node
, &node_pos
, node_width
, node_height
);
762 for (edge
= agfstedge (plugin
->graph
, node
); edge
;
763 edge
= agnxtedge (plugin
->graph
, edge
, node
))
765 GnomeCanvasPathDef
*path_def
;
768 path_def
= gnome_canvas_path_def_new();
770 for ( i
= 0; i
< ED_spl(edge
)->list
->size
-1; i
+=3)
773 /* go on with bezier curves. We can retrieve the info such
774 * as control points from the struct of the edge
776 gnome_canvas_path_def_moveto (path_def
,
777 ((ED_spl(edge
))->list
->list
[0+i
]).x
,
778 -((ED_spl(edge
))->list
->list
[0+i
]).y
);
780 gnome_canvas_path_def_curveto (path_def
,
781 ((ED_spl(edge
))->list
->list
[1+i
]).x
,
782 -((ED_spl(edge
))->list
->list
[1+i
]).y
,
783 ((ED_spl(edge
))->list
->list
[2+i
]).x
,
784 -((ED_spl(edge
))->list
->list
[2+i
]).y
,
785 ((ED_spl(edge
))->list
->list
[3+i
]).x
,
786 -((ED_spl(edge
))->list
->list
[3+i
]).y
);
788 /* check whether we have to draw an arrow. Is the right point? */
789 if ( i
+3 >= (ED_spl(edge
)->list
->size
-1) )
791 GnomeCanvasPoints
* points
;
792 gdouble upper_bound
= (gdouble
)(node_pos
.y
+
793 INCH_TO_PIXELS (node_height
)/2);
805 h
= abs (((ED_spl(edge
))->list
->list
[3+i
]).y
- upper_bound
);
807 if ((((ED_spl(edge
))->list
->list
[3+i
]).x
-
808 ((ED_spl(edge
))->list
->list
[2+i
]).x
) > 0)
809 x_offset
= sqrt( abs(10*10 - h
*h
));
811 x_offset
= -sqrt( abs(10*10 - h
*h
));
813 /* let's draw a canvas_line with an arrow-end */
814 points
= gnome_canvas_points_new (2);
817 points
->coords
[0] = ((ED_spl(edge
))->list
->list
[3+i
]).x
;
818 points
->coords
[1] = -((ED_spl(edge
))->list
->list
[3+i
]).y
;
822 ((ED_spl(edge
))->list
->list
[3+i
]).x
+ x_offset
;
823 points
->coords
[3] = -upper_bound
;
825 /* ok we take an arrow_max_length of 10 pixels for default. */
826 if ( abs(x_offset
) <= 10 )
829 gnome_canvas_item_new (gnome_canvas_root
830 (GNOME_CANVAS (plugin
->canvas
)),
831 gnome_canvas_line_get_type(),
834 &plugin
->canvas
->style
->text
[GTK_STATE_NORMAL
],
835 "last_arrowhead", TRUE
,
836 "arrow_shape_a", 10.0,
837 "arrow_shape_b", 10.0,
838 "arrow_shape_c", 4.0,
841 plugin
->drawable_list
=
842 g_list_prepend (plugin
->drawable_list
, item
);
847 /* draw the path_def */
848 item
= gnome_canvas_item_new (gnome_canvas_root
849 (GNOME_CANVAS (plugin
->canvas
)),
850 gnome_canvas_bpath_get_type(),
853 &plugin
->canvas
->style
->text
[GTK_STATE_NORMAL
],
856 plugin
->drawable_list
=
857 g_list_prepend (plugin
->drawable_list
, item
);
860 if (abs(node_pos
.x
) > max_canvas_size_x
)
861 max_canvas_size_x
= abs(node_pos
.x
) + INCH_TO_PIXELS (node_width
)/2;
863 if (abs(node_pos
.y
+ node_height
) > max_canvas_size_y
)
864 max_canvas_size_y
= abs(node_pos
.y
) + INCH_TO_PIXELS (node_height
)/2;
867 gtk_widget_set_size_request (plugin
->canvas
, max_canvas_size_x
+100,
868 max_canvas_size_y
+100);
869 gnome_canvas_set_scroll_region ( GNOME_CANVAS (plugin
->canvas
), -50,
870 50, max_canvas_size_x
+ 50,
871 -max_canvas_size_y
-100);
873 cls_inherit_graph_cleanup (plugin
);
876 /*----------------------------------------------------------------------------
877 * update the internal graphviz graph-structure, then redraw the graph on the
881 class_inheritance_update_graph (AnjutaClassInheritance
*plugin
)
883 IAnjutaSymbolManager
*sm
;
884 IAnjutaIterable
*iter
;
885 IAnjutaSymbol
*symbol
;
886 GList
*classes
, *node
;
887 GHashTable
*class_parents
;
889 g_return_if_fail (plugin
!= NULL
);
891 if (plugin
->top_dir
== NULL
)
894 sm
= anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin
)->shell
,
895 IAnjutaSymbolManager
, NULL
);
899 iter
= ianjuta_symbol_manager_search (sm
, IANJUTA_SYMBOL_TYPE_CLASS
,
901 IANJUTA_SYMBOL_FIELD_SIMPLE
,
902 NULL
, FALSE
, TRUE
, NULL
);
905 DEBUG_PRINT ("class_inheritance_update_graph (): search returned no items.");
909 DEBUG_PRINT ("Number of classes found = %d",
910 ianjuta_iterable_get_length (iter
, NULL
));
913 const gchar *class_name;
914 symbol = IANJUTA_SYMBOL (iter);
915 class_name = ianjuta_symbol_get_name (symbol, NULL);
916 DEBUG_PRINT ("=======> %s", class_name);
918 while (ianjuta_iterable_next (iter, NULL) == TRUE);
920 ianjuta_iterable_first (iter
, NULL
);
921 if (ianjuta_iterable_get_length (iter
, NULL
) <= 0)
923 g_object_unref (iter
);
927 /* Get all classes */
929 class_parents
= g_hash_table_new_full (g_str_hash
, g_str_equal
,
930 g_free
, g_object_unref
);
932 const gchar
*class_name
, *old_parents
;
933 IAnjutaIterable
*parents
;
935 symbol
= IANJUTA_SYMBOL (iter
);
937 /* get parents of the current class */
938 parents
= ianjuta_symbol_manager_get_class_parents (sm
, symbol
,
939 IANJUTA_SYMBOL_FIELD_SIMPLE
,
942 if (parents
== NULL
|| ianjuta_iterable_get_length (parents
, NULL
) <= 0)
944 DEBUG_PRINT ("continuing 1...");
948 class_name
= ianjuta_symbol_get_name (symbol
, NULL
);
950 if ((old_parents
= g_hash_table_lookup (class_parents
, class_name
)))
952 /* we already have a class inserted with that name */
953 DEBUG_PRINT ("continuing 2...");
957 DEBUG_PRINT ("parsed %s", class_name
);
958 /* insert into the hash table a class name together with the associated parents */
959 g_hash_table_insert (class_parents
, g_strdup (class_name
), parents
);
960 classes
= g_list_prepend (classes
, g_strdup (class_name
));
961 } while (ianjuta_iterable_next (iter
, NULL
) == TRUE
);
963 classes
= g_list_reverse (classes
);
965 /* we don't need the iter anymore */
966 g_object_unref (iter
);
968 /* For all classes get their parents */
972 const gchar
*class_name
;
973 IAnjutaIterable
*parents
;
975 class_name
= node
->data
;
976 parents
= g_hash_table_lookup (class_parents
, class_name
);
979 IAnjutaSymbol
*symbol
;
980 symbol
= IANJUTA_SYMBOL (parents
);
981 const gchar
*parent_name
;
983 parent_name
= ianjuta_symbol_get_name (symbol
, NULL
);
985 cls_inherit_add_node (plugin
, class_name
);
986 cls_inherit_add_node (plugin
, parent_name
);
987 cls_inherit_add_edge (plugin
, parent_name
, class_name
);
988 } while (ianjuta_iterable_next (parents
, NULL
) == TRUE
);
990 /* parse next deriver class in the glist */
991 node
= g_list_next (node
);
993 g_list_foreach (classes
, (GFunc
)g_free
, NULL
);
994 g_list_free (classes
);
995 g_hash_table_destroy (class_parents
);
996 cls_inherit_draw_graph (plugin
);
999 static GnomeUIInfo canvas_menu_uiinfo
[] = {
1003 N_("Update the graph"),
1004 on_update_menu_item_selected
,
1007 GNOME_APP_PIXMAP_NONE
,
1016 class_inheritance_hash_table_clear (AnjutaClassInheritance
*plugin
) {
1018 if (plugin
->expansion_node_list
== NULL
)
1021 /* destroy the nodestatus hash table */
1022 g_hash_table_destroy (plugin
->expansion_node_list
);
1024 /* reinitialize the table */
1025 plugin
->expansion_node_list
= g_hash_table_new_full (g_str_hash
,
1026 g_str_equal
, g_free
,
1027 (GDestroyNotify
)cls_inherit_nodestatus_destroy
);
1031 class_inheritance_base_gui_init (AnjutaClassInheritance
*plugin
)
1033 GtkWidget
*s_window
;
1035 s_window
= gtk_scrolled_window_new (NULL
, NULL
);
1036 plugin
->canvas
= gnome_canvas_new_aa ();
1037 //gtk_widget_modify_bg (plugin->canvas, GTK_STATE_NORMAL,
1038 // &plugin->canvas->style->base[GTK_STATE_NORMAL]);
1039 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (s_window
),
1040 GTK_POLICY_AUTOMATIC
,
1041 GTK_POLICY_AUTOMATIC
);
1042 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (s_window
),
1045 gtk_widget_set_size_request (plugin
->canvas
, CANVAS_MIN_SIZE
,
1047 gnome_canvas_set_scroll_region (GNOME_CANVAS (plugin
->canvas
),
1051 -CANVAS_MIN_SIZE
/2);
1053 g_signal_connect (G_OBJECT (plugin
->canvas
), "event",
1054 G_CALLBACK (on_canvas_event
),
1057 g_signal_connect (G_OBJECT (plugin
->canvas
),
1059 G_CALLBACK (on_style_set
),
1062 plugin
->widget
= gtk_vbox_new (FALSE
, 2);
1065 gtk_box_pack_start (GTK_BOX (plugin
->widget
), s_window
, TRUE
, TRUE
, TRUE
);
1067 gtk_widget_show_all (plugin
->widget
);
1069 /* create new GList */
1070 plugin
->drawable_list
= NULL
;
1071 plugin
->node_list
= NULL
;
1073 plugin
->expansion_node_list
= g_hash_table_new_full (g_str_hash
,
1074 g_str_equal
, g_free
,
1075 (GDestroyNotify
)cls_inherit_nodestatus_destroy
);
1078 plugin
->menu
= gtk_menu_new ();
1080 /* set the user data on update selection */
1081 canvas_menu_uiinfo
[0].user_data
= plugin
;
1083 gnome_app_fill_menu (GTK_MENU_SHELL (plugin
->menu
), canvas_menu_uiinfo
,
1086 plugin
->update
= canvas_menu_uiinfo
[0].widget
;
1088 gtk_widget_ref (plugin
->menu
);
1089 gtk_widget_ref (plugin
->update
);