Updated Spanish translation
[anjuta-git-plugin.git] / plugins / class-inheritance / class-inherit.c
blob02d50ff2335251b005a31ee4173ea6517a4c778e
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 NULL);
107 if (iter_searched == NULL)
108 return;
110 symbol_searched = IANJUTA_SYMBOL (iter_searched);
111 iter = ianjuta_symbol_manager_get_members (sm, symbol_searched,
112 IANJUTA_SYMBOL_FIELD_SIMPLE,
113 FALSE, NULL);
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;
121 gint line;
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
132 (item), image);
134 if (file)
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),
142 item);
143 g_signal_connect (G_OBJECT (item), "activate",
144 G_CALLBACK (on_member_menuitem_clicked),
145 nodedata);
146 } while (ianjuta_iterable_next (iter, NULL));
148 if (iter)
150 g_object_unref (iter);
152 if (iter_searched)
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),
164 nodedata->anchored);
166 g_signal_connect (G_OBJECT (checkitem), "toggled",
167 G_CALLBACK (on_toggled_menuitem_clicked),
168 nodedata);
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,
176 event->button.time);
179 /*----------------------------------------------------------------------------
180 * initialize the internal graphviz structure.
182 static void
183 cls_inherit_graph_init (AnjutaClassInheritance *plugin, gchar* graph_label)
185 aginit ();
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.
194 static void
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;
209 plugin->gvc = NULL;
213 /*----------------------------------------------------------------------------
214 * destroys a NodeData element. All it's resources will be deallocated
215 * and setted to null.
217 static void
218 cls_inherit_nodedata_destroy (NodeData *node_data)
220 if (node_data->name)
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;
232 if (node_data->menu)
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.
248 void
249 class_inheritance_clean_canvas (AnjutaClassInheritance *plugin)
251 if (plugin->drawable_list == NULL || plugin->node_list == NULL)
252 return;
254 /* destroying a gnome_canvas_item will un-paint automatically from
255 * the canvas
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,
262 NULL);
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.
275 static gboolean
276 cls_inherit_add_node (AnjutaClassInheritance *plugin, const gchar* node_name)
278 Agnode_t *node;
279 Agsym_t *sym;
280 NodeExpansionStatus *node_status;
282 /* if graph isn't initialized, init it */
283 if (!plugin->graph)
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)
288 return FALSE;
290 /* check for the node in the hash_table */
291 if ( (node_status =
292 (NodeExpansionStatus*)g_hash_table_lookup
293 (plugin->expansion_node_list, node_name)) != NULL &&
294 node_status->expansion_status != NODE_NOT_EXPANDED) {
295 GString *label;
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);
312 if (sm)
314 IAnjutaIterable *iter;
315 IAnjutaIterable *iter_searched;
316 IAnjutaSymbol *symbol_searched;
317 iter_searched = ianjuta_symbol_manager_search (sm, IANJUTA_SYMBOL_TYPE_CLASS,
318 TRUE,
319 IANJUTA_SYMBOL_FIELD_SIMPLE,
320 node_name,
321 FALSE,
322 TRUE,
323 NULL);
325 if (iter_searched == NULL) {
326 g_string_free (label, TRUE);
327 return FALSE;
330 symbol_searched = IANJUTA_SYMBOL (iter_searched);
332 iter = ianjuta_symbol_manager_get_members (sm, symbol_searched,
333 IANJUTA_SYMBOL_FIELD_SIMPLE,
334 FALSE, NULL);
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;
343 else {
344 max_label_items = NODE_HALF_DISPLAY_ELEM_NUM;
347 if (iter && real_items_length > 0)
349 gint i;
350 i = 0;
353 const gchar *name;
354 IAnjutaSymbol *symbol = IANJUTA_SYMBOL (iter);
356 name = ianjuta_symbol_get_name (symbol, NULL);
357 g_string_append_printf (label, "|%s", name);
358 i++;
359 } while (ianjuta_iterable_next (iter, NULL) && i < max_label_items);
361 if (iter)
362 g_object_unref (iter);
364 if (iter_searched)
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);
391 /* set the font */
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");
407 return TRUE;
410 /*----------------------------------------------------------------------------
411 * add an edge to an Agraph.
413 static gboolean
414 cls_inherit_add_edge (AnjutaClassInheritance *plugin,
415 const gchar* node_from,
416 const gchar* node_to)
418 Agedge_t *edge;
419 Agnode_t *n_from, *n_to;
421 /* if we hadn't initialized out graph we return FALSE. Edges require
422 * two nodes
424 if (!plugin->graph)
425 return FALSE;
427 if ((n_from = agfindnode (plugin->graph, (gchar*)node_from)) == NULL)
428 return FALSE;
430 if ((n_to = agfindnode (plugin->graph, (gchar*)node_to)) == NULL)
431 return FALSE;
433 if ((edge = agedge (plugin->graph, n_from, n_to)) == NULL)
434 return FALSE;
436 return TRUE;
440 /*----------------------------------------------------------------------------
441 * Draw an expanded node. Function simplify cls_inherit_draw_graph().
443 static void
444 cls_inherit_draw_expanded_node (AnjutaClassInheritance *plugin, Agnode_t *node,
445 point* node_pos, gdouble node_width, gdouble node_height) {
446 GnomeCanvasItem *item;
447 NodeData *node_data;
448 NodeExpansionStatus *node_status;
449 gint expansion_status;
450 gint i, j;
451 IAnjutaSymbolManager *sm;
452 IAnjutaIterable *symbol_iter = NULL;
454 sm = anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin)->shell,
455 IAnjutaSymbolManager, NULL);
456 if (!sm) {
457 return;
460 IAnjutaIterable *iter_searched;
461 IAnjutaSymbol *symbol_searched;
462 iter_searched = ianjuta_symbol_manager_search (sm, IANJUTA_SYMBOL_TYPE_CLASS,
463 TRUE,
464 IANJUTA_SYMBOL_FIELD_SIMPLE,
465 node->name,
466 FALSE,
467 TRUE,
468 NULL);
470 if (iter_searched == NULL) {
471 return;
474 symbol_searched = IANJUTA_SYMBOL (iter_searched);
476 symbol_iter = ianjuta_symbol_manager_get_members (sm, symbol_searched,
477 IANJUTA_SYMBOL_FIELD_SIMPLE,
478 FALSE, NULL);
480 /* we need to know which label to draw, wether only the "show all" or just
481 * the "normal view" */
482 if ( (node_status =
483 (NodeExpansionStatus*)g_hash_table_lookup
484 (plugin->expansion_node_list, node->name)) == NULL) {
485 expansion_status = NODE_NOT_EXPANDED;
487 else
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++ ) {
492 gint y1, y2;
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 (),
508 "x1",
509 (gdouble) (node_pos->x -INCH_TO_PIXELS (node_width)/2),
510 "y1",
511 (gdouble) -node_pos->y -INCH_TO_PIXELS (node_height)/2 +
512 j*abs(y1-y2),
513 "x2",
514 (gdouble) (node_pos->x +INCH_TO_PIXELS (node_width)/2),
515 "y2",
516 (gdouble) -node_pos->y -INCH_TO_PIXELS (node_height)/2 +
517 (j+1)*abs(y1-y2),
518 "fill_color_gdk",
519 &plugin->canvas->style->base[GTK_STATE_ACTIVE],
520 NULL);
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),
528 node_data);
530 /* --- texts --- */
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,
539 "x",
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),
544 "fill_color_gdk",
545 &plugin->canvas->style->text[GTK_STATE_ACTIVE],
546 "anchor", GTK_ANCHOR_W,
547 NULL);
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,
554 NULL);
555 continue;
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,
563 NULL);
564 continue;
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,
572 NULL);
573 continue;
576 /* go on with the icons */
577 if (symbol_iter && ianjuta_iterable_get_length (symbol_iter, NULL) > 0) {
578 const GdkPixbuf *pixbuf;
579 const gchar* uri;
580 gint line;
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(),
590 "x",
591 (gdouble) (node_pos->x -
592 INCH_TO_PIXELS (node_width)/2 +2),
593 "y",
594 (gdouble) -node_pos->y -
595 INCH_TO_PIXELS (node_height)/2+(j+0.5)*abs(y1-y2)-5,
596 "pixbuf", pixbuf,
597 NULL);
599 /* set now the object properties on node_data. We still have a
600 * reference to it so we can access its canvas_item */
601 if (uri) {
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);
613 if (symbol_iter)
614 g_object_unref (symbol_iter);
616 if (iter_searched)
617 g_object_unref (iter_searched);
619 /* make the outline bounds */
620 item =
621 gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (plugin->canvas)),
622 gnome_canvas_rect_get_type (),
623 "x1",
624 (gdouble) (node_pos->x -INCH_TO_PIXELS (node_width)/2-1 ),
625 "y1",
626 (gdouble) -node_pos->y -INCH_TO_PIXELS (node_height)/2 -1,
627 "x2",
628 (gdouble) node_pos->x +INCH_TO_PIXELS (node_width)/2+1,
629 "y2",
630 (gdouble) -node_pos->y +INCH_TO_PIXELS (node_height)/2 -1,
631 "outline_color_gdk",
632 &plugin->canvas->style->text[GTK_STATE_ACTIVE],
633 "width_units", 1.0,
634 NULL);
636 plugin->drawable_list = g_list_prepend (plugin->drawable_list, item);
639 static void
640 cls_inherit_draw_single_node (AnjutaClassInheritance *plugin, Agnode_t *node,
641 point *node_pos, gdouble node_width, gdouble node_height) {
642 NodeData *node_data;
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 (),
657 "x1",
658 (gdouble) (node_pos->x - INCH_TO_PIXELS (node_width)/2),
659 "y1",
660 (gdouble) -(node_pos->y - INCH_TO_PIXELS (node_height)/2),
661 "x2",
662 (gdouble) (node_pos->x + INCH_TO_PIXELS (node_width)/2),
663 "y2",
664 (gdouble) -(node_pos->y + INCH_TO_PIXELS (node_height)/2),
665 "fill_color_gdk",
666 &plugin->canvas->style->base[GTK_STATE_NORMAL],
667 "outline_color_gdk",
668 &plugin->canvas->style->text[GTK_STATE_NORMAL],
669 "width_units", 1.0,
670 NULL);
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),
676 node_data);
678 /* --- texts --- */
679 item = gnome_canvas_item_new (gnome_canvas_root
680 (GNOME_CANVAS (plugin->canvas)),
681 gnome_canvas_text_get_type (),
682 "text", node->name,
683 "font", NODE_FONT_DEFAULT,
684 "justification", GTK_JUSTIFY_CENTER,
685 "anchor", GTK_ANCHOR_CENTER,
686 "x",
687 (gdouble) (node_pos->x - INCH_TO_PIXELS (node_width)/2),
688 "y", (gdouble) -node_pos->y,
689 "fill_color_gdk",
690 &plugin->canvas->style->text[GTK_STATE_NORMAL],
691 "anchor", GTK_ANCHOR_W,
692 NULL );
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
707 * draw new things.
709 static void
710 cls_inherit_draw_graph (AnjutaClassInheritance *plugin)
712 gint num_nodes;
713 gdouble max_canvas_size_x, max_canvas_size_y;
714 GnomeCanvasItem *item;
715 Agnode_t *node;
716 Agedge_t *edge;
718 if (plugin->graph == NULL)
719 return;
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
734 * for others paints
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))
746 gdouble node_width;
747 gdouble node_height;
748 point node_pos;
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);
761 /* --- edges --- */
762 for (edge = agfstedge (plugin->graph, node); edge;
763 edge = agnxtedge (plugin->graph, edge, node))
765 GnomeCanvasPathDef *path_def;
766 gint i;
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);
794 gdouble x_offset;
795 gint h;
798 __|__ _
799 \ / |
800 \ / | h
801 ° _|
802 ^^^^^^^^^^^^^
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));
810 else
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);
816 /* starting point */
817 points->coords[0] = ((ED_spl(edge))->list->list[3+i]).x;
818 points->coords[1] = -((ED_spl(edge))->list->list[3+i]).y;
820 /* pointer */
821 points->coords[2] =
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 )
828 item =
829 gnome_canvas_item_new (gnome_canvas_root
830 (GNOME_CANVAS (plugin->canvas)),
831 gnome_canvas_line_get_type(),
832 "points", points,
833 "fill_color_gdk",
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,
839 "width_units", 1.0,
840 NULL);
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(),
851 "bpath", path_def,
852 "outline_color_gdk",
853 &plugin->canvas->style->text[GTK_STATE_NORMAL],
854 "width_pixels", 1,
855 NULL);
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
878 * canvas
880 void
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)
892 return;
894 sm = anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin)->shell,
895 IAnjutaSymbolManager, NULL);
896 if (!sm)
897 return;
899 iter = ianjuta_symbol_manager_search (sm, IANJUTA_SYMBOL_TYPE_CLASS,
900 TRUE,
901 IANJUTA_SYMBOL_FIELD_SIMPLE,
902 NULL, FALSE, TRUE, NULL);
903 if (!iter)
905 DEBUG_PRINT ("class_inheritance_update_graph (): search returned no items.");
906 return;
909 DEBUG_PRINT ("Number of classes found = %d",
910 ianjuta_iterable_get_length (iter, NULL));
912 do {
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);
924 return;
927 /* Get all classes */
928 classes = NULL;
929 class_parents = g_hash_table_new_full (g_str_hash, g_str_equal,
930 g_free, g_object_unref);
931 do {
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,
940 NULL);
942 if (parents == NULL || ianjuta_iterable_get_length (parents, NULL) <= 0)
944 DEBUG_PRINT ("continuing 1...");
945 continue;
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...");
954 continue;
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 */
969 node = classes;
970 while (node)
972 const gchar *class_name;
973 IAnjutaIterable *parents;
975 class_name = node->data;
976 parents = g_hash_table_lookup (class_parents, class_name);
978 do {
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[] = {
1000 { /*0*/
1001 GNOME_APP_UI_ITEM,
1002 N_("Update"),
1003 N_("Update the graph"),
1004 on_update_menu_item_selected,
1005 NULL,
1006 NULL,
1007 GNOME_APP_PIXMAP_NONE,
1008 NULL,
1011 NULL},
1012 GNOMEUIINFO_END
1015 void
1016 class_inheritance_hash_table_clear (AnjutaClassInheritance *plugin) {
1018 if (plugin->expansion_node_list == NULL)
1019 return;
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);
1030 void
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),
1043 plugin->canvas);
1045 gtk_widget_set_size_request (plugin->canvas, CANVAS_MIN_SIZE,
1046 CANVAS_MIN_SIZE);
1047 gnome_canvas_set_scroll_region (GNOME_CANVAS (plugin->canvas),
1048 -CANVAS_MIN_SIZE/2,
1049 CANVAS_MIN_SIZE/2,
1050 CANVAS_MIN_SIZE/2,
1051 -CANVAS_MIN_SIZE/2);
1053 g_signal_connect (G_OBJECT (plugin->canvas), "event",
1054 G_CALLBACK (on_canvas_event),
1055 plugin);
1057 g_signal_connect (G_OBJECT (plugin->canvas),
1058 "style_set",
1059 G_CALLBACK (on_style_set),
1060 plugin);
1062 plugin->widget = gtk_vbox_new (FALSE, 2);
1063 /* --packing-- */
1064 /* vbox */
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);
1077 /* menu create */
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,
1084 NULL, FALSE, 0);
1086 plugin->update = canvas_menu_uiinfo[0].widget;
1088 gtk_widget_ref (plugin->menu);
1089 gtk_widget_ref (plugin->update);