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
,
110 if (iter_searched
== NULL
)
113 symbol_searched
= IANJUTA_SYMBOL (iter_searched
);
114 iter
= ianjuta_symbol_manager_get_members (sm
, symbol_searched
,
115 IANJUTA_SYMBOL_FIELD_SIMPLE
,
117 if (iter
&& ianjuta_iterable_get_length (iter
, NULL
) > 0)
119 IAnjutaSymbol
*symbol
= IANJUTA_SYMBOL (iter
);
122 const gchar
*name
, *file
;
123 const GdkPixbuf
*pixbuf
;
126 name
= ianjuta_symbol_get_name (symbol
, NULL
);
127 pixbuf
= ianjuta_symbol_get_icon (symbol
, NULL
);
128 file
= ianjuta_symbol_get_extra_info_string (symbol
,
129 IANJUTA_SYMBOL_FIELD_FILE_PATH
, NULL
);
130 line
= ianjuta_symbol_get_line (symbol
, NULL
);
132 item
= gtk_image_menu_item_new_with_label (name
);
133 image
= gtk_image_new_from_pixbuf ((GdkPixbuf
*)pixbuf
);
134 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM
139 g_object_set_data_full (G_OBJECT (item
), "__file_path",
140 g_strdup (file
), g_free
);
141 g_object_set_data (G_OBJECT (item
), "__line",
142 GINT_TO_POINTER (line
));
144 gtk_container_add (GTK_CONTAINER (nodedata
->menu
),
146 g_signal_connect (G_OBJECT (item
), "activate",
147 G_CALLBACK (on_member_menuitem_clicked
),
149 } while (ianjuta_iterable_next (iter
, NULL
));
153 g_object_unref (iter
);
157 g_object_unref (iter_searched
);
163 separator
= gtk_separator_menu_item_new ();
164 /* create the check menuitem */
165 checkitem
= gtk_check_menu_item_new_with_label (_("Fixed data-view"));
166 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (checkitem
),
169 g_signal_connect (G_OBJECT (checkitem
), "toggled",
170 G_CALLBACK (on_toggled_menuitem_clicked
),
173 gtk_container_add (GTK_CONTAINER (nodedata
->menu
), separator
);
174 gtk_container_add (GTK_CONTAINER (nodedata
->menu
), checkitem
);
176 gtk_widget_show_all (nodedata
->menu
);
177 gtk_menu_popup (GTK_MENU (nodedata
->menu
), NULL
, NULL
,
178 NULL
, NULL
, event
->button
.button
,
182 /*----------------------------------------------------------------------------
183 * initialize the internal graphviz structure.
186 cls_inherit_graph_init (AnjutaClassInheritance
*plugin
, gchar
* graph_label
)
189 plugin
->graph
= agopen (graph_label
, AGDIGRAPH
);
190 plugin
->gvc
= gvContext();
193 /*----------------------------------------------------------------------------
194 * Perform a dot_cleanup and a graph closing. Call this function at the end of
195 * call to draw_graph.
198 cls_inherit_graph_cleanup (AnjutaClassInheritance
*plugin
)
200 if (plugin
->graph
!= NULL
)
202 gvFreeLayout (plugin
->gvc
, plugin
->graph
);
203 agclose (plugin
->graph
);
206 if (plugin
->gvc
!= NULL
)
208 gvFreeContext (plugin
->gvc
);
211 plugin
->graph
= NULL
;
216 /*----------------------------------------------------------------------------
217 * destroys a NodeData element. All it's resources will be deallocated
218 * and setted to null.
221 cls_inherit_nodedata_destroy (NodeData
*node_data
)
225 g_free (node_data
->name
);
226 node_data
->name
= NULL
;
229 if (node_data
->canvas_item
)
231 gtk_object_destroy (GTK_OBJECT (node_data
->canvas_item
));
232 node_data
->canvas_item
= NULL
;
237 gtk_widget_destroy (node_data
->menu
);
238 node_data
->menu
= NULL
;
240 if (node_data
->sub_item
)
242 g_free (node_data
->sub_item
);
243 node_data
->sub_item
= NULL
;
245 node_data
->anchored
= FALSE
;
248 /*----------------------------------------------------------------------------
249 * clean the canvas and all its painted objects.
252 class_inheritance_clean_canvas (AnjutaClassInheritance
*plugin
)
254 if (plugin
->drawable_list
== NULL
|| plugin
->node_list
== NULL
)
257 /* destroying a gnome_canvas_item will un-paint automatically from
260 g_list_foreach (plugin
->drawable_list
, (GFunc
)gtk_object_destroy
, NULL
);
261 g_list_free(plugin
->drawable_list
);
263 /* the same for the nodes' list */
264 g_list_foreach (plugin
->node_list
, (GFunc
)cls_inherit_nodedata_destroy
,
266 g_list_free(plugin
->node_list
);
268 /* re-initializing the g_list */
269 plugin
->drawable_list
= NULL
;
270 plugin
->node_list
= NULL
;
274 /*----------------------------------------------------------------------------
275 * add a node to an Agraph. Check also if the node is yet in the hash_table so
276 * that we can build the label of the node with the class-data.
279 cls_inherit_add_node (AnjutaClassInheritance
*plugin
, const gchar
* node_name
)
283 NodeExpansionStatus
*node_status
;
285 /* if graph isn't initialized, init it */
287 cls_inherit_graph_init (plugin
, _(DEFAULT_GRAPH_NAME
));
289 /* let's add the node to the graph */
290 if ((node
= agnode (plugin
->graph
, (gchar
*)node_name
)) == NULL
)
293 /* check for the node in the hash_table */
295 (NodeExpansionStatus
*)g_hash_table_lookup
296 (plugin
->expansion_node_list
, node_name
)) != NULL
&&
297 node_status
->expansion_status
!= NODE_NOT_EXPANDED
) {
299 gint max_label_items
= 0;
300 gint real_items_length
= 0;
301 IAnjutaSymbolManager
*sm
;
303 if (!(sym
= agfindattr(plugin
->graph
->proto
->n
, "shape")))
304 sym
= agnodeattr(plugin
->graph
, "shape", "");
305 agxset(node
, sym
->index
, "record");
307 if (!(sym
= agfindattr(plugin
->graph
->proto
->n
, "label")))
308 sym
= agnodeattr(plugin
->graph
, "label", "");
310 label
= g_string_new ("");
311 g_string_printf (label
, "{%s", node_name
);
313 sm
= anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin
)->shell
,
314 IAnjutaSymbolManager
, NULL
);
317 IAnjutaIterable
*iter
;
318 IAnjutaIterable
*iter_searched
;
319 IAnjutaSymbol
*symbol_searched
;
320 iter_searched
= ianjuta_symbol_manager_search (sm
, IANJUTA_SYMBOL_TYPE_CLASS
,
322 IANJUTA_SYMBOL_FIELD_SIMPLE
,
331 if (iter_searched
== NULL
) {
332 g_string_free (label
, TRUE
);
336 symbol_searched
= IANJUTA_SYMBOL (iter_searched
);
338 iter
= ianjuta_symbol_manager_get_members (sm
, symbol_searched
,
339 IANJUTA_SYMBOL_FIELD_SIMPLE
,
341 real_items_length
= ianjuta_iterable_get_length (iter
, NULL
);
343 /* set the max number of items to draw */
344 if (real_items_length
<= NODE_HALF_DISPLAY_ELEM_NUM
||
345 node_status
->expansion_status
== NODE_FULL_EXPANDED
) {
346 max_label_items
= real_items_length
;
347 node_status
->expansion_status
= NODE_FULL_EXPANDED
;
350 max_label_items
= NODE_HALF_DISPLAY_ELEM_NUM
;
353 if (iter
&& real_items_length
> 0)
360 IAnjutaSymbol
*symbol
= IANJUTA_SYMBOL (iter
);
362 name
= ianjuta_symbol_get_name (symbol
, NULL
);
363 g_string_append_printf (label
, "|%s", name
);
365 } while (ianjuta_iterable_next (iter
, NULL
) && i
< max_label_items
);
368 g_object_unref (iter
);
371 g_object_unref (iter_searched
);
374 if (node_status
->expansion_status
== NODE_HALF_EXPANDED
&&
375 real_items_length
> NODE_HALF_DISPLAY_ELEM_NUM
) {
376 g_string_append_printf (label
, "|%s", NODE_SHOW_ALL_MEMBERS_STR
);
379 g_string_append_printf (label
, "|%s }", NODE_SHOW_NORMAL_VIEW_STR
);
380 agxset(node
, sym
->index
, label
->str
);
382 g_string_free (label
, TRUE
);
384 else { /* the node isn't in an expansion status.
385 * Go on setting a regular one */
387 /* Set an attribute - in this case one that affects the visible rendering */
388 if (!(sym
= agfindattr(plugin
->graph
->proto
->n
, "shape")))
389 sym
= agnodeattr(plugin
->graph
, "shape", "");
390 agxset(node
, sym
->index
, "box");
392 if (!(sym
= agfindattr(plugin
->graph
->proto
->n
, "label")))
393 sym
= agnodeattr(plugin
->graph
, "label", "");
394 agxset(node
, sym
->index
, node
->name
);
398 if (!(sym
= agfindattr(plugin
->graph
->proto
->n
, "fontname")))
399 sym
= agnodeattr(plugin
->graph
, "fontname", "");
400 agxset(node
, sym
->index
, "Courier new");
402 /* set the font-size */
403 if (!(sym
= agfindattr(plugin
->graph
->proto
->n
, "fontsize")))
404 sym
= agnodeattr(plugin
->graph
, "fontsize", "");
405 /* hack: set canvas_text_fontsize + 4 points or text would oversize the block */
406 /* add some more points for icons 16x16 space */
407 agxset(node
, sym
->index
, "17");
409 if (!(sym
= agfindattr(plugin
->graph
->proto
->n
, "ratio")))
410 sym
= agnodeattr(plugin
->graph
, "ratio", "");
411 agxset(node
, sym
->index
, "expand");
416 /*----------------------------------------------------------------------------
417 * add an edge to an Agraph.
420 cls_inherit_add_edge (AnjutaClassInheritance
*plugin
,
421 const gchar
* node_from
,
422 const gchar
* node_to
)
425 Agnode_t
*n_from
, *n_to
;
427 /* if we hadn't initialized out graph we return FALSE. Edges require
433 if ((n_from
= agfindnode (plugin
->graph
, (gchar
*)node_from
)) == NULL
)
436 if ((n_to
= agfindnode (plugin
->graph
, (gchar
*)node_to
)) == NULL
)
439 if ((edge
= agedge (plugin
->graph
, n_from
, n_to
)) == NULL
)
446 /*----------------------------------------------------------------------------
447 * Draw an expanded node. Function simplify cls_inherit_draw_graph().
450 cls_inherit_draw_expanded_node (AnjutaClassInheritance
*plugin
, Agnode_t
*node
,
451 point
* node_pos
, gdouble node_width
, gdouble node_height
) {
452 GnomeCanvasItem
*item
;
454 NodeExpansionStatus
*node_status
;
455 gint expansion_status
;
457 IAnjutaSymbolManager
*sm
;
458 IAnjutaIterable
*symbol_iter
= NULL
;
460 sm
= anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin
)->shell
,
461 IAnjutaSymbolManager
, NULL
);
466 IAnjutaIterable
*iter_searched
;
467 IAnjutaSymbol
*symbol_searched
;
468 iter_searched
= ianjuta_symbol_manager_search (sm
, IANJUTA_SYMBOL_TYPE_CLASS
,
470 IANJUTA_SYMBOL_FIELD_SIMPLE
,
479 if (iter_searched
== NULL
) {
483 symbol_searched
= IANJUTA_SYMBOL (iter_searched
);
485 symbol_iter
= ianjuta_symbol_manager_get_members (sm
, symbol_searched
,
486 IANJUTA_SYMBOL_FIELD_SIMPLE
,
489 /* we need to know which label to draw, wether only the "show all" or just
490 * the "normal view" */
492 (NodeExpansionStatus
*)g_hash_table_lookup
493 (plugin
->expansion_node_list
, node
->name
)) == NULL
) {
494 expansion_status
= NODE_NOT_EXPANDED
;
497 expansion_status
= node_status
->expansion_status
;
499 for (i
=0; i
< NODE_NUM_FIELDS (node
); i
++ ) {
500 for (j
=0; j
< NODE_NTH_FIELD (node
,i
)->n_flds
; j
++ ) {
502 y1
= NODE_LOWER_LEFT(node
,i
,j
).y
;
503 y2
= NODE_UPPER_RIGHT(node
,i
,j
).y
;
505 node_data
= g_new0 (NodeData
, 1);
507 /* set the plugin reference */
508 node_data
->plugin
= plugin
;
509 node_data
->anchored
= TRUE
;
510 node_data
->name
= g_strdup (node
->name
);
512 node_data
->sub_item
= g_strdup (NODE_NTH_TEXT (node
,i
,j
));
514 node_data
->canvas_item
=
515 gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (plugin
->canvas
)),
516 gnome_canvas_rect_get_type (),
518 (gdouble
) (node_pos
->x
-INCH_TO_PIXELS (node_width
)/2),
520 (gdouble
) -node_pos
->y
-INCH_TO_PIXELS (node_height
)/2 +
523 (gdouble
) (node_pos
->x
+INCH_TO_PIXELS (node_width
)/2),
525 (gdouble
) -node_pos
->y
-INCH_TO_PIXELS (node_height
)/2 +
528 &plugin
->canvas
->style
->base
[GTK_STATE_ACTIVE
],
531 /* add to the nodelist: we'll set the __uri and __line properites later
532 * on this loop, when we'll parse symbols. */
533 plugin
->node_list
= g_list_prepend (plugin
->node_list
, node_data
);
535 g_signal_connect (GTK_OBJECT (node_data
->canvas_item
), "event",
536 G_CALLBACK (on_nodedata_expanded_event
),
540 item
= gnome_canvas_item_new (gnome_canvas_root
541 (GNOME_CANVAS (plugin
->canvas
)),
542 gnome_canvas_text_get_type (),
543 "text", NODE_NTH_TEXT (node
,i
,j
),
544 "font", NODE_FONT_DEFAULT
,
545 "justification", GTK_JUSTIFY_CENTER
,
546 "style", PANGO_STYLE_ITALIC
,
547 "anchor", GTK_ANCHOR_CENTER
,
549 (gdouble
) (node_pos
->x
-
550 INCH_TO_PIXELS (node_width
)/2 + 20),
551 "y", (gdouble
) -node_pos
->y
-
552 INCH_TO_PIXELS (node_height
)/2+(j
+0.5)*abs(y1
-y2
),
554 &plugin
->canvas
->style
->text
[GTK_STATE_ACTIVE
],
555 "anchor", GTK_ANCHOR_W
,
557 plugin
->drawable_list
= g_list_prepend (plugin
->drawable_list
, item
);
559 if (j
== 0) { /* the Class' name case: make it bold */
560 gnome_canvas_item_set (item
,
561 "weight", PANGO_WEIGHT_BOLD
,
562 "style", PANGO_STYLE_NORMAL
,
566 else /* we need to draw the last 2 elements differently */
567 if (expansion_status
== NODE_HALF_EXPANDED
&&
568 j
> (NODE_NTH_FIELD (node
,i
)->n_flds
-3)) {
569 gnome_canvas_item_set (item
,
570 "weight", PANGO_WEIGHT_HEAVY
,
571 "style", PANGO_STYLE_NORMAL
,
575 else /* only the last one. Usually "Normal view" */
576 if (expansion_status
== NODE_FULL_EXPANDED
&&
577 j
> (NODE_NTH_FIELD (node
,i
)->n_flds
-2)) {
578 gnome_canvas_item_set (item
,
579 "weight", PANGO_WEIGHT_HEAVY
,
580 "style", PANGO_STYLE_NORMAL
,
585 /* go on with the icons */
586 if (symbol_iter
&& ianjuta_iterable_get_length (symbol_iter
, NULL
) > 0) {
587 const GdkPixbuf
*pixbuf
;
590 IAnjutaSymbol
*symbol
= IANJUTA_SYMBOL (symbol_iter
);
592 file
= ianjuta_symbol_get_file (symbol
, NULL
);
593 line
= ianjuta_symbol_get_line (symbol
, NULL
);
594 pixbuf
= ianjuta_symbol_get_icon (symbol
, NULL
);
596 item
= gnome_canvas_item_new ( gnome_canvas_root
597 (GNOME_CANVAS (plugin
->canvas
)),
598 gnome_canvas_pixbuf_get_type(),
600 (gdouble
) (node_pos
->x
-
601 INCH_TO_PIXELS (node_width
)/2 +2),
603 (gdouble
) -node_pos
->y
-
604 INCH_TO_PIXELS (node_height
)/2+(j
+0.5)*abs(y1
-y2
)-5,
608 /* set now the object properties on node_data. We still have a
609 * reference to it so we can access its canvas_item */
611 g_object_set_data_full (G_OBJECT (node_data
->canvas_item
), "__file",
612 file
, g_object_unref
);
613 g_object_set_data (G_OBJECT (node_data
->canvas_item
), "__line",
614 GINT_TO_POINTER (line
));
617 plugin
->drawable_list
= g_list_prepend (plugin
->drawable_list
, item
);
618 ianjuta_iterable_next (symbol_iter
, NULL
);
623 g_object_unref (symbol_iter
);
626 g_object_unref (iter_searched
);
628 /* make the outline bounds */
630 gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (plugin
->canvas
)),
631 gnome_canvas_rect_get_type (),
633 (gdouble
) (node_pos
->x
-INCH_TO_PIXELS (node_width
)/2-1 ),
635 (gdouble
) -node_pos
->y
-INCH_TO_PIXELS (node_height
)/2 -1,
637 (gdouble
) node_pos
->x
+INCH_TO_PIXELS (node_width
)/2+1,
639 (gdouble
) -node_pos
->y
+INCH_TO_PIXELS (node_height
)/2 -1,
641 &plugin
->canvas
->style
->text
[GTK_STATE_ACTIVE
],
645 plugin
->drawable_list
= g_list_prepend (plugin
->drawable_list
, item
);
649 cls_inherit_draw_single_node (AnjutaClassInheritance
*plugin
, Agnode_t
*node
,
650 point
*node_pos
, gdouble node_width
, gdouble node_height
) {
652 GnomeCanvasItem
*item
;
653 gdouble text_width_value
;
655 node_data
= g_new0 (NodeData
, 1);
657 /* set the plugin reference */
658 node_data
->plugin
= plugin
;
659 node_data
->anchored
= FALSE
;
660 node_data
->name
= g_strdup (node
->name
);
661 node_data
->sub_item
= NULL
;
663 node_data
->canvas_item
=
664 gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (plugin
->canvas
)),
665 gnome_canvas_rect_get_type (),
667 (gdouble
) (node_pos
->x
- INCH_TO_PIXELS (node_width
)/2),
669 (gdouble
) -(node_pos
->y
- INCH_TO_PIXELS (node_height
)/2),
671 (gdouble
) (node_pos
->x
+ INCH_TO_PIXELS (node_width
)/2),
673 (gdouble
) -(node_pos
->y
+ INCH_TO_PIXELS (node_height
)/2),
675 &plugin
->canvas
->style
->base
[GTK_STATE_NORMAL
],
677 &plugin
->canvas
->style
->text
[GTK_STATE_NORMAL
],
681 plugin
->node_list
= g_list_prepend (plugin
->node_list
, node_data
);
683 g_signal_connect (GTK_OBJECT (node_data
->canvas_item
), "event",
684 G_CALLBACK (on_nodedata_event
),
688 item
= gnome_canvas_item_new (gnome_canvas_root
689 (GNOME_CANVAS (plugin
->canvas
)),
690 gnome_canvas_text_get_type (),
692 "font", NODE_FONT_DEFAULT
,
693 "justification", GTK_JUSTIFY_CENTER
,
694 "anchor", GTK_ANCHOR_CENTER
,
696 (gdouble
) (node_pos
->x
- INCH_TO_PIXELS (node_width
)/2),
697 "y", (gdouble
) -node_pos
->y
,
699 &plugin
->canvas
->style
->text
[GTK_STATE_NORMAL
],
700 "anchor", GTK_ANCHOR_W
,
703 /* center the text in the node... */
704 g_object_get (item
, "text_width", &text_width_value
, NULL
);
706 gnome_canvas_item_set (item
, "x",
707 (gdouble
)((node_pos
->x
- text_width_value
/2)), NULL
);
709 plugin
->drawable_list
= g_list_prepend (plugin
->drawable_list
, item
);
713 /*----------------------------------------------------------------------------
714 * draw the graph on the canvas. So nodes, edges, arrows, texts..
715 * If something is found already drawn on the canvas it is cleaned before
719 cls_inherit_draw_graph (AnjutaClassInheritance
*plugin
)
722 gdouble max_canvas_size_x
, max_canvas_size_y
;
723 GnomeCanvasItem
*item
;
727 if (plugin
->graph
== NULL
)
730 DEBUG_PRINT ("======== going to draw graph ========");
731 num_nodes
= agnnodes (plugin
->graph
);
732 g_return_if_fail (num_nodes
> 0);
734 /* compiles nodes/edges informations, such as positions, coordinates etc */
735 gvLayout (plugin
->gvc
, plugin
->graph
, "dot");
736 //dot_layout (plugin->graph);
738 /* set the size of the canvas. We need this to set the scrolling.. */
739 max_canvas_size_x
= max_canvas_size_y
= CANVAS_MIN_SIZE
;
741 /* check whether we had already drawn something on the canvas.
742 * In case remove the items so we can clean up the canvas ready
745 if (g_list_length (plugin
->drawable_list
) > 0 ||
746 g_list_length (plugin
->node_list
) > 0)
748 class_inheritance_clean_canvas (plugin
);
751 /* first of all draw the nodes */
752 for (node
= agfstnode (plugin
->graph
); node
;
753 node
= agnxtnode (plugin
->graph
, node
))
759 /* get some infos from the node */
760 node_pos
= ND_coord_i(node
);
761 node_width
= ND_width (node
);
762 node_height
= ND_height (node
);
764 if (strcmp ("record", ND_shape (node
)->name
) == 0 ) {
765 cls_inherit_draw_expanded_node (plugin
, node
, &node_pos
, node_width
, node_height
);
767 else /* it's a normal single node */
768 cls_inherit_draw_single_node (plugin
, node
, &node_pos
, node_width
, node_height
);
771 for (edge
= agfstedge (plugin
->graph
, node
); edge
;
772 edge
= agnxtedge (plugin
->graph
, edge
, node
))
774 GnomeCanvasPathDef
*path_def
;
777 path_def
= gnome_canvas_path_def_new();
779 for ( i
= 0; i
< ED_spl(edge
)->list
->size
-1; i
+=3)
782 /* go on with bezier curves. We can retrieve the info such
783 * as control points from the struct of the edge
785 gnome_canvas_path_def_moveto (path_def
,
786 ((ED_spl(edge
))->list
->list
[0+i
]).x
,
787 -((ED_spl(edge
))->list
->list
[0+i
]).y
);
789 gnome_canvas_path_def_curveto (path_def
,
790 ((ED_spl(edge
))->list
->list
[1+i
]).x
,
791 -((ED_spl(edge
))->list
->list
[1+i
]).y
,
792 ((ED_spl(edge
))->list
->list
[2+i
]).x
,
793 -((ED_spl(edge
))->list
->list
[2+i
]).y
,
794 ((ED_spl(edge
))->list
->list
[3+i
]).x
,
795 -((ED_spl(edge
))->list
->list
[3+i
]).y
);
797 /* check whether we have to draw an arrow. Is the right point? */
798 if ( i
+3 >= (ED_spl(edge
)->list
->size
-1) )
800 GnomeCanvasPoints
* points
;
801 gdouble upper_bound
= (gdouble
)(node_pos
.y
+
802 INCH_TO_PIXELS (node_height
)/2);
814 h
= abs (((ED_spl(edge
))->list
->list
[3+i
]).y
- upper_bound
);
816 if ((((ED_spl(edge
))->list
->list
[3+i
]).x
-
817 ((ED_spl(edge
))->list
->list
[2+i
]).x
) > 0)
818 x_offset
= sqrt( abs(10*10 - h
*h
));
820 x_offset
= -sqrt( abs(10*10 - h
*h
));
822 /* let's draw a canvas_line with an arrow-end */
823 points
= gnome_canvas_points_new (2);
826 points
->coords
[0] = ((ED_spl(edge
))->list
->list
[3+i
]).x
;
827 points
->coords
[1] = -((ED_spl(edge
))->list
->list
[3+i
]).y
;
831 ((ED_spl(edge
))->list
->list
[3+i
]).x
+ x_offset
;
832 points
->coords
[3] = -upper_bound
;
834 /* ok we take an arrow_max_length of 10 pixels for default. */
835 if ( abs(x_offset
) <= 10 )
838 gnome_canvas_item_new (gnome_canvas_root
839 (GNOME_CANVAS (plugin
->canvas
)),
840 gnome_canvas_line_get_type(),
843 &plugin
->canvas
->style
->text
[GTK_STATE_NORMAL
],
844 "last_arrowhead", TRUE
,
845 "arrow_shape_a", 10.0,
846 "arrow_shape_b", 10.0,
847 "arrow_shape_c", 4.0,
850 plugin
->drawable_list
=
851 g_list_prepend (plugin
->drawable_list
, item
);
856 /* draw the path_def */
857 item
= gnome_canvas_item_new (gnome_canvas_root
858 (GNOME_CANVAS (plugin
->canvas
)),
859 gnome_canvas_bpath_get_type(),
862 &plugin
->canvas
->style
->text
[GTK_STATE_NORMAL
],
865 plugin
->drawable_list
=
866 g_list_prepend (plugin
->drawable_list
, item
);
869 if (abs(node_pos
.x
) > max_canvas_size_x
)
870 max_canvas_size_x
= abs(node_pos
.x
) + INCH_TO_PIXELS (node_width
)/2;
872 if (abs(node_pos
.y
+ node_height
) > max_canvas_size_y
)
873 max_canvas_size_y
= abs(node_pos
.y
) + INCH_TO_PIXELS (node_height
)/2;
876 gtk_widget_set_size_request (plugin
->canvas
, max_canvas_size_x
+100,
877 max_canvas_size_y
+100);
878 gnome_canvas_set_scroll_region ( GNOME_CANVAS (plugin
->canvas
), -50,
879 50, max_canvas_size_x
+ 50,
880 -max_canvas_size_y
-100);
882 cls_inherit_graph_cleanup (plugin
);
885 /*----------------------------------------------------------------------------
886 * update the internal graphviz graph-structure, then redraw the graph on the
890 class_inheritance_update_graph (AnjutaClassInheritance
*plugin
)
892 IAnjutaSymbolManager
*sm
;
893 IAnjutaIterable
*iter
;
894 IAnjutaSymbol
*symbol
;
895 GList
*classes
, *node
;
896 GHashTable
*class_parents
;
898 g_return_if_fail (plugin
!= NULL
);
900 if (plugin
->top_dir
== NULL
)
903 sm
= anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin
)->shell
,
904 IAnjutaSymbolManager
, NULL
);
908 iter
= ianjuta_symbol_manager_search (sm
, IANJUTA_SYMBOL_TYPE_CLASS
,
910 IANJUTA_SYMBOL_FIELD_SIMPLE
,
911 NULL
, FALSE
, TRUE
, FALSE
, -1, -1, NULL
);
914 DEBUG_PRINT ("class_inheritance_update_graph (): search returned no items.");
918 DEBUG_PRINT ("Number of classes found = %d",
919 ianjuta_iterable_get_length (iter
, NULL
));
922 const gchar *class_name;
923 symbol = IANJUTA_SYMBOL (iter);
924 class_name = ianjuta_symbol_get_name (symbol, NULL);
925 DEBUG_PRINT ("=======> %s", class_name);
927 while (ianjuta_iterable_next (iter, NULL) == TRUE);
929 ianjuta_iterable_first (iter
, NULL
);
930 if (ianjuta_iterable_get_length (iter
, NULL
) <= 0)
932 g_object_unref (iter
);
936 /* Get all classes */
938 class_parents
= g_hash_table_new_full (g_str_hash
, g_str_equal
,
939 g_free
, g_object_unref
);
941 const gchar
*class_name
, *old_parents
;
942 IAnjutaIterable
*parents
;
944 symbol
= IANJUTA_SYMBOL (iter
);
946 /* get parents of the current class */
947 parents
= ianjuta_symbol_manager_get_class_parents (sm
, symbol
,
948 IANJUTA_SYMBOL_FIELD_SIMPLE
,
951 if (parents
== NULL
|| ianjuta_iterable_get_length (parents
, NULL
) <= 0)
953 DEBUG_PRINT ("continuing 1...");
957 class_name
= ianjuta_symbol_get_name (symbol
, NULL
);
959 if ((old_parents
= g_hash_table_lookup (class_parents
, class_name
)))
961 /* we already have a class inserted with that name */
962 DEBUG_PRINT ("continuing 2...");
966 DEBUG_PRINT ("parsed %s", class_name
);
967 /* insert into the hash table a class name together with the associated parents */
968 g_hash_table_insert (class_parents
, g_strdup (class_name
), parents
);
969 classes
= g_list_prepend (classes
, g_strdup (class_name
));
970 } while (ianjuta_iterable_next (iter
, NULL
) == TRUE
);
972 classes
= g_list_reverse (classes
);
974 /* we don't need the iter anymore */
975 g_object_unref (iter
);
977 /* For all classes get their parents */
981 const gchar
*class_name
;
982 IAnjutaIterable
*parents
;
984 class_name
= node
->data
;
985 parents
= g_hash_table_lookup (class_parents
, class_name
);
988 IAnjutaSymbol
*symbol
;
989 symbol
= IANJUTA_SYMBOL (parents
);
990 const gchar
*parent_name
;
992 parent_name
= ianjuta_symbol_get_name (symbol
, NULL
);
994 cls_inherit_add_node (plugin
, class_name
);
995 cls_inherit_add_node (plugin
, parent_name
);
996 cls_inherit_add_edge (plugin
, parent_name
, class_name
);
997 } while (ianjuta_iterable_next (parents
, NULL
) == TRUE
);
999 /* parse next deriver class in the glist */
1000 node
= g_list_next (node
);
1002 g_list_foreach (classes
, (GFunc
)g_free
, NULL
);
1003 g_list_free (classes
);
1004 g_hash_table_destroy (class_parents
);
1005 cls_inherit_draw_graph (plugin
);
1008 static GnomeUIInfo canvas_menu_uiinfo
[] = {
1012 N_("Update the graph"),
1013 on_update_menu_item_selected
,
1016 GNOME_APP_PIXMAP_NONE
,
1025 class_inheritance_hash_table_clear (AnjutaClassInheritance
*plugin
) {
1027 if (plugin
->expansion_node_list
== NULL
)
1030 /* destroy the nodestatus hash table */
1031 g_hash_table_destroy (plugin
->expansion_node_list
);
1033 /* reinitialize the table */
1034 plugin
->expansion_node_list
= g_hash_table_new_full (g_str_hash
,
1035 g_str_equal
, g_free
,
1036 (GDestroyNotify
)cls_inherit_nodestatus_destroy
);
1040 class_inheritance_base_gui_init (AnjutaClassInheritance
*plugin
)
1042 GtkWidget
*s_window
;
1044 s_window
= gtk_scrolled_window_new (NULL
, NULL
);
1045 plugin
->canvas
= gnome_canvas_new_aa ();
1046 //gtk_widget_modify_bg (plugin->canvas, GTK_STATE_NORMAL,
1047 // &plugin->canvas->style->base[GTK_STATE_NORMAL]);
1048 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (s_window
),
1049 GTK_POLICY_AUTOMATIC
,
1050 GTK_POLICY_AUTOMATIC
);
1051 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (s_window
),
1054 gtk_widget_set_size_request (plugin
->canvas
, CANVAS_MIN_SIZE
,
1056 gnome_canvas_set_scroll_region (GNOME_CANVAS (plugin
->canvas
),
1060 -CANVAS_MIN_SIZE
/2);
1062 g_signal_connect (G_OBJECT (plugin
->canvas
), "event",
1063 G_CALLBACK (on_canvas_event
),
1066 g_signal_connect (G_OBJECT (plugin
->canvas
),
1068 G_CALLBACK (on_style_set
),
1071 plugin
->widget
= gtk_vbox_new (FALSE
, 2);
1074 gtk_box_pack_start (GTK_BOX (plugin
->widget
), s_window
, TRUE
, TRUE
, TRUE
);
1076 gtk_widget_show_all (plugin
->widget
);
1078 /* create new GList */
1079 plugin
->drawable_list
= NULL
;
1080 plugin
->node_list
= NULL
;
1082 plugin
->expansion_node_list
= g_hash_table_new_full (g_str_hash
,
1083 g_str_equal
, g_free
,
1084 (GDestroyNotify
)cls_inherit_nodestatus_destroy
);
1087 plugin
->menu
= gtk_menu_new ();
1089 /* set the user data on update selection */
1090 canvas_menu_uiinfo
[0].user_data
= plugin
;
1092 gnome_app_fill_menu (GTK_MENU_SHELL (plugin
->menu
), canvas_menu_uiinfo
,
1095 plugin
->update
= canvas_menu_uiinfo
[0].widget
;
1097 gtk_widget_ref (plugin
->menu
);
1098 gtk_widget_ref (plugin
->update
);