Fix a Gtk warning when checking path input in the log viewer.
[anjuta-git-plugin.git] / plugins / profiler / gprof-function-call-chart-view.c
blob315cee25a76ddeef3be1939ac6d5e0c341dc17b1
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * gprof-function-call-chart-view.c
4 * Copyright (C) James Liggett 2006 <jrliggett@cox.net>
5 *
6 * Portions borrowed from the Class Inheritance Graph
7 * Copyright (C) Massimo Cora' 2005
8 *
9 * gprof-function-call-chart-view.c is free software.
11 * You may redistribute it and/or modify it under the terms of the
12 * GNU General Public License, as published by the Free Software
13 * Foundation; either version 2, or (at your option) any later version.
15 * plugin.c is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 * See the GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with plugin.c. See the file "COPYING". If not,
22 * write to: The Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor,
24 * Boston, MA 02110-1301, USA.
27 #include "gprof-function-call-chart-view.h"
29 /* Drawing helper macros */
30 #define NODE_LOWER_LEFT(node,main_index,rec_index) \
31 ((point)((field_t*)ND_shape_info (node))->fld[main_index]->fld[rec_index]->b.LL)
33 #define NODE_UPPER_RIGHT(node,main_index,rec_index) \
34 ((point)((field_t*)ND_shape_info (node))->fld[main_index]->fld[rec_index]->b.UR)
36 #define NODE_NUM_FIELDS(node) \
37 ((field_t*)ND_shape_info (node))->n_flds
39 #define NODE_NTH_FIELD(node,nth) \
40 ((field_t*)ND_shape_info (node))->fld[nth]
42 #define NODE_NTH_TEXT(node,main_index,rec_index) \
43 ((field_t*)ND_shape_info (node))->fld[main_index]->fld[rec_index]->lp->text
45 #define INCH_TO_PIXELS(inch_size) \
46 inch_size * 72
48 struct _GProfFunctionCallChartViewPriv
50 GtkWidget *canvas;
51 GtkWidget *scrolled_window;
52 gdouble canvas_size_x;
53 gdouble canvas_size_y;
54 GList *graphs;
55 GVC_t *gvc;
56 GList *canvas_items;
57 gint y_offset;
59 /* Used for context drawing */
60 Agnode_t *current_node;
61 GList *current_graph;
62 };
64 static void
65 gprof_function_call_chart_view_destroy_graph (GProfFunctionCallChartView *self)
67 GList *current_graph;
69 current_graph = self->priv->graphs;
71 while (current_graph)
73 gvFreeLayout (self->priv->gvc, current_graph->data);
74 agclose (current_graph->data);
76 current_graph = g_list_next (current_graph);
79 if (self->priv->gvc)
80 gvFreeContext (self->priv->gvc);
82 self->priv->graphs = NULL;
83 self->priv->gvc = NULL;
86 static void
87 gprof_function_call_chart_view_add_function (GProfFunctionCallChartView *self,
88 Agraph_t *subgraph,
89 GProfCallGraph *call_graph,
90 GProfCallGraphBlock *block,
91 Agnode_t *parent_node)
93 Agnode_t *new_node;
94 GList *entry_iter;
95 GProfCallGraphBlockEntry *current_entry;
96 GProfCallGraphBlockEntry *current_child;
97 GProfCallGraphBlock *next_block;
98 GProfCallGraphBlockEntry *next_block_primary_entry;
99 gchar *current_entry_name;
100 gchar *next_block_name;
102 current_entry = gprof_call_graph_block_get_primary_entry (block);
103 current_entry_name = gprof_call_graph_block_entry_get_name (current_entry);
105 new_node = agnode (subgraph, current_entry_name);
107 /* Make the node box shaped */
108 agsafeset (new_node, "shape", "box", "");
110 /* Add the edge to this node's parent, if it has one */
111 if (parent_node)
112 agedge (subgraph, parent_node, new_node);
114 /* For recursive functions, add an edge that loops back to the node */
115 if (gprof_call_graph_block_is_recursive (block))
116 agedge (subgraph, new_node, new_node);
118 current_child = gprof_call_graph_block_get_first_child (block, &entry_iter);
120 while (current_child)
122 next_block = gprof_call_graph_find_block (call_graph,
123 gprof_call_graph_block_entry_get_name (current_child));
124 current_child = gprof_call_graph_block_entry_get_next (entry_iter,
125 &entry_iter);
127 if (next_block)
129 next_block_primary_entry = gprof_call_graph_block_get_primary_entry (next_block);
130 next_block_name = gprof_call_graph_block_entry_get_name (next_block_primary_entry);
132 /* Make sure we don't go into an infinite loop on recursive functions */
133 if (strcmp(next_block_name, current_entry_name) == 0)
134 continue;
136 gprof_function_call_chart_view_add_function (self, subgraph,
137 call_graph,
138 next_block, new_node);
144 static void
145 gprof_function_call_chart_view_create_graph (GProfFunctionCallChartView *self)
147 GProfProfileData *profile_data;
148 GProfCallGraph *call_graph;
149 GProfCallGraphBlock *current_block;
150 GProfCallGraphBlockEntry *current_entry;
151 gchar *entry_name;
152 GList *root_iter;
153 Agraph_t *subgraph;
155 /* If a previous graph exists, delete it and start over */
156 gprof_function_call_chart_view_destroy_graph (self);
158 self->priv->gvc = gvContext ();
160 profile_data = gprof_view_get_data (GPROF_VIEW (self));
161 call_graph = gprof_profile_data_get_call_graph (profile_data);
163 current_block = gprof_call_graph_get_root (call_graph, &root_iter);
165 /* If the call graph doesn't have any roots, that usually means the user
166 * is showing only select functions. Recurse from each block so we still
167 * get a graph */
168 if (!current_block)
169 current_block = gprof_call_graph_get_first_block (call_graph,
170 &root_iter);
172 while (current_block)
174 current_entry = gprof_call_graph_block_get_primary_entry (current_block);
175 entry_name = gprof_call_graph_block_entry_get_name (current_entry);
176 subgraph = agopen (entry_name, AGDIGRAPHSTRICT);
177 self->priv->graphs = g_list_append (self->priv->graphs, subgraph);
179 gprof_function_call_chart_view_add_function (self, subgraph,
180 call_graph,
181 current_block, NULL);
183 current_block = gprof_call_graph_block_get_next (root_iter, &root_iter);
189 static void
190 gprof_function_call_chart_view_clear_canvas (GProfFunctionCallChartView *self)
192 if (self->priv->canvas_items)
194 g_list_foreach (self->priv->canvas_items, (GFunc) gtk_object_destroy,
195 NULL);
196 g_list_free (self->priv->canvas_items);
198 self->priv->canvas_items = NULL;
202 static gint
203 on_node_event (GnomeCanvasItem *item, GdkEvent *event, gpointer data)
205 GProfFunctionCallChartView *self;
206 gchar *function_name;
208 self = GPROF_FUNCTION_CALL_CHART_VIEW (data);
211 switch (event->type)
213 case GDK_ENTER_NOTIFY:
214 /* Make the outline wide */
215 gnome_canvas_item_set (item,
216 "width_units", 2.5,
217 "fill_color_gdk",
218 &self->priv->canvas->style->base[GTK_STATE_PRELIGHT],
219 "outline_color_gdk",
220 &self->priv->canvas->style->text[GTK_STATE_PRELIGHT],
221 NULL);
223 return TRUE;
224 break;
225 case GDK_LEAVE_NOTIFY:
226 /* Make the outline thin */
227 gnome_canvas_item_set (item,
228 "width_units", 1.0,
229 "fill_color_gdk",
230 &self->priv->canvas->style->base[GTK_STATE_NORMAL],
231 "outline_color_gdk",
232 &self->priv->canvas->style->text[GTK_STATE_NORMAL],
233 NULL);
234 return TRUE;
235 break;
236 case GDK_2BUTTON_PRESS:
237 function_name = (gchar *)g_object_get_data (G_OBJECT (item),
238 "function-name");
239 gprof_view_show_symbol_in_editor (GPROF_VIEW (self), function_name);
240 g_free (function_name);
242 return TRUE;
243 break;
244 default:
245 break;
248 return FALSE;
251 static void
252 gprof_function_call_chart_view_draw_node (GProfFunctionCallChartView *self,
253 Agnode_t *node, point *node_pos,
254 gdouble node_width,
255 gdouble node_height)
257 GnomeCanvasItem *item;
258 gdouble text_width;
260 /* Node box */
261 item = gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (self->priv->canvas)),
262 gnome_canvas_rect_get_type (),
263 "x1",
264 (gdouble) (node_pos->x - INCH_TO_PIXELS (node_width) / 2),
265 "y1",
266 (gdouble) -(node_pos->y - INCH_TO_PIXELS (node_height) / 2),
267 "x2",
268 (gdouble) (node_pos->x + INCH_TO_PIXELS (node_width) / 2),
269 "y2",
270 (gdouble) -(node_pos->y + INCH_TO_PIXELS (node_height) / 2),
271 "fill_color_gdk",
272 &self->priv->canvas->style->base[GTK_STATE_NORMAL],
273 "outline_color_gdk",
274 &self->priv->canvas->style->text[GTK_STATE_NORMAL],
275 "width_units",
276 1.0,
277 NULL);
279 g_object_set_data_full (G_OBJECT (item), "function-name", g_strdup (node->name),
280 g_free);
281 g_signal_connect (GTK_OBJECT (item), "event", G_CALLBACK (on_node_event),
282 (gpointer) self);
284 self->priv->canvas_items = g_list_append (self->priv->canvas_items, item);
286 /* Label */
287 item = gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (self->priv->canvas)),
288 gnome_canvas_text_get_type (),
289 "text",
290 node->name,
291 "justification",
292 GTK_JUSTIFY_CENTER,
293 "anchor",
294 GTK_ANCHOR_CENTER,
295 "x",
296 (gdouble) (node_pos->x - INCH_TO_PIXELS (node_width) / 2),
297 "y",
298 (gdouble) -node_pos->y,
299 "fill_color_gdk",
300 &self->priv->canvas->style->text[GTK_STATE_NORMAL],
301 "anchor",
302 GTK_ANCHOR_W,
303 NULL);
305 /* Center the label in the box */
306 g_object_get (item, "text_width", &text_width, NULL);
307 gnome_canvas_item_set (item, "x",
308 (gdouble) (node_pos->x - text_width / 2), NULL);
309 self->priv->canvas_items = g_list_append (self->priv->canvas_items, item);
311 /* Check if we've drawn outside the scroll region. If so, expand the
312 * canvas */
313 if (abs(node_pos->x + node_width) > self->priv->canvas_size_x )
315 self->priv->canvas_size_x = abs(node_pos->x) +
316 INCH_TO_PIXELS (node_width) / 2;
319 if (abs(node_pos->y + node_height) > self->priv->canvas_size_y)
321 self->priv->canvas_size_y = abs(node_pos->y) +
322 INCH_TO_PIXELS (node_height) / 2;
325 /* Set up the scrolling region */
326 gtk_widget_set_size_request (self->priv->canvas,
327 self->priv->canvas_size_x + 100,
328 self->priv->canvas_size_y + 100);
329 gnome_canvas_set_scroll_region (GNOME_CANVAS (self->priv->canvas), -50, 50,
330 (gdouble) self->priv->canvas_size_x + 50,
331 (gdouble) -self->priv->canvas_size_y - 100);
334 static void
335 gprof_function_call_chart_view_draw_edge (GProfFunctionCallChartView *self,
336 Agedge_t *edge, point *node_pos,
337 gdouble node_height)
339 GnomeCanvasItem *item;
340 GnomeCanvasPathDef *edge_path_def;
341 gdouble arrow_upper_bound;
342 gdouble arrow_x_offset;
343 gdouble arrow_height;
344 GnomeCanvasPoints *arrow_points;
345 gint i;
347 edge_path_def = gnome_canvas_path_def_new ();
349 for (i = 0; i < (ED_spl(edge)->list->size - 1); i += 3)
351 gnome_canvas_path_def_moveto (edge_path_def,
352 ((ED_spl(edge))->list->list[i]).x,
353 -((ED_spl(edge))->list->list[i]).y -
354 self->priv->y_offset);
355 gnome_canvas_path_def_curveto (edge_path_def,
356 ((ED_spl(edge))->list->list[i + 1]).x,
357 -((ED_spl(edge))->list->list[i + 1]).y -
358 self->priv->y_offset,
359 ((ED_spl(edge))->list->list[i + 2]).x,
360 -((ED_spl(edge))->list->list[i + 2]).y -
361 self->priv->y_offset,
362 ((ED_spl(edge))->list->list[i + 3]).x,
363 -((ED_spl(edge))->list->list[i + 3]).y -
364 self->priv->y_offset);
366 /* Draw the arrow at the end of the edge */
367 if (i + 3 >= (ED_spl(edge)->list->size - 1))
369 arrow_upper_bound = (gdouble) (node_pos->y +
370 INCH_TO_PIXELS (node_height) / 2) -
371 self->priv->y_offset;
372 arrow_height = abs (((ED_spl(edge))->list->list[i + 3]).y -
373 arrow_upper_bound);
375 /* Determine the length (x offset) of the arrowhead.
376 * Arrow x offsets shouldn't be more than 20 units. */
377 if ((((ED_spl(edge))->list->list[i + 3]).x -
378 ((ED_spl(edge))->list->list[i + 2]).x) > 0)
380 arrow_x_offset = sqrt( abs(100 - arrow_height * arrow_height));
382 else
384 arrow_x_offset = -sqrt( abs(100 - arrow_height * arrow_height));
387 /* Now actually draw the arrow */
388 arrow_points = gnome_canvas_points_new (2);
390 /* Starting point */
391 arrow_points->coords[0] = ((ED_spl(edge))->list->list[i + 3]).x;
392 arrow_points->coords[1] = -((ED_spl(edge))->list->list[i + 3].y +
393 self->priv->y_offset);
395 /* Arrow head */
396 arrow_points->coords[2] = ((ED_spl(edge))->list->list[i + 3]).x +
397 arrow_x_offset;
398 arrow_points->coords[3] = -(arrow_upper_bound + self->priv->y_offset);
400 if (abs (arrow_x_offset) <= 20)
402 item = gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (self->priv->canvas)),
403 gnome_canvas_line_get_type(),
404 "points",
405 arrow_points,
406 "fill_color_gdk",
407 &self->priv->canvas->style->text[GTK_STATE_NORMAL],
408 "last_arrowhead",
409 TRUE,
410 "arrow_shape_a",
411 10.0,
412 "arrow_shape_b",
413 10.0,
414 "arrow_shape_c",
415 4.0,
416 "width_units",
417 1.0,
418 NULL);
419 self->priv->canvas_items = g_list_append (self->priv->canvas_items,
420 item);
423 gnome_canvas_points_free (arrow_points);
428 /* Draw the edge path */
429 item = gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (self->priv->canvas)),
430 gnome_canvas_bpath_get_type(),
431 "bpath",
432 edge_path_def,
433 "outline_color_gdk",
434 &self->priv->canvas->style->text[GTK_STATE_NORMAL],
435 "width_pixels",
437 NULL);
438 self->priv->canvas_items = g_list_append (self->priv->canvas_items, item);
441 static void
442 gprof_function_call_chart_view_init (GProfFunctionCallChartView *self)
444 self->priv = g_new0 (GProfFunctionCallChartViewPriv, 1);
446 self->priv->canvas = gnome_canvas_new_aa ();
447 self->priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
449 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (self->priv->
450 scrolled_window),
451 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
452 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (self->priv->
453 scrolled_window),
454 self->priv->canvas);
455 gtk_widget_show_all (self->priv->scrolled_window);
457 /* Set up graph stuff */
458 aginit ();
461 static void
462 gprof_function_call_chart_view_finalize (GObject *obj)
464 GProfFunctionCallChartView *self;
466 self = (GProfFunctionCallChartView *) obj;
468 g_free (self->priv);
471 static void
472 gprof_function_call_chart_view_class_init (GProfFunctionCallChartViewClass *klass)
474 GObjectClass *object_class;
475 GProfViewClass *view_class;
477 object_class = (GObjectClass *) klass;
478 view_class = GPROF_VIEW_CLASS (klass);
480 object_class->finalize = gprof_function_call_chart_view_finalize;
481 view_class->refresh = gprof_function_call_chart_view_refresh;
482 view_class->get_widget = gprof_function_call_chart_view_get_widget;
485 GType
486 gprof_function_call_chart_view_get_type (void)
488 static GType obj_type = 0;
490 if (!obj_type)
492 static const GTypeInfo obj_info =
494 sizeof (GProfFunctionCallChartViewClass),
495 (GBaseInitFunc) NULL,
496 (GBaseFinalizeFunc) NULL,
497 (GClassInitFunc) gprof_function_call_chart_view_class_init,
498 (GClassFinalizeFunc) NULL,
499 NULL, /* class_data */
500 sizeof (GProfFunctionCallChartView),
501 0, /* n_preallocs */
502 (GInstanceInitFunc) gprof_function_call_chart_view_init,
503 NULL /* value_table */
505 obj_type = g_type_register_static (GPROF_VIEW_TYPE,
506 "GProfFunctionCallChartView", &obj_info, 0);
508 return obj_type;
511 GProfFunctionCallChartView *
512 gprof_function_call_chart_view_new (GProfProfileData *profile_data,
513 IAnjutaSymbolManager *symbol_manager,
514 IAnjutaDocumentManager *document_manager)
516 GProfFunctionCallChartView *view;
518 view = g_object_new (GPROF_FUNCTION_CALL_CHART_VIEW_TYPE, NULL);
519 gprof_view_set_data (GPROF_VIEW (view), profile_data);
520 gprof_view_set_symbol_manager (GPROF_VIEW (view), symbol_manager);
521 gprof_view_set_document_manager (GPROF_VIEW (view), document_manager);
523 return view;
526 static gboolean
527 async_draw_graph (gpointer user_data)
529 GProfFunctionCallChartView *self;
530 Agedge_t *current_edge;
531 Agraph_t *current_graph;
532 point node_pos;
533 gdouble node_width;
534 gdouble node_height;
536 self = GPROF_FUNCTION_CALL_CHART_VIEW (user_data);
538 if (self->priv->current_graph)
540 current_graph = self->priv->current_graph->data;
542 if (self->priv->current_node)
544 node_pos = ND_coord_i (self->priv->current_node);
545 node_pos.y += self->priv->y_offset;
546 node_width = ND_width (self->priv->current_node);
547 node_height = ND_height (self->priv->current_node);
549 gprof_function_call_chart_view_draw_node (self, self->priv->current_node,
550 &node_pos, node_width, node_height);
553 /* Handle the edges */
554 current_edge = agfstedge (current_graph,
555 self->priv->current_node);
557 while (current_edge)
559 gprof_function_call_chart_view_draw_edge (self, current_edge,
560 &node_pos, node_height);
562 current_edge = agnxtedge (current_graph, current_edge,
563 self->priv->current_node);
566 self->priv->current_node = agnxtnode (current_graph,
567 self->priv->current_node);
568 return TRUE;
571 self->priv->y_offset = self->priv->canvas_size_y + 25;
572 self->priv->current_graph = g_list_next (self->priv->current_graph);
574 if (self->priv->current_graph)
576 self->priv->current_node = agfstnode (self->priv->current_graph->data);
577 return TRUE;
581 gprof_function_call_chart_view_destroy_graph (self);
583 return FALSE;
586 void
587 gprof_function_call_chart_view_refresh (GProfView *view)
589 GProfFunctionCallChartView *self;
590 GList *current_graph;
592 self = GPROF_FUNCTION_CALL_CHART_VIEW (view);
594 gprof_function_call_chart_view_create_graph (self);
595 gprof_function_call_chart_view_clear_canvas (self);
597 /* Set up layout of graph */
598 current_graph = self->priv->graphs;
600 /* Set canvas size for scrolling */
601 self->priv->canvas_size_x = 250;
602 self->priv->canvas_size_y = 250;
603 self->priv->y_offset = 0;
605 while (current_graph)
607 gvLayout (self->priv->gvc, current_graph->data, "dot");
608 current_graph = g_list_next (current_graph);
611 /* Handle the graph asyncronously */
612 self->priv->current_graph = self->priv->graphs;
614 if (self->priv->current_graph)
615 self->priv->current_node = agfstnode (self->priv->current_graph->data);
617 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 200, async_draw_graph, self,
618 NULL);
621 GtkWidget *
622 gprof_function_call_chart_view_get_widget (GProfView *view)
624 GProfFunctionCallChartView *self;
626 self = GPROF_FUNCTION_CALL_CHART_VIEW (view);
628 return self->priv->scrolled_window;