Updated Spanish translation
[anjuta-git-plugin.git] / plugins / class-inheritance / class-inherit.c
blob0e6f460ddf051558d3a5664d6e9a0f6d048f56e7
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
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.
20 #include <glib.h>
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>
26 #include "plugin.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.
60 static void
61 cls_inherit_nodestatus_destroy (NodeExpansionStatus *node) {
62 if (node->name)
63 g_free (node->name);
65 g_free (node);
68 void
69 class_inheritance_show_dynamic_class_popup_menu (GdkEvent *event,
70 NodeData* nodedata)
72 GtkWidget *item, *image;
73 GtkWidget *checkitem, *separator;
75 /* Destroy the old menu before creating a new one */
76 if (nodedata->menu)
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);
90 if (sm == NULL)
91 return;
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,
100 TRUE,
101 IANJUTA_SYMBOL_FIELD_SIMPLE,
102 nodedata->name,
103 FALSE,
104 TRUE,
105 FALSE,
108 NULL);
110 if (iter_searched == NULL)
111 return;
113 symbol_searched = IANJUTA_SYMBOL (iter_searched);
114 iter = ianjuta_symbol_manager_get_members (sm, symbol_searched,
115 IANJUTA_SYMBOL_FIELD_SIMPLE,
116 FALSE, NULL);
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;
124 gint line;
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
135 (item), image);
137 if (file)
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),
145 item);
146 g_signal_connect (G_OBJECT (item), "activate",
147 G_CALLBACK (on_member_menuitem_clicked),
148 nodedata);
149 } while (ianjuta_iterable_next (iter, NULL));
151 if (iter)
153 g_object_unref (iter);
155 if (iter_searched)
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),
167 nodedata->anchored);
169 g_signal_connect (G_OBJECT (checkitem), "toggled",
170 G_CALLBACK (on_toggled_menuitem_clicked),
171 nodedata);
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,
179 event->button.time);
182 /*----------------------------------------------------------------------------
183 * initialize the internal graphviz structure.
185 static void
186 cls_inherit_graph_init (AnjutaClassInheritance *plugin, gchar* graph_label)
188 aginit ();
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.
197 static void
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;
212 plugin->gvc = NULL;
216 /*----------------------------------------------------------------------------
217 * destroys a NodeData element. All it's resources will be deallocated
218 * and setted to null.
220 static void
221 cls_inherit_nodedata_destroy (NodeData *node_data)
223 if (node_data->name)
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;
235 if (node_data->menu)
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.
251 void
252 class_inheritance_clean_canvas (AnjutaClassInheritance *plugin)
254 if (plugin->drawable_list == NULL || plugin->node_list == NULL)
255 return;
257 /* destroying a gnome_canvas_item will un-paint automatically from
258 * the canvas
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,
265 NULL);
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.
278 static gboolean
279 cls_inherit_add_node (AnjutaClassInheritance *plugin, const gchar* node_name)
281 Agnode_t *node;
282 Agsym_t *sym;
283 NodeExpansionStatus *node_status;
285 /* if graph isn't initialized, init it */
286 if (!plugin->graph)
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)
291 return FALSE;
293 /* check for the node in the hash_table */
294 if ( (node_status =
295 (NodeExpansionStatus*)g_hash_table_lookup
296 (plugin->expansion_node_list, node_name)) != NULL &&
297 node_status->expansion_status != NODE_NOT_EXPANDED) {
298 GString *label;
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);
315 if (sm)
317 IAnjutaIterable *iter;
318 IAnjutaIterable *iter_searched;
319 IAnjutaSymbol *symbol_searched;
320 iter_searched = ianjuta_symbol_manager_search (sm, IANJUTA_SYMBOL_TYPE_CLASS,
321 TRUE,
322 IANJUTA_SYMBOL_FIELD_SIMPLE,
323 node_name,
324 FALSE,
325 TRUE,
326 FALSE,
329 NULL);
331 if (iter_searched == NULL) {
332 g_string_free (label, TRUE);
333 return FALSE;
336 symbol_searched = IANJUTA_SYMBOL (iter_searched);
338 iter = ianjuta_symbol_manager_get_members (sm, symbol_searched,
339 IANJUTA_SYMBOL_FIELD_SIMPLE,
340 FALSE, NULL);
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;
349 else {
350 max_label_items = NODE_HALF_DISPLAY_ELEM_NUM;
353 if (iter && real_items_length > 0)
355 gint i;
356 i = 0;
359 const gchar *name;
360 IAnjutaSymbol *symbol = IANJUTA_SYMBOL (iter);
362 name = ianjuta_symbol_get_name (symbol, NULL);
363 g_string_append_printf (label, "|%s", name);
364 i++;
365 } while (ianjuta_iterable_next (iter, NULL) && i < max_label_items);
367 if (iter)
368 g_object_unref (iter);
370 if (iter_searched)
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);
397 /* set the font */
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");
413 return TRUE;
416 /*----------------------------------------------------------------------------
417 * add an edge to an Agraph.
419 static gboolean
420 cls_inherit_add_edge (AnjutaClassInheritance *plugin,
421 const gchar* node_from,
422 const gchar* node_to)
424 Agedge_t *edge;
425 Agnode_t *n_from, *n_to;
427 /* if we hadn't initialized out graph we return FALSE. Edges require
428 * two nodes
430 if (!plugin->graph)
431 return FALSE;
433 if ((n_from = agfindnode (plugin->graph, (gchar*)node_from)) == NULL)
434 return FALSE;
436 if ((n_to = agfindnode (plugin->graph, (gchar*)node_to)) == NULL)
437 return FALSE;
439 if ((edge = agedge (plugin->graph, n_from, n_to)) == NULL)
440 return FALSE;
442 return TRUE;
446 /*----------------------------------------------------------------------------
447 * Draw an expanded node. Function simplify cls_inherit_draw_graph().
449 static void
450 cls_inherit_draw_expanded_node (AnjutaClassInheritance *plugin, Agnode_t *node,
451 point* node_pos, gdouble node_width, gdouble node_height) {
452 GnomeCanvasItem *item;
453 NodeData *node_data;
454 NodeExpansionStatus *node_status;
455 gint expansion_status;
456 gint i, j;
457 IAnjutaSymbolManager *sm;
458 IAnjutaIterable *symbol_iter = NULL;
460 sm = anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin)->shell,
461 IAnjutaSymbolManager, NULL);
462 if (!sm) {
463 return;
466 IAnjutaIterable *iter_searched;
467 IAnjutaSymbol *symbol_searched;
468 iter_searched = ianjuta_symbol_manager_search (sm, IANJUTA_SYMBOL_TYPE_CLASS,
469 TRUE,
470 IANJUTA_SYMBOL_FIELD_SIMPLE,
471 node->name,
472 FALSE,
473 TRUE,
474 FALSE,
477 NULL);
479 if (iter_searched == NULL) {
480 return;
483 symbol_searched = IANJUTA_SYMBOL (iter_searched);
485 symbol_iter = ianjuta_symbol_manager_get_members (sm, symbol_searched,
486 IANJUTA_SYMBOL_FIELD_SIMPLE,
487 FALSE, NULL);
489 /* we need to know which label to draw, wether only the "show all" or just
490 * the "normal view" */
491 if ( (node_status =
492 (NodeExpansionStatus*)g_hash_table_lookup
493 (plugin->expansion_node_list, node->name)) == NULL) {
494 expansion_status = NODE_NOT_EXPANDED;
496 else
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++ ) {
501 gint y1, y2;
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 (),
517 "x1",
518 (gdouble) (node_pos->x -INCH_TO_PIXELS (node_width)/2),
519 "y1",
520 (gdouble) -node_pos->y -INCH_TO_PIXELS (node_height)/2 +
521 j*abs(y1-y2),
522 "x2",
523 (gdouble) (node_pos->x +INCH_TO_PIXELS (node_width)/2),
524 "y2",
525 (gdouble) -node_pos->y -INCH_TO_PIXELS (node_height)/2 +
526 (j+1)*abs(y1-y2),
527 "fill_color_gdk",
528 &plugin->canvas->style->base[GTK_STATE_ACTIVE],
529 NULL);
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),
537 node_data);
539 /* --- texts --- */
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,
548 "x",
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),
553 "fill_color_gdk",
554 &plugin->canvas->style->text[GTK_STATE_ACTIVE],
555 "anchor", GTK_ANCHOR_W,
556 NULL);
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,
563 NULL);
564 continue;
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,
572 NULL);
573 continue;
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,
581 NULL);
582 continue;
585 /* go on with the icons */
586 if (symbol_iter && ianjuta_iterable_get_length (symbol_iter, NULL) > 0) {
587 const GdkPixbuf *pixbuf;
588 GFile* file;
589 gint line;
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(),
599 "x",
600 (gdouble) (node_pos->x -
601 INCH_TO_PIXELS (node_width)/2 +2),
602 "y",
603 (gdouble) -node_pos->y -
604 INCH_TO_PIXELS (node_height)/2+(j+0.5)*abs(y1-y2)-5,
605 "pixbuf", pixbuf,
606 NULL);
608 /* set now the object properties on node_data. We still have a
609 * reference to it so we can access its canvas_item */
610 if (file) {
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);
622 if (symbol_iter)
623 g_object_unref (symbol_iter);
625 if (iter_searched)
626 g_object_unref (iter_searched);
628 /* make the outline bounds */
629 item =
630 gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (plugin->canvas)),
631 gnome_canvas_rect_get_type (),
632 "x1",
633 (gdouble) (node_pos->x -INCH_TO_PIXELS (node_width)/2-1 ),
634 "y1",
635 (gdouble) -node_pos->y -INCH_TO_PIXELS (node_height)/2 -1,
636 "x2",
637 (gdouble) node_pos->x +INCH_TO_PIXELS (node_width)/2+1,
638 "y2",
639 (gdouble) -node_pos->y +INCH_TO_PIXELS (node_height)/2 -1,
640 "outline_color_gdk",
641 &plugin->canvas->style->text[GTK_STATE_ACTIVE],
642 "width_units", 1.0,
643 NULL);
645 plugin->drawable_list = g_list_prepend (plugin->drawable_list, item);
648 static void
649 cls_inherit_draw_single_node (AnjutaClassInheritance *plugin, Agnode_t *node,
650 point *node_pos, gdouble node_width, gdouble node_height) {
651 NodeData *node_data;
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 (),
666 "x1",
667 (gdouble) (node_pos->x - INCH_TO_PIXELS (node_width)/2),
668 "y1",
669 (gdouble) -(node_pos->y - INCH_TO_PIXELS (node_height)/2),
670 "x2",
671 (gdouble) (node_pos->x + INCH_TO_PIXELS (node_width)/2),
672 "y2",
673 (gdouble) -(node_pos->y + INCH_TO_PIXELS (node_height)/2),
674 "fill_color_gdk",
675 &plugin->canvas->style->base[GTK_STATE_NORMAL],
676 "outline_color_gdk",
677 &plugin->canvas->style->text[GTK_STATE_NORMAL],
678 "width_units", 1.0,
679 NULL);
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),
685 node_data);
687 /* --- texts --- */
688 item = gnome_canvas_item_new (gnome_canvas_root
689 (GNOME_CANVAS (plugin->canvas)),
690 gnome_canvas_text_get_type (),
691 "text", node->name,
692 "font", NODE_FONT_DEFAULT,
693 "justification", GTK_JUSTIFY_CENTER,
694 "anchor", GTK_ANCHOR_CENTER,
695 "x",
696 (gdouble) (node_pos->x - INCH_TO_PIXELS (node_width)/2),
697 "y", (gdouble) -node_pos->y,
698 "fill_color_gdk",
699 &plugin->canvas->style->text[GTK_STATE_NORMAL],
700 "anchor", GTK_ANCHOR_W,
701 NULL );
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
716 * draw new things.
718 static void
719 cls_inherit_draw_graph (AnjutaClassInheritance *plugin)
721 gint num_nodes;
722 gdouble max_canvas_size_x, max_canvas_size_y;
723 GnomeCanvasItem *item;
724 Agnode_t *node;
725 Agedge_t *edge;
727 if (plugin->graph == NULL)
728 return;
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
743 * for others paints
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))
755 gdouble node_width;
756 gdouble node_height;
757 point node_pos;
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);
770 /* --- edges --- */
771 for (edge = agfstedge (plugin->graph, node); edge;
772 edge = agnxtedge (plugin->graph, edge, node))
774 GnomeCanvasPathDef *path_def;
775 gint i;
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);
803 gdouble x_offset;
804 gint h;
807 __|__ _
808 \ / |
809 \ / | h
810 ° _|
811 ^^^^^^^^^^^^^
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));
819 else
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);
825 /* starting point */
826 points->coords[0] = ((ED_spl(edge))->list->list[3+i]).x;
827 points->coords[1] = -((ED_spl(edge))->list->list[3+i]).y;
829 /* pointer */
830 points->coords[2] =
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 )
837 item =
838 gnome_canvas_item_new (gnome_canvas_root
839 (GNOME_CANVAS (plugin->canvas)),
840 gnome_canvas_line_get_type(),
841 "points", points,
842 "fill_color_gdk",
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,
848 "width_units", 1.0,
849 NULL);
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(),
860 "bpath", path_def,
861 "outline_color_gdk",
862 &plugin->canvas->style->text[GTK_STATE_NORMAL],
863 "width_pixels", 1,
864 NULL);
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
887 * canvas
889 void
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)
901 return;
903 sm = anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin)->shell,
904 IAnjutaSymbolManager, NULL);
905 if (!sm)
906 return;
908 iter = ianjuta_symbol_manager_search (sm, IANJUTA_SYMBOL_TYPE_CLASS,
909 TRUE,
910 IANJUTA_SYMBOL_FIELD_SIMPLE,
911 NULL, FALSE, TRUE, FALSE, -1, -1, NULL);
912 if (!iter)
914 DEBUG_PRINT ("class_inheritance_update_graph (): search returned no items.");
915 return;
918 DEBUG_PRINT ("Number of classes found = %d",
919 ianjuta_iterable_get_length (iter, NULL));
921 do {
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);
933 return;
936 /* Get all classes */
937 classes = NULL;
938 class_parents = g_hash_table_new_full (g_str_hash, g_str_equal,
939 g_free, g_object_unref);
940 do {
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,
949 NULL);
951 if (parents == NULL || ianjuta_iterable_get_length (parents, NULL) <= 0)
953 DEBUG_PRINT ("continuing 1...");
954 continue;
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...");
963 continue;
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 */
978 node = classes;
979 while (node)
981 const gchar *class_name;
982 IAnjutaIterable *parents;
984 class_name = node->data;
985 parents = g_hash_table_lookup (class_parents, class_name);
987 do {
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[] = {
1009 { /*0*/
1010 GNOME_APP_UI_ITEM,
1011 N_("Update"),
1012 N_("Update the graph"),
1013 on_update_menu_item_selected,
1014 NULL,
1015 NULL,
1016 GNOME_APP_PIXMAP_NONE,
1017 NULL,
1020 NULL},
1021 GNOMEUIINFO_END
1024 void
1025 class_inheritance_hash_table_clear (AnjutaClassInheritance *plugin) {
1027 if (plugin->expansion_node_list == NULL)
1028 return;
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);
1039 void
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),
1052 plugin->canvas);
1054 gtk_widget_set_size_request (plugin->canvas, CANVAS_MIN_SIZE,
1055 CANVAS_MIN_SIZE);
1056 gnome_canvas_set_scroll_region (GNOME_CANVAS (plugin->canvas),
1057 -CANVAS_MIN_SIZE/2,
1058 CANVAS_MIN_SIZE/2,
1059 CANVAS_MIN_SIZE/2,
1060 -CANVAS_MIN_SIZE/2);
1062 g_signal_connect (G_OBJECT (plugin->canvas), "event",
1063 G_CALLBACK (on_canvas_event),
1064 plugin);
1066 g_signal_connect (G_OBJECT (plugin->canvas),
1067 "style_set",
1068 G_CALLBACK (on_style_set),
1069 plugin);
1071 plugin->widget = gtk_vbox_new (FALSE, 2);
1072 /* --packing-- */
1073 /* vbox */
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);
1086 /* menu create */
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,
1093 NULL, FALSE, 0);
1095 plugin->update = canvas_menu_uiinfo[0].widget;
1097 gtk_widget_ref (plugin->menu);
1098 gtk_widget_ref (plugin->update);