1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
3 * gprof-function-call-chart-view.c
4 * Copyright (C) James Liggett 2006 <jrliggett@cox.net>
6 * Portions borrowed from the Class Inheritance Graph
7 * Copyright (C) Massimo Cora' 2005
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) \
48 struct _GProfFunctionCallChartViewPriv
51 GtkWidget
*scrolled_window
;
52 gdouble canvas_size_x
;
53 gdouble canvas_size_y
;
59 /* Used for context drawing */
60 Agnode_t
*current_node
;
65 gprof_function_call_chart_view_destroy_graph (GProfFunctionCallChartView
*self
)
69 current_graph
= self
->priv
->graphs
;
73 gvFreeLayout (self
->priv
->gvc
, current_graph
->data
);
74 agclose (current_graph
->data
);
76 current_graph
= g_list_next (current_graph
);
80 gvFreeContext (self
->priv
->gvc
);
82 self
->priv
->graphs
= NULL
;
83 self
->priv
->gvc
= NULL
;
87 gprof_function_call_chart_view_add_function (GProfFunctionCallChartView
*self
,
89 GProfCallGraph
*call_graph
,
90 GProfCallGraphBlock
*block
,
91 Agnode_t
*parent_node
)
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 */
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
,
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)
136 gprof_function_call_chart_view_add_function (self
, subgraph
,
138 next_block
, new_node
);
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
;
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
169 current_block
= gprof_call_graph_get_first_block (call_graph
,
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
,
181 current_block
, NULL
);
183 current_block
= gprof_call_graph_block_get_next (root_iter
, &root_iter
);
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
,
196 g_list_free (self
->priv
->canvas_items
);
198 self
->priv
->canvas_items
= NULL
;
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
);
213 case GDK_ENTER_NOTIFY
:
214 /* Make the outline wide */
215 gnome_canvas_item_set (item
,
218 &self
->priv
->canvas
->style
->base
[GTK_STATE_PRELIGHT
],
220 &self
->priv
->canvas
->style
->text
[GTK_STATE_PRELIGHT
],
225 case GDK_LEAVE_NOTIFY
:
226 /* Make the outline thin */
227 gnome_canvas_item_set (item
,
230 &self
->priv
->canvas
->style
->base
[GTK_STATE_NORMAL
],
232 &self
->priv
->canvas
->style
->text
[GTK_STATE_NORMAL
],
236 case GDK_2BUTTON_PRESS
:
237 function_name
= (gchar
*)g_object_get_data (G_OBJECT (item
),
239 gprof_view_show_symbol_in_editor (GPROF_VIEW (self
), function_name
);
240 g_free (function_name
);
252 gprof_function_call_chart_view_draw_node (GProfFunctionCallChartView
*self
,
253 Agnode_t
*node
, point
*node_pos
,
257 GnomeCanvasItem
*item
;
261 item
= gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (self
->priv
->canvas
)),
262 gnome_canvas_rect_get_type (),
264 (gdouble
) (node_pos
->x
- INCH_TO_PIXELS (node_width
) / 2),
266 (gdouble
) -(node_pos
->y
- INCH_TO_PIXELS (node_height
) / 2),
268 (gdouble
) (node_pos
->x
+ INCH_TO_PIXELS (node_width
) / 2),
270 (gdouble
) -(node_pos
->y
+ INCH_TO_PIXELS (node_height
) / 2),
272 &self
->priv
->canvas
->style
->base
[GTK_STATE_NORMAL
],
274 &self
->priv
->canvas
->style
->text
[GTK_STATE_NORMAL
],
279 g_object_set_data_full (G_OBJECT (item
), "function-name", g_strdup (node
->name
),
281 g_signal_connect (GTK_OBJECT (item
), "event", G_CALLBACK (on_node_event
),
284 self
->priv
->canvas_items
= g_list_append (self
->priv
->canvas_items
, item
);
287 item
= gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (self
->priv
->canvas
)),
288 gnome_canvas_text_get_type (),
296 (gdouble
) (node_pos
->x
- INCH_TO_PIXELS (node_width
) / 2),
298 (gdouble
) -node_pos
->y
,
300 &self
->priv
->canvas
->style
->text
[GTK_STATE_NORMAL
],
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
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);
335 gprof_function_call_chart_view_draw_edge (GProfFunctionCallChartView
*self
,
336 Agedge_t
*edge
, point
*node_pos
,
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
;
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
-
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
));
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);
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
);
396 arrow_points
->coords
[2] = ((ED_spl(edge
))->list
->list
[i
+ 3]).x
+
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(),
407 &self
->priv
->canvas
->style
->text
[GTK_STATE_NORMAL
],
419 self
->priv
->canvas_items
= g_list_append (self
->priv
->canvas_items
,
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(),
434 &self
->priv
->canvas
->style
->text
[GTK_STATE_NORMAL
],
438 self
->priv
->canvas_items
= g_list_append (self
->priv
->canvas_items
, item
);
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
->
451 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
452 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (self
->priv
->
455 gtk_widget_show_all (self
->priv
->scrolled_window
);
457 /* Set up graph stuff */
462 gprof_function_call_chart_view_finalize (GObject
*obj
)
464 GProfFunctionCallChartView
*self
;
466 self
= (GProfFunctionCallChartView
*) obj
;
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
;
486 gprof_function_call_chart_view_get_type (void)
488 static GType obj_type
= 0;
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
),
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);
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
);
527 async_draw_graph (gpointer user_data
)
529 GProfFunctionCallChartView
*self
;
530 Agedge_t
*current_edge
;
531 Agraph_t
*current_graph
;
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
);
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
);
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
);
581 gprof_function_call_chart_view_destroy_graph (self
);
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
,
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
;