b99c6e068913639384268f029f32a4e4dcafcd55
[obnox/wireshark/wip.git] / gtk / graph_analysis.c
1 /* graph_analysis.c
2  * Graphic Analysis addition for Wireshark
3  *
4  * $Id$
5  *
6  * Copyright 2004, Verso Technologies Inc.
7  * By Alejandro Vaquero <alejandrovaquero@yahoo.com>
8  *
9  * based on rtp_analysis.c and io_stat
10  *
11  *
12  * Wireshark - Network traffic analyzer
13  * By Gerald Combs <gerald@wireshark.org>
14  * Copyright 1998 Gerald Combs
15  *
16  * This program is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU General Public License
18  * as published by the Free Software Foundation; either version 2
19  * of the License, or (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 #include <string.h>
35
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39
40 #include <gtk/gtk.h>
41 #include <gdk/gdkkeysyms.h>
42
43 #include <epan/epan_dissect.h>
44 #include <epan/tap.h>
45 #include <epan/dissectors/packet-rtp.h>
46 #include <epan/addr_resolv.h>
47 #include "epan/filesystem.h"
48
49 #include "../util.h"
50 #include "../register.h"
51 #include "../color.h"
52 #include "../simple_dialog.h"
53 #include <wsutil/file_util.h>
54
55 #include "gtk/gtkglobals.h"
56 #include "gtk/file_dlg.h"
57 #include "gtk/gui_utils.h"
58 #include "gtk/main.h"
59 #include "gtk/graph_analysis.h"
60
61
62 /****************************************************************************/
63
64
65 #define OK_TEXT "[ Ok ]"
66 #define PT_UNDEFINED -1
67
68
69 static GtkWidget *save_to_file_w = NULL;
70
71 #define MAX_LABEL 50
72 #define MAX_COMMENT 100
73 #define ITEM_HEIGHT 20
74 #define NODE_WIDTH 100
75 #define TOP_Y_BORDER 40
76 #define BOTTOM_Y_BORDER 0
77 #define COMMENT_WIDTH 400
78 #define TIME_WIDTH 50
79
80 #define NODE_CHARS_WIDTH 20
81 #define CONV_TIME_HEADER "Conv.| Time    "
82 #define TIME_HEADER "|Time     "
83 #define CONV_TIME_EMPTY_HEADER     "     |         "
84 #define TIME_EMPTY_HEADER     "|         "
85 #define CONV_TIME_HEADER_LENGTH 16
86 #define TIME_HEADER_LENGTH 10
87
88 /****************************************************************************/
89 /* Reset the user_data structure */
90 static void graph_analysis_reset(graph_analysis_data_t* user_data)
91 {
92         int i;
93
94         user_data->num_nodes = 0;
95         user_data->num_items = 0;
96         for (i=0; i<MAX_NUM_NODES; i++){
97                 user_data->nodes[i].type = AT_NONE;
98                 user_data->nodes[i].len = 0;
99                 g_free((void *)user_data->nodes[i].data);
100                 user_data->nodes[i].data = NULL;
101         }
102
103         user_data->dlg.first_node=0;
104         user_data->dlg.first_item=0;
105         user_data->dlg.left_x_border=0;
106         user_data->dlg.selected_item=0xFFFFFFFF;    /*not item selected */
107 }
108
109 /****************************************************************************/
110 /* Reset the user_data structure */
111 static void graph_analysis_init_dlg(graph_analysis_data_t* user_data)
112 {
113         int i;
114
115         user_data->num_nodes = 0;
116         user_data->num_items = 0;
117         user_data->on_destroy_user_data = NULL;
118         user_data->data = NULL;
119         for (i=0; i<MAX_NUM_NODES; i++){
120                 user_data->nodes[i].type = AT_NONE;
121                 user_data->nodes[i].len = 0;
122                 user_data->nodes[i].data = NULL;
123         }
124
125         user_data->dlg.first_node=0;
126         user_data->dlg.first_item=0;
127         user_data->dlg.left_x_border=0;
128         user_data->dlg.selected_item=0xFFFFFFFF;    /*not item selected */
129         /* init dialog_graph */
130         user_data->dlg.needs_redraw=TRUE;
131         user_data->dlg.draw_area_time=NULL;
132         user_data->dlg.draw_area=NULL;
133         user_data->dlg.pixmap=NULL;
134         user_data->dlg.pixmap_time=NULL;
135         user_data->dlg.draw_area_comments=NULL;
136         user_data->dlg.pixmap_comments=NULL;
137         user_data->dlg.v_scrollbar=NULL;
138         user_data->dlg.v_scrollbar_adjustment=NULL;
139         user_data->dlg.hpane=NULL;
140         user_data->dlg.pixmap_width = 350;
141         user_data->dlg.pixmap_height=400;
142         user_data->dlg.first_node=0;
143         user_data->dlg.first_item=0;
144         user_data->dlg.left_x_border=0;
145         user_data->dlg.selected_item=0xFFFFFFFF;    /*not item selected */
146         user_data->dlg.window=NULL;
147         user_data->dlg.parent_w=NULL;
148         user_data->dlg.inverse = FALSE;
149         user_data->dlg.title=NULL;
150 }
151
152 /****************************************************************************/
153 /* CALLBACKS */
154
155 /****************************************************************************/
156 /* close the dialog window */
157 static void on_destroy(GtkWidget *win _U_, graph_analysis_data_t *user_data _U_)
158 {
159         int i;
160
161         for (i=0; i<MAX_NUM_NODES; i++){
162                 user_data->nodes[i].type = AT_NONE;
163                 user_data->nodes[i].len = 0;
164                 g_free((void *)user_data->nodes[i].data);
165                 user_data->nodes[i].data = NULL;
166         }
167         user_data->dlg.window = NULL;
168         g_free(user_data->dlg.title);
169         user_data->dlg.title = NULL;
170
171         if(user_data->on_destroy_user_data){
172         user_data->on_destroy_user_data(user_data->data);
173         }
174 }
175
176 #define RIGHT_ARROW 1
177 #define LEFT_ARROW 0
178 #define WIDTH_ARROW 8
179 #define HEIGHT_ARROW 6
180
181 /****************************************************************************/
182 static void draw_arrow(GdkDrawable *pixmap, GdkGC *gc, gint x, gint y, gboolean direction)
183 {
184         GdkPoint arrow_point[3];
185
186         arrow_point[0].x = x;
187         arrow_point[0].y = y-HEIGHT_ARROW/2;
188         if (direction == RIGHT_ARROW)
189                 arrow_point[1].x = x+WIDTH_ARROW;
190         else
191                 arrow_point[1].x = x-WIDTH_ARROW;
192         arrow_point[1].y = y;
193         arrow_point[2].x = x;
194         arrow_point[2].y = y+HEIGHT_ARROW/2;;
195
196         if (GDK_IS_DRAWABLE(pixmap)) {
197                 gdk_draw_polygon(pixmap, gc, TRUE, arrow_point, 3);
198         }
199 }
200
201 /****************************************************************************/
202 /* Adds trailing characters to complete the requested length.               */
203 /****************************************************************************/
204
205 static void enlarge_string(GString *gstr, guint32 length, char pad){
206
207         guint32 i;
208
209         for (i = gstr->len; i < length; i++){
210                 g_string_append_c(gstr, pad);
211         }
212 }
213
214 /****************************************************************************/
215 /* overwrites the characters in a string, between positions p1 and p2, with */
216 /*   the characters of text_to_insert                                       */
217 /*   NB: it does not check that p1 and p2 fit into string                                       */
218 /****************************************************************************/
219
220 static void overwrite (GString *gstr, char *text_to_insert, guint32 p1, guint32 p2){
221
222         guint32 len;
223         gsize pos;
224
225         if (p1 == p2)
226                 return;
227
228         if (p1 > p2){
229                 pos = p2;
230                 len = p1 - p2;
231         }
232         else{
233                 pos = p1;
234                 len = p2 - p1;
235         }
236
237         if (len > strlen(text_to_insert)){
238                 len = strlen(text_to_insert);
239         }
240
241         /* ouch this is ugly but gtk1 needs it */
242         if ((guint32)pos > (guint32)gstr->len)
243                 pos = gstr->len;
244
245         /* ouch this is ugly but gtk1 needs it */
246         if ((guint32)(pos + len) > (guint32)gstr->len)
247                 g_string_truncate(gstr, pos);
248         else
249                 g_string_erase(gstr, pos, len);
250
251         g_string_insert(gstr, pos, text_to_insert);
252 }
253
254
255 /*
256  * XXX - We might want to refactor this to write the graph data directly to
257  * the file instead of loading everything into strings first.
258  */
259
260 /****************************************************************************/
261 static gboolean dialog_graph_dump_to_file(graph_analysis_data_t* user_data)
262 {
263         guint32 i, first_node, display_items, display_nodes;
264         guint32 start_position, end_position, item_width, header_length;
265         guint32 current_item;
266         graph_analysis_item_t *gai;
267         guint16 first_conv_num = 0;
268         gboolean several_convs = FALSE;
269         gboolean first_packet = TRUE;
270
271         GString *label_string, *empty_line,*separator_line, *tmp_str, *tmp_str2;
272         char *empty_header;
273         char src_port[8],dst_port[8];
274
275         GList* list;
276
277         FILE *of;
278
279         of = ws_fopen(user_data->dlg.save_file,"w");
280         if (of==NULL){
281                 return FALSE;
282         }
283
284         label_string   = g_string_new("");
285         empty_line     = g_string_new("");
286         separator_line = g_string_new("");
287         tmp_str        = g_string_new("");
288         tmp_str2       = g_string_new("");
289
290         /* get the items to display and fill the matrix array */
291         list = g_list_first(user_data->graph_info->list);
292         current_item = 0;
293         i = 0;
294         while (list && current_item < NUM_DISPLAY_ITEMS)
295         {
296                 gai = list->data;
297                 if (gai->display){
298                         user_data->dlg.items[current_item].frame_num = gai->frame_num;
299                         user_data->dlg.items[current_item].time = gai->time;
300                         user_data->dlg.items[current_item].port_src = gai->port_src;
301                         user_data->dlg.items[current_item].port_dst = gai->port_dst;
302                         user_data->dlg.items[current_item].frame_label = gai->frame_label;
303                         user_data->dlg.items[current_item].comment = gai->comment;
304                         user_data->dlg.items[current_item].conv_num = gai->conv_num;
305                         user_data->dlg.items[current_item].src_node = gai->src_node;
306                         user_data->dlg.items[current_item].dst_node = gai->dst_node;
307                         if (first_packet){
308                                 first_conv_num = gai->conv_num;
309                                 first_packet=FALSE;
310                         }
311                         if (user_data->dlg.items[current_item].conv_num != first_conv_num){
312                                 several_convs = TRUE;
313                         }
314                         current_item++;
315                         i++;
316                 }
317
318                 list = g_list_next(list);
319         }
320         display_items = current_item;
321
322         /* if not items to display */
323         if (display_items == 0) goto exit;
324
325         display_nodes = user_data->num_nodes;
326
327         first_node = user_data->dlg.first_node;
328
329         /* Write the conv. and time headers */
330
331         if (several_convs){
332                 fprintf (of, CONV_TIME_HEADER);
333                 empty_header = CONV_TIME_EMPTY_HEADER;
334                 header_length = CONV_TIME_HEADER_LENGTH;
335         }
336         else{
337                 fprintf (of, TIME_HEADER);
338                 empty_header = TIME_EMPTY_HEADER;
339                 header_length = TIME_HEADER_LENGTH;
340         }
341
342         /* Write the node names on top */
343         for (i=0; i<display_nodes; i++){
344                 /* print the node identifiers */
345                 g_string_printf(label_string, "| %s",
346                         get_addr_name(&(user_data->nodes[i+first_node])));
347                 enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
348                 fprintf(of, "%s", label_string->str);
349                 g_string_printf(label_string, "| ");
350                 enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
351                 g_string_append(empty_line, label_string->str);
352         }
353         g_string_append_c(empty_line, '|');
354
355         enlarge_string(separator_line, empty_line->len + header_length, '-');
356
357         fprintf(of, "|\n");
358
359         /*
360          * Draw the items
361          */
362
363         for (current_item=0; current_item<display_items; current_item++){
364
365                 start_position = (user_data->dlg.items[current_item].src_node-first_node)*NODE_CHARS_WIDTH+NODE_CHARS_WIDTH/2;
366
367                 end_position = (user_data->dlg.items[current_item].dst_node-first_node)*NODE_CHARS_WIDTH+NODE_CHARS_WIDTH/2;
368
369                 if (start_position > end_position){
370                         item_width=start_position-end_position;
371                 }
372                 else if (start_position < end_position){
373                         item_width=end_position-start_position;
374                 }
375                 else{ /* same origin and destination address */
376                         end_position = start_position+NODE_CHARS_WIDTH;
377                         item_width = NODE_CHARS_WIDTH;
378                 }
379
380                 /* separator between conversations */
381                 if (user_data->dlg.items[current_item].conv_num != first_conv_num){
382                         fprintf(of, "%s\n", separator_line->str);
383                         first_conv_num=user_data->dlg.items[current_item].conv_num;
384                 }
385
386                 /* write the conversation number */
387                 if (several_convs){
388                         g_string_printf(label_string, "%i", user_data->dlg.items[current_item].conv_num);
389                         enlarge_string(label_string, 5, ' ');
390                         fprintf(of, "%s", label_string->str);
391                 }
392
393                 /* write the time */
394                 g_string_printf(label_string, "|%.3f", user_data->dlg.items[current_item].time);
395                 enlarge_string(label_string, 10, ' ');
396                 fprintf(of, "%s", label_string->str);
397
398                 /* write the frame label */
399
400                 g_string_printf(tmp_str, empty_line->str);
401                 overwrite(tmp_str,user_data->dlg.items[current_item].frame_label,
402                         start_position,
403                         end_position
404                         );
405                 fprintf(of, "%s", tmp_str->str);
406
407                 /* write the comments */
408                 fprintf(of, "%s\n", user_data->dlg.items[current_item].comment);
409
410                 /* write the arrow and frame label*/
411                 fprintf(of, "%s", empty_header);
412
413                 g_string_printf(tmp_str, empty_line->str);
414
415                 g_string_truncate(tmp_str2, 0);
416
417                 if (start_position<end_position){
418                         enlarge_string(tmp_str2, item_width-2, '-');
419                         g_string_append_c(tmp_str2, '>');
420                 }
421                 else{
422                         g_string_printf(tmp_str2, "<");
423                         enlarge_string(tmp_str2, item_width-1, '-');
424                 }
425
426                 overwrite(tmp_str,tmp_str2->str,
427                         start_position,
428                         end_position
429                         );
430
431                 g_snprintf(src_port,8,"(%i)", user_data->dlg.items[current_item].port_src);
432                 g_snprintf(dst_port,8,"(%i)", user_data->dlg.items[current_item].port_dst);
433
434                 if (start_position<end_position){
435                         overwrite(tmp_str,src_port,start_position-9,start_position-1);
436                         overwrite(tmp_str,dst_port,end_position+1,end_position+9);
437                 }
438                 else{
439                         overwrite(tmp_str,src_port,start_position+1,start_position+9);
440                         overwrite(tmp_str,dst_port,end_position-9,end_position+1);
441                 }
442
443                 fprintf(of,"%s\n",tmp_str->str);
444         }
445
446 exit:
447         g_string_free(label_string, TRUE);
448         g_string_free(empty_line, TRUE);
449         g_string_free(separator_line, TRUE);
450         g_string_free(tmp_str, TRUE);
451         g_string_free(tmp_str2, TRUE);
452
453         fclose (of);
454         return TRUE;
455
456 }
457
458 /****************************************************************************/
459 static void save_to_file_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
460 {
461         /* Note that we no longer have a Save to file dialog box. */
462         save_to_file_w = NULL;
463 }
464
465 /****************************************************************************/
466 /* save in a file */
467
468 /* first an auxiliary function in case we need an overwrite confirmation dialog */
469
470 static void overwrite_existing_file_cb(gpointer dialog _U_, gint btn, gpointer user_data _U_)
471 {
472         graph_analysis_data_t *user_data_p;
473
474         user_data_p = user_data;
475
476         switch(btn) {
477         case(ESD_BTN_YES):
478             /* overwrite the file*/
479             dialog_graph_dump_to_file(user_data);
480             break;
481         case(ESD_BTN_NO):
482             break;
483         default:
484             g_assert_not_reached();
485         }
486 }
487
488 /* and then the save in a file dialog itself */
489
490 static void save_to_file_ok_cb(GtkWidget *ok_bt _U_, gpointer user_data _U_)
491 {
492         FILE *file_test;
493         gpointer dialog;
494         graph_analysis_data_t *user_data_p;
495
496         user_data_p = user_data;
497
498         user_data_p->dlg.save_file = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (save_to_file_w)));
499
500         /* Perhaps the user specified a directory instead of a file.
501         Check whether they did. */
502         if (test_for_directory(user_data_p->dlg.save_file) == EISDIR) {
503                 /* It's a directory - set the file selection box to display it. */
504                 set_last_open_dir(user_data_p->dlg.save_file);
505                 g_free(user_data_p->dlg.save_file);
506                 file_selection_set_current_folder(save_to_file_w, get_last_open_dir());
507                 return;
508         }
509
510
511         /* check whether the file exists */
512         file_test = ws_fopen(user_data_p->dlg.save_file,"r");
513         if (file_test!=NULL){
514
515                 dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_YES_NO,
516                   "%sFile: \"%s\" already exists!%s\n\n"
517                   "Do you want to overwrite it?",
518                   simple_dialog_primary_start(),user_data_p->dlg.save_file, simple_dialog_primary_end());
519                 simple_dialog_set_cb(dialog, overwrite_existing_file_cb, user_data);
520                 fclose(file_test);
521         }
522
523         else{
524                 if (!dialog_graph_dump_to_file(user_data))
525                         return;
526         }
527         window_destroy(GTK_WIDGET(save_to_file_w));
528
529 }
530
531 /****************************************************************************/
532 static void
533 on_save_bt_clicked                    (GtkButton       *button _U_,
534                                         gpointer         user_data _U_)
535 {
536         GtkWidget *vertb;
537         GtkWidget *ok_bt;
538
539         if (save_to_file_w != NULL) {
540                 /* There's already a Save to file dialog box; reactivate it. */
541                 reactivate_window(save_to_file_w);
542                 return;
543         }
544
545         save_to_file_w = gtk_file_selection_new("Wireshark: Save graph to plain text file");
546
547         /* Container for each row of widgets */
548         vertb = gtk_vbox_new(FALSE, 0);
549         gtk_container_border_width(GTK_CONTAINER(vertb), 5);
550         gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(save_to_file_w)->action_area),
551                 vertb, FALSE, FALSE, 0);
552         gtk_widget_show (vertb);
553
554         ok_bt = GTK_FILE_SELECTION(save_to_file_w)->ok_button;
555         g_signal_connect(ok_bt, "clicked", G_CALLBACK(save_to_file_ok_cb), user_data);
556
557         window_set_cancel_button(save_to_file_w,
558         GTK_FILE_SELECTION(save_to_file_w)->cancel_button, window_cancel_button_cb);
559
560         g_signal_connect(save_to_file_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
561         g_signal_connect(save_to_file_w, "destroy", G_CALLBACK(save_to_file_destroy_cb),
562                        NULL);
563
564         gtk_widget_show(save_to_file_w);
565         window_present(save_to_file_w);
566
567
568 }
569
570 /****************************************************************************/
571 static void dialog_graph_draw(graph_analysis_data_t* user_data)
572 {
573         guint32 i, last_item, first_item, display_items;
574         guint32 start_arrow, end_arrow, label_x, src_port_x, dst_port_x, arrow_width;
575         guint32 current_item;
576         guint32 left_x_border;
577         guint32 right_x_border;
578         guint32 top_y_border;
579         guint32 bottom_y_border;
580         graph_analysis_item_t *gai;
581         guint16 first_conv_num;
582         gboolean several_convs = FALSE;
583         gboolean first_packet = TRUE;
584
585         GdkGC *frame_fg_color;
586         GdkGC *frame_bg_color;
587         GdkGC *div_line_color;
588
589         PangoLayout  *layout;
590         PangoLayout  *big_layout;
591         PangoLayout  *small_layout;
592         gint label_width, label_height;
593         guint32 draw_width, draw_height;
594         char label_string[MAX_COMMENT];
595         GList* list;
596
597         /* new variables */
598
599         if(!user_data->dlg.needs_redraw){
600                 return;
601         }
602         user_data->dlg.needs_redraw=FALSE;
603
604         /* Clear out old plt */
605         if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap_time) )
606                 gdk_draw_rectangle(user_data->dlg.pixmap_time,
607                                                    user_data->dlg.draw_area_time->style->white_gc,
608                                                    TRUE,
609                                                    0, 0,
610                                                    user_data->dlg.draw_area_time->allocation.width,
611                                                    user_data->dlg.draw_area_time->allocation.height);
612
613         if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap) )
614                 gdk_draw_rectangle(user_data->dlg.pixmap,
615                                                    user_data->dlg.draw_area->style->white_gc,
616                                                    TRUE,
617                                                    0, 0,
618                                                    user_data->dlg.draw_area->allocation.width,
619                                                    user_data->dlg.draw_area->allocation.height);
620
621         if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap_comments) )
622                 gdk_draw_rectangle(user_data->dlg.pixmap_comments,
623                                                    user_data->dlg.draw_area->style->white_gc,
624                                                    TRUE,
625                                                    0, 0,
626                                                    user_data->dlg.draw_area_comments->allocation.width,
627                                                    user_data->dlg.draw_area_comments->allocation.height);
628
629         /* Calculate the y border */
630         top_y_border=TOP_Y_BORDER;      /* to display the node address */
631         bottom_y_border=2;
632
633             draw_height=user_data->dlg.draw_area->allocation.height-top_y_border-bottom_y_border;
634
635         first_item = user_data->dlg.first_item;
636         display_items = draw_height/ITEM_HEIGHT;
637         last_item = first_item+display_items-1;
638
639         /* get the items to display and fill the matrix array */
640         list = g_list_first(user_data->graph_info->list);
641         current_item = 0;
642         i = 0;
643         while (list)
644         {
645                 gai = list->data;
646                 if (gai->display){
647                         if (current_item>=display_items) break;         /* the item is outside the display */
648                         if (i>=first_item){
649                                 user_data->dlg.items[current_item].frame_num = gai->frame_num;
650                                 user_data->dlg.items[current_item].time = gai->time;
651                                 user_data->dlg.items[current_item].port_src = gai->port_src;
652                                 user_data->dlg.items[current_item].port_dst = gai->port_dst;
653                                 /* Add "..." if the length is 50 characters */
654                                 if (strlen(gai->frame_label) > 48) {
655                                         gai->frame_label[48] = '.';
656                                         gai->frame_label[47] = '.';
657                                         gai->frame_label[46] = '.';
658                                 }
659                                 user_data->dlg.items[current_item].frame_label = gai->frame_label;
660                                 user_data->dlg.items[current_item].comment = gai->comment;
661                                 user_data->dlg.items[current_item].conv_num = gai->conv_num;
662
663                                 if (first_packet){
664                                         first_conv_num = gai->conv_num;
665                                         first_packet=FALSE;
666                                 }
667
668                                 if (user_data->dlg.items[current_item].conv_num != first_conv_num){
669                                         several_convs = TRUE;
670                                 }
671
672                                 user_data->dlg.items[current_item].src_node = gai->src_node;
673                                 user_data->dlg.items[current_item].dst_node = gai->dst_node;
674                                 user_data->dlg.items[current_item].line_style = gai->line_style;
675                                 current_item++;
676                         }
677                         i++;
678                 }
679
680                 list = g_list_next(list);
681         }
682         /* in case the windows is resized so we have to move the top item */
683         if ((first_item + display_items) > user_data->num_items){
684                 if (display_items>user_data->num_items)
685                         first_item=0;
686                 else
687                         first_item = user_data->num_items - display_items;
688         }
689
690         /* in case there are less items than possible displayed */
691         display_items = current_item;
692         last_item = first_item+display_items-1;
693
694         /* if not items to display */
695         if (display_items == 0) return;
696
697
698         /* Calculate the x borders */
699         /* We use time from the last display item to calcultate the x left border */
700         g_snprintf(label_string, MAX_LABEL, "%.3f", user_data->dlg.items[display_items-1].time);
701         layout = gtk_widget_create_pango_layout(user_data->dlg.draw_area_time, label_string);
702         big_layout = gtk_widget_create_pango_layout(user_data->dlg.draw_area_time, label_string);
703         small_layout = gtk_widget_create_pango_layout(user_data->dlg.draw_area_time, label_string);
704
705         /* XXX - to prevent messages like "Couldn't load font x, falling back to y", I've changed font
706            description from "Helvetica-Bold 8" to "Helvetica,Sans,Bold 8", this seems to be
707            conforming to the API, see http://developer.gnome.org/doc/API/2.0/pango/pango-Fonts.html */
708         pango_layout_set_font_description(big_layout, pango_font_description_from_string("Helvetica,Sans,Bold 8"));
709         pango_layout_set_font_description(small_layout, pango_font_description_from_string("Helvetica,Sans,Bold 7"));
710
711         pango_layout_get_pixel_size(layout, &label_width, &label_height);
712
713                 /* resize the "time" draw area */
714
715         left_x_border=3;
716         user_data->dlg.left_x_border = left_x_border;
717
718         right_x_border=2;
719         draw_width=user_data->dlg.pixmap_width-right_x_border-left_x_border;
720
721         /* Paint time title background */
722         if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap_time) )
723                 gdk_draw_rectangle(user_data->dlg.pixmap_time,
724                                                    user_data->dlg.draw_area_time->style->bg_gc[2],
725                                                    TRUE,
726                                                    0,
727                                                    0,
728                                                    user_data->dlg.draw_area_time->allocation.width,
729                                                    top_y_border);
730         /* Paint main title background */
731         if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap) )
732                 gdk_draw_rectangle(user_data->dlg.pixmap,
733                                                    user_data->dlg.draw_area->style->bg_gc[2],
734                                                    TRUE,
735                                                    0,
736                                                    0,
737                                                    user_data->dlg.draw_area->allocation.width,
738                                                    top_y_border);
739         /* Paint main comment background */
740         if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap_comments) )
741                 gdk_draw_rectangle(user_data->dlg.pixmap_comments,
742                                                    user_data->dlg.draw_area_comments->style->bg_gc[2],
743                                                    TRUE,
744                                                    0,
745                                                    0,
746                                                    user_data->dlg.draw_area_comments->allocation.width,
747                                                    top_y_border);
748
749
750         /* Draw the word "Time" on top of time column */
751         g_snprintf(label_string, label_width, "%s", "Time");
752         pango_layout_set_text(layout, label_string, -1);
753         pango_layout_get_pixel_size(layout, &label_width, &label_height);
754         if (GDK_IS_DRAWABLE(user_data->dlg.pixmap_time)) {
755                 gdk_draw_layout(user_data->dlg.pixmap_time,
756                                                 user_data->dlg.draw_area_time->style->black_gc,
757                                                 left_x_border,
758                                                 top_y_border/2-label_height/2,
759                                                 layout);
760         }
761
762         /* Draw the word "Comment" on top of comment column */
763         g_snprintf(label_string, label_width, "%s", "Comment");
764         pango_layout_set_text(layout, label_string, -1);
765         pango_layout_get_pixel_size(layout, &label_width, &label_height);
766         if (GDK_IS_DRAWABLE(user_data->dlg.pixmap_comments)) {
767                 gdk_draw_layout(user_data->dlg.pixmap_comments,
768                            user_data->dlg.draw_area_comments->style->black_gc,
769                            MAX_COMMENT/2-label_width/2,
770                            top_y_border/2-label_height/2,
771                            layout);
772         }
773
774         /* Paint the background items */
775         for (current_item=0; current_item<display_items; current_item++){
776                 /*select the color. if it is the selected item select blue color */
777                 if ( current_item+first_item == user_data->dlg.selected_item ) {
778                         frame_bg_color = user_data->dlg.bg_gc[0];
779                 } else {
780                         frame_bg_color = user_data->dlg.bg_gc[1+user_data->dlg.items[current_item].conv_num%MAX_NUM_COL_CONV];
781                 }
782
783                 /* Paint background */
784                 if (GDK_IS_DRAWABLE(user_data->dlg.pixmap)) {
785                         gdk_draw_rectangle(user_data->dlg.pixmap,
786                                                            frame_bg_color,
787                                                            TRUE,
788                                                            left_x_border,
789                                                            top_y_border+current_item*ITEM_HEIGHT,
790                                                            draw_width,
791                                                            ITEM_HEIGHT);
792                 }
793         }
794         /* Draw the node names on top and the division lines */
795         for (i=0; i<user_data->num_nodes; i++){
796                 /* print the node identifiers */
797                 /* XXX we assign 5 pixels per character in the node identity */
798                 g_snprintf(label_string, NODE_WIDTH/5, "%s",
799                         get_addr_name(&(user_data->nodes[i])));
800                 pango_layout_set_text(layout, label_string, -1);
801                 pango_layout_get_pixel_size(layout, &label_width, &label_height);
802                 if (GDK_IS_DRAWABLE(user_data->dlg.pixmap)) {
803                         gdk_draw_layout(user_data->dlg.pixmap,
804                                                         user_data->dlg.draw_area->style->black_gc,
805                                                         left_x_border+NODE_WIDTH/2-label_width/2+NODE_WIDTH*i,
806                                                         top_y_border/2-label_height/2,
807                                                         layout);
808                 }
809
810                 /* draw the node division lines */
811                 if (GDK_IS_DRAWABLE(user_data->dlg.pixmap) ) {
812                         gdk_draw_line(user_data->dlg.pixmap, user_data->dlg.div_line_gc[0],
813                                                   left_x_border+NODE_WIDTH/2+NODE_WIDTH*i,
814                                                   top_y_border,
815                                                   left_x_border+NODE_WIDTH/2+NODE_WIDTH*i,
816                                                   user_data->dlg.draw_area->allocation.height-bottom_y_border);
817                 }
818
819         }
820
821         /* Draw the items */
822         for (current_item=0; current_item<display_items; current_item++){
823                 /* draw the time */
824                 g_snprintf(label_string, MAX_LABEL, "%.3f", user_data->dlg.items[current_item].time);
825                 pango_layout_set_text(layout, label_string, -1);
826                 pango_layout_get_pixel_size(layout, &label_width, &label_height);
827                 if (GDK_IS_DRAWABLE(user_data->dlg.pixmap_time)) {
828                         gdk_draw_layout(user_data->dlg.pixmap_time,
829                                                         user_data->dlg.draw_area->style->black_gc,
830                                                         3,
831                                                         top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT/2-label_height/2,
832                                                         layout);
833                 }
834
835                 /*draw the comments */
836                 g_snprintf(label_string, MAX_COMMENT, "%s", user_data->dlg.items[current_item].comment);
837                 pango_layout_set_text(small_layout, label_string, -1);
838                 pango_layout_get_pixel_size(small_layout, &label_width, &label_height);
839                 if (GDK_IS_DRAWABLE(user_data->dlg.pixmap_comments)) {
840                         gdk_draw_layout(user_data->dlg.pixmap_comments,
841                                                         user_data->dlg.draw_area->style->black_gc,
842                                                         2,
843                                                         top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT/2-label_height/2,
844                                                         small_layout);
845                 }
846                 /* select colors */
847                 if ( current_item+first_item == user_data->dlg.selected_item ){
848                         frame_fg_color = user_data->dlg.draw_area->style->white_gc;
849                         div_line_color = user_data->dlg.div_line_gc[1];
850                 } else {
851                         frame_fg_color = user_data->dlg.draw_area->style->black_gc;
852                         div_line_color = user_data->dlg.div_line_gc[0];
853                 }
854                 /* draw the arrow line */
855                 start_arrow = left_x_border+(user_data->dlg.items[current_item].src_node)*NODE_WIDTH+NODE_WIDTH/2;
856                 end_arrow = left_x_border+(user_data->dlg.items[current_item].dst_node)*NODE_WIDTH+NODE_WIDTH/2;
857
858                 if (GDK_IS_DRAWABLE(user_data->dlg.pixmap) ) {
859                         gdk_draw_line(user_data->dlg.pixmap, frame_fg_color,
860                                 start_arrow,
861                                 top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-7,
862                                 end_arrow,
863                                 top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-7);
864
865                         /* draw the additional line when line style is 2 pixels width */
866                         if (user_data->dlg.items[current_item].line_style == 2) {
867                                 gdk_draw_line(user_data->dlg.pixmap, frame_fg_color,
868                                         start_arrow,
869                                         top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-6,
870                                         end_arrow,
871                                         top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-6);
872                         }
873                 }
874
875                 /* draw the arrow */
876                 if (start_arrow<end_arrow)
877                         draw_arrow(user_data->dlg.pixmap, frame_fg_color, end_arrow-WIDTH_ARROW,top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-7, RIGHT_ARROW);
878                 else
879                         draw_arrow(user_data->dlg.pixmap, frame_fg_color, end_arrow+WIDTH_ARROW,top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-7, LEFT_ARROW);
880
881                 /* draw the frame comment */
882                 g_snprintf(label_string, MAX_LABEL, "%s", user_data->dlg.items[current_item].frame_label);
883                 pango_layout_set_text(big_layout, label_string, -1);
884                 pango_layout_get_pixel_size(big_layout, &label_width, &label_height);
885                 if (start_arrow<end_arrow){
886                         arrow_width = end_arrow-start_arrow;
887                         label_x = arrow_width/2+start_arrow;
888                 }
889                 else {
890                         arrow_width = start_arrow-end_arrow;
891                         label_x = arrow_width/2+end_arrow;
892                 }
893
894                 if (label_width>(gint)arrow_width) arrow_width = label_width;
895
896                 if ((int)left_x_border > ((int)label_x-(int)label_width/2))
897                         label_x = left_x_border + label_width/2;
898
899                 if (GDK_IS_DRAWABLE(user_data->dlg.pixmap)) {
900                         gdk_draw_layout(user_data->dlg.pixmap,
901                                                         frame_fg_color,
902                                                         label_x - label_width/2,
903                                                         top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT/2-label_height/2-3,
904                                                         big_layout);
905                 }
906
907                 /* draw the source port number */
908                 g_snprintf(label_string, MAX_LABEL, "(%i)", user_data->dlg.items[current_item].port_src);
909                 pango_layout_set_text(small_layout, label_string, -1);
910                 pango_layout_get_pixel_size(small_layout, &label_width, &label_height);
911                 if (start_arrow<end_arrow){
912                         src_port_x = start_arrow - label_width - 2;
913                 }
914                 else {
915                         src_port_x = start_arrow + 2;
916                 }
917                 if (GDK_IS_DRAWABLE(user_data->dlg.pixmap)) {
918                         gdk_draw_layout(user_data->dlg.pixmap,
919                                                         div_line_color,
920                                                         src_port_x,
921                                                         top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-2-label_height/2-2,
922                                                         small_layout);
923                 }
924
925                 /* draw the destination port number */
926                 g_snprintf(label_string, MAX_LABEL, "(%i)", user_data->dlg.items[current_item].port_dst);
927                 pango_layout_set_text(small_layout, label_string, -1);
928                 pango_layout_get_pixel_size(small_layout, &label_width, &label_height);
929                 if (start_arrow<end_arrow){
930                         dst_port_x = end_arrow + 2;
931                 }
932                 else {
933                         dst_port_x = end_arrow - label_width - 2;
934                 }
935                 if (GDK_IS_DRAWABLE(user_data->dlg.pixmap)) {
936                         gdk_draw_layout(user_data->dlg.pixmap,
937                                                         div_line_color,
938                                                         dst_port_x,
939                                                         top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-2-label_height/2-2,
940                                                         small_layout);
941                 }
942                 /* draw the div line of the selected item with soft gray*/
943                 if ( current_item+first_item == user_data->dlg.selected_item )
944                         for (i=0; i<user_data->num_nodes; i++){
945                                 if (GDK_IS_DRAWABLE(user_data->dlg.pixmap) ) {
946                                         gdk_draw_line(user_data->dlg.pixmap, user_data->dlg.div_line_gc[1],
947                                                                   left_x_border+NODE_WIDTH/2+NODE_WIDTH*i,
948                                                                   (user_data->dlg.selected_item-first_item)*ITEM_HEIGHT+TOP_Y_BORDER,
949                                                                   left_x_border+NODE_WIDTH/2+NODE_WIDTH*i,
950                                                                   (user_data->dlg.selected_item-first_item)*ITEM_HEIGHT+TOP_Y_BORDER+ITEM_HEIGHT);
951                                 }
952                         }
953         }
954
955         g_object_unref(G_OBJECT(layout));
956
957         /* refresh the draw areas */
958         if (GDK_IS_DRAWABLE(user_data->dlg.draw_area_time->window) )
959                 gdk_draw_pixmap(user_data->dlg.draw_area_time->window,
960                                                 user_data->dlg.draw_area_time->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.draw_area_time)],
961                                                 user_data->dlg.pixmap_time,
962                                                 0, 0,
963                                                 0, 0,
964                                                 user_data->dlg.draw_area_time->allocation.width, user_data->dlg.draw_area_time->allocation.height);
965
966         if (GDK_IS_DRAWABLE(user_data->dlg.draw_area->window) )
967                 gdk_draw_pixmap(user_data->dlg.draw_area->window,
968                                                 user_data->dlg.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.draw_area)],
969                                                 user_data->dlg.pixmap,
970                                                 0, 0,
971                                                 0, 0,
972                                                 user_data->dlg.draw_area->allocation.width, user_data->dlg.draw_area->allocation.height);
973
974         if (GDK_IS_DRAWABLE(user_data->dlg.draw_area_comments->window) )
975                 gdk_draw_pixmap(user_data->dlg.draw_area_comments->window,
976                                                 user_data->dlg.draw_area_comments->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.draw_area_comments)],
977                                                 user_data->dlg.pixmap_comments,
978                                                 0, 0,
979                                                 0, 0,
980                                                 user_data->dlg.draw_area_comments->allocation.width, user_data->dlg.draw_area_comments->allocation.height);
981
982         /* update the v_scrollbar */
983         user_data->dlg.v_scrollbar_adjustment->upper=(gfloat) user_data->num_items-1;
984         user_data->dlg.v_scrollbar_adjustment->step_increment=1;
985         user_data->dlg.v_scrollbar_adjustment->page_increment=(gfloat) (last_item-first_item);
986         user_data->dlg.v_scrollbar_adjustment->page_size=(gfloat) (last_item-first_item);
987         user_data->dlg.v_scrollbar_adjustment->value=(gfloat) first_item;
988
989         gtk_adjustment_changed(user_data->dlg.v_scrollbar_adjustment);
990         gtk_adjustment_value_changed(user_data->dlg.v_scrollbar_adjustment);
991 }
992
993 /****************************************************************************/
994 static void dialog_graph_redraw(graph_analysis_data_t* user_data)
995 {
996         user_data->dlg.needs_redraw=TRUE;
997         dialog_graph_draw(user_data);
998 }
999
1000 /****************************************************************************/
1001 static gint button_press_event(GtkWidget *widget, GdkEventButton *event _U_)
1002 {
1003         graph_analysis_data_t *user_data;
1004         guint32 item;
1005
1006         user_data=(graph_analysis_data_t *)g_object_get_data(G_OBJECT(widget), "graph_analysis_data_t");
1007
1008         if (event->type != GDK_BUTTON_PRESS) return TRUE;
1009
1010         if (event->y<TOP_Y_BORDER) return TRUE;
1011
1012         /* get the item clicked */
1013         item = ((guint32)event->y - TOP_Y_BORDER) / ITEM_HEIGHT;
1014         if (item >= user_data->num_items) return TRUE;
1015         user_data->dlg.selected_item = item + user_data->dlg.first_item;
1016
1017         user_data->dlg.needs_redraw=TRUE;
1018         dialog_graph_draw(user_data);
1019
1020         cf_goto_frame(&cfile, user_data->dlg.items[item].frame_num);
1021
1022         return TRUE;
1023 }
1024
1025 /****************************************************************************/
1026 static gint scroll_event(GtkWidget *widget, GdkEventScroll *event)
1027 {
1028         graph_analysis_data_t *user_data;
1029
1030         user_data=(graph_analysis_data_t *)g_object_get_data(G_OBJECT(widget), "graph_analysis_data_t");
1031
1032         /* Up scroll */
1033         switch(event->direction) {
1034         case(GDK_SCROLL_UP):
1035                 if (user_data->dlg.first_item == 0) return TRUE;
1036                 if (user_data->dlg.first_item < 3)
1037                         user_data->dlg.first_item = 0;
1038                 else
1039                         user_data->dlg.first_item -= 3;
1040                 break;
1041         case(GDK_SCROLL_DOWN):
1042                 if ((user_data->dlg.first_item+user_data->dlg.v_scrollbar_adjustment->page_size+1 == user_data->num_items)) return TRUE;
1043                 if ((user_data->dlg.first_item+user_data->dlg.v_scrollbar_adjustment->page_size+1) > (user_data->num_items-3))
1044                         user_data->dlg.first_item = user_data->num_items-(guint32)user_data->dlg.v_scrollbar_adjustment->page_size-1;
1045                 else
1046                         user_data->dlg.first_item += 3;
1047             break;
1048         case(GDK_SCROLL_LEFT):
1049         case(GDK_SCROLL_RIGHT):
1050             /* nothing to do */
1051             break;
1052         }
1053         dialog_graph_redraw(user_data);
1054
1055         return TRUE;
1056 }
1057
1058 /****************************************************************************/
1059 static gint key_press_event(GtkWidget *widget, GdkEventKey *event _U_)
1060 {
1061         graph_analysis_data_t *user_data;
1062
1063         user_data=(graph_analysis_data_t *)g_object_get_data(G_OBJECT(widget), "graph_analysis_data_t");
1064
1065         /* if there is nothing selected, just return */
1066         if (user_data->dlg.selected_item == 0xFFFFFFFF) return TRUE;
1067
1068         /* Up arrow */
1069         if (event->keyval == GDK_Up){
1070                 if (user_data->dlg.selected_item == 0) return TRUE;
1071                 user_data->dlg.selected_item--;
1072                 if ( (user_data->dlg.selected_item<user_data->dlg.first_item) || (user_data->dlg.selected_item>user_data->dlg.first_item+user_data->dlg.v_scrollbar_adjustment->page_size) )
1073                         user_data->dlg.first_item = user_data->dlg.selected_item;
1074                 /* Down arrow */
1075         } else if (event->keyval == GDK_Down){
1076                 if (user_data->dlg.selected_item == user_data->num_items-1) return TRUE;
1077                 user_data->dlg.selected_item++;
1078                 if ( (user_data->dlg.selected_item<user_data->dlg.first_item) || (user_data->dlg.selected_item>user_data->dlg.first_item+user_data->dlg.v_scrollbar_adjustment->page_size) )
1079                         user_data->dlg.first_item = (guint32)user_data->dlg.selected_item-(guint32)user_data->dlg.v_scrollbar_adjustment->page_size;
1080         } else if (event->keyval == GDK_Left){
1081                 if (user_data->dlg.first_node == 0) return TRUE;
1082                 user_data->dlg.first_node--;
1083         } else return TRUE;
1084
1085         user_data->dlg.needs_redraw=TRUE;
1086         dialog_graph_draw(user_data);
1087
1088         cf_goto_frame(&cfile, user_data->dlg.items[user_data->dlg.selected_item-user_data->dlg.first_item].frame_num);
1089
1090         return TRUE;
1091 }
1092
1093 /****************************************************************************/
1094 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1095 {
1096         graph_analysis_data_t *user_data;
1097
1098         user_data=(graph_analysis_data_t *)g_object_get_data(G_OBJECT(widget), "graph_analysis_data_t");
1099         if(!user_data){
1100                 exit(10);
1101         }
1102
1103         if (GDK_IS_DRAWABLE(widget->window) )
1104                 gdk_draw_pixmap(widget->window,
1105                 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1106                 user_data->dlg.pixmap,
1107                 event->area.x, event->area.y,
1108                 event->area.x, event->area.y,
1109                 event->area.width, event->area.height);
1110
1111         return FALSE;
1112 }
1113
1114 /****************************************************************************/
1115 static gint expose_event_comments(GtkWidget *widget, GdkEventExpose *event)
1116 {
1117         graph_analysis_data_t *user_data;
1118
1119         user_data=(graph_analysis_data_t *)g_object_get_data(G_OBJECT(widget), "graph_analysis_data_t");
1120         if(!user_data){
1121                 exit(10);
1122         }
1123
1124         if (GDK_IS_DRAWABLE(widget->window) )
1125                 gdk_draw_pixmap(widget->window,
1126                 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1127                 user_data->dlg.pixmap_comments,
1128                 event->area.x, event->area.y,
1129                 event->area.x, event->area.y,
1130                 event->area.width, event->area.height);
1131
1132         return FALSE;
1133 }
1134
1135 /****************************************************************************/
1136 static gint expose_event_time(GtkWidget *widget, GdkEventExpose *event)
1137 {
1138         graph_analysis_data_t *user_data;
1139
1140         user_data=(graph_analysis_data_t *)g_object_get_data(G_OBJECT(widget), "graph_analysis_data_t");
1141         if(!user_data){
1142                 exit(10);
1143         }
1144
1145         if (GDK_IS_DRAWABLE(widget->window) )
1146                 gdk_draw_pixmap(widget->window,
1147                 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1148                 user_data->dlg.pixmap_time,
1149                 event->area.x, event->area.y,
1150                 event->area.x, event->area.y,
1151                 event->area.width, event->area.height);
1152
1153         return FALSE;
1154 }
1155
1156 /****************************************************************************/
1157 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1158 {
1159         graph_analysis_data_t *user_data;
1160         int i;
1161
1162         /* gray and soft gray colors */
1163         static GdkColor color_div_line[2] = {
1164                 {0, 0x64ff, 0x64ff, 0x64ff},
1165                 {0, 0x7fff, 0x7fff, 0x7fff}
1166         };
1167
1168         /* the first calor is blue to highlight the selected item */
1169         static GdkColor col[MAX_NUM_COL_CONV+1] = {
1170                 {0,     0x00FF, 0x00FF, 0xFFFF},
1171         {0,     0x33FF, 0xFFFF, 0x33FF},
1172         {0,     0x00FF, 0xCCFF, 0xCCFF},
1173                 {0,     0x66FF, 0xFFFF, 0xFFFF},
1174                 {0,     0x99FF, 0x66FF, 0xFFFF},
1175                 {0,     0xFFFF, 0xFFFF, 0x33FF},
1176                 {0,     0xCCFF, 0x99FF, 0xFFFF},
1177                 {0,     0xCCFF, 0xFFFF, 0x33FF},
1178                 {0,     0xFFFF, 0xCCFF, 0xCCFF},
1179                 {0,     0xFFFF, 0x99FF, 0x66FF},
1180                 {0,     0xFFFF, 0xFFFF, 0x99FF}
1181         };
1182
1183         user_data=(graph_analysis_data_t *)g_object_get_data(G_OBJECT(widget), "graph_analysis_data_t");
1184
1185         if(!user_data){
1186                 exit(10);
1187         }
1188
1189         if(user_data->dlg.pixmap){
1190                 gdk_pixmap_unref(user_data->dlg.pixmap);
1191                 user_data->dlg.pixmap=NULL;
1192         }
1193
1194         user_data->dlg.pixmap=gdk_pixmap_new(widget->window,
1195                         widget->allocation.width,
1196                         widget->allocation.height,
1197                         -1);
1198
1199         if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap) )
1200                         gdk_draw_rectangle(user_data->dlg.pixmap,
1201                 widget->style->white_gc,
1202                 TRUE,
1203                 0, 0,
1204                 widget->allocation.width,
1205                 widget->allocation.height);
1206
1207         /* create gc for division lines and set the line stype to dash*/
1208         for (i=0; i<2; i++){
1209                 user_data->dlg.div_line_gc[i]=gdk_gc_new(user_data->dlg.pixmap);
1210                 gdk_gc_set_line_attributes(user_data->dlg.div_line_gc[i], 1, GDK_LINE_ON_OFF_DASH, 0, 0);
1211         gdk_gc_set_rgb_fg_color(user_data->dlg.div_line_gc[i], &color_div_line[i]);
1212         }
1213
1214         /* create gcs for the background items */
1215         for (i=0; i<MAX_NUM_COL_CONV+1; i++){
1216                 user_data->dlg.bg_gc[i]=gdk_gc_new(user_data->dlg.pixmap);
1217         gdk_gc_set_rgb_fg_color(user_data->dlg.bg_gc[i], &col[i]);
1218         }
1219
1220                 dialog_graph_redraw(user_data);
1221
1222         return TRUE;
1223 }
1224
1225 /****************************************************************************/
1226 static gint configure_event_comments(GtkWidget *widget, GdkEventConfigure *event _U_)
1227 {
1228         graph_analysis_data_t *user_data;
1229
1230         user_data=(graph_analysis_data_t *)g_object_get_data(G_OBJECT(widget), "graph_analysis_data_t");
1231
1232         if(!user_data){
1233                 exit(10);
1234         }
1235
1236         if(user_data->dlg.pixmap_comments){
1237                 gdk_pixmap_unref(user_data->dlg.pixmap_comments);
1238                 user_data->dlg.pixmap_comments=NULL;
1239         }
1240
1241         user_data->dlg.pixmap_comments=gdk_pixmap_new(widget->window,
1242                                                 widget->allocation.width,
1243                         widget->allocation.height,
1244                         -1);
1245
1246         if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap) )
1247                 gdk_draw_rectangle(user_data->dlg.pixmap_comments,
1248                                                    widget->style->white_gc,
1249                                                    TRUE,
1250                                                    0, 0,
1251                                                    widget->allocation.width,
1252                                                    widget->allocation.height);
1253
1254         dialog_graph_redraw(user_data);
1255         return TRUE;
1256 }
1257
1258 /****************************************************************************/
1259 static gint configure_event_time(GtkWidget *widget, GdkEventConfigure *event _U_)
1260 {
1261         graph_analysis_data_t *user_data;
1262
1263         user_data=(graph_analysis_data_t *)g_object_get_data(G_OBJECT(widget), "graph_analysis_data_t");
1264
1265         if(!user_data){
1266                 exit(10);
1267         }
1268
1269         if(user_data->dlg.pixmap_time){
1270                 gdk_pixmap_unref(user_data->dlg.pixmap_time);
1271                 user_data->dlg.pixmap_time=NULL;
1272         }
1273
1274         user_data->dlg.pixmap_time=gdk_pixmap_new(widget->window,
1275                                                 widget->allocation.width,
1276                         widget->allocation.height,
1277                         -1);
1278
1279         if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap_time) )
1280                 gdk_draw_rectangle(user_data->dlg.pixmap_time,
1281                                                    widget->style->white_gc,
1282                                                    TRUE,
1283                                                    0, 0,
1284                                                    widget->allocation.width,
1285                                                    widget->allocation.height);
1286
1287         dialog_graph_redraw(user_data);
1288
1289
1290         return TRUE;
1291 }
1292 /****************************************************************************/
1293 static gint pane_callback(GtkWidget *widget, GParamSpec *pspec _U_, gpointer data)
1294 {
1295         graph_analysis_data_t *user_data=(graph_analysis_data_t *)data;
1296
1297         if(!user_data){
1298                 exit(10);
1299         }
1300         if (gtk_paned_get_position(GTK_PANED(user_data->dlg.hpane)) > user_data->dlg.pixmap_width)
1301                 gtk_paned_set_position(GTK_PANED(user_data->dlg.hpane), user_data->dlg.pixmap_width);
1302         else if (gtk_paned_get_position(GTK_PANED(user_data->dlg.hpane)) < NODE_WIDTH*2)
1303                 gtk_paned_set_position(GTK_PANED(user_data->dlg.hpane), NODE_WIDTH*2);
1304         /* repaint the comment area because when moving the pane position thre are times that the expose_event_comments is not called */
1305         if (GDK_IS_DRAWABLE(user_data->dlg.draw_area_comments->window) )
1306                 gdk_draw_pixmap(user_data->dlg.draw_area_comments->window,
1307                 user_data->dlg.draw_area_comments->style->fg_gc[GTK_WIDGET_STATE(widget)],
1308                 user_data->dlg.pixmap_comments,
1309                                         0,0,
1310                                         0,0,
1311                                         user_data->dlg.draw_area_comments->allocation.width,
1312                 user_data->dlg.draw_area_comments->allocation.height);
1313         return TRUE;
1314 }
1315
1316 /****************************************************************************/
1317 static gint v_scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1318 {
1319         graph_analysis_data_t *user_data=(graph_analysis_data_t *)data;
1320         if ((user_data->dlg.first_item+user_data->dlg.v_scrollbar_adjustment->page_size+1 == user_data->num_items)
1321                 && (user_data->dlg.v_scrollbar_adjustment->value >= user_data->dlg.first_item ))
1322                 return TRUE;
1323
1324         if (user_data->dlg.first_item == user_data->dlg.v_scrollbar_adjustment->value)
1325                 return TRUE;
1326
1327         user_data->dlg.first_item = (guint32) user_data->dlg.v_scrollbar_adjustment->value;
1328
1329         dialog_graph_redraw(user_data);
1330         return TRUE;
1331 }
1332
1333 /****************************************************************************/
1334 static void create_draw_area(graph_analysis_data_t* user_data, GtkWidget *box)
1335 {
1336         GtkWidget *vbox;
1337         GtkWidget *hbox;
1338         GtkWidget *viewport;
1339         GtkWidget *scroll_window_comments;
1340         GtkWidget *viewport_comments;
1341
1342         hbox=gtk_hbox_new(FALSE, 0);
1343         gtk_widget_show(hbox);
1344
1345         vbox=gtk_vbox_new(FALSE, 0);
1346         gtk_widget_show(vbox);
1347
1348         /* create "time" draw area */
1349         user_data->dlg.draw_area_time=gtk_drawing_area_new();
1350         gtk_widget_set_size_request(user_data->dlg.draw_area_time, TIME_WIDTH, user_data->dlg.pixmap_height);
1351         g_object_set_data(G_OBJECT(user_data->dlg.draw_area_time), "graph_analysis_data_t", user_data);
1352
1353         /* create "comments" draw area */
1354         user_data->dlg.draw_area_comments=gtk_drawing_area_new();
1355         gtk_widget_set_size_request(user_data->dlg.draw_area_comments, COMMENT_WIDTH, user_data->dlg.pixmap_height);
1356         scroll_window_comments=gtk_scrolled_window_new(NULL, NULL);
1357         gtk_widget_set_size_request(scroll_window_comments, COMMENT_WIDTH/2, user_data->dlg.pixmap_height);
1358         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll_window_comments), GTK_POLICY_ALWAYS, GTK_POLICY_NEVER);
1359         viewport_comments = gtk_viewport_new(gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(scroll_window_comments)), gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scroll_window_comments)));
1360         gtk_container_add(GTK_CONTAINER(viewport_comments), user_data->dlg.draw_area_comments);
1361         gtk_container_add(GTK_CONTAINER(scroll_window_comments), viewport_comments);
1362         gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport_comments), GTK_SHADOW_NONE);
1363         g_object_set_data(G_OBJECT(user_data->dlg.draw_area_comments), "graph_analysis_data_t", user_data);
1364         gtk_widget_add_events (user_data->dlg.draw_area_comments, GDK_BUTTON_PRESS_MASK);
1365
1366         g_signal_connect(user_data->dlg.draw_area_comments, "scroll_event",  G_CALLBACK(scroll_event), user_data);
1367         /* create main Graph draw area */
1368         user_data->dlg.draw_area=gtk_drawing_area_new();
1369         if (user_data->num_nodes < 2)
1370                 user_data->dlg.pixmap_width = 2 * NODE_WIDTH;
1371         else
1372                 user_data->dlg.pixmap_width = user_data->num_nodes * NODE_WIDTH;
1373         gtk_widget_set_size_request(user_data->dlg.draw_area, user_data->dlg.pixmap_width, user_data->dlg.pixmap_height);
1374         user_data->dlg.scroll_window=gtk_scrolled_window_new(NULL, NULL);
1375         if ( user_data->num_nodes < 6)
1376                 gtk_widget_set_size_request(user_data->dlg.scroll_window, NODE_WIDTH*user_data->num_nodes, user_data->dlg.pixmap_height);
1377         else
1378                 gtk_widget_set_size_request(user_data->dlg.scroll_window, NODE_WIDTH*5, user_data->dlg.pixmap_height);
1379         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(user_data->dlg.scroll_window), GTK_POLICY_ALWAYS, GTK_POLICY_NEVER);
1380         viewport = gtk_viewport_new(gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(user_data->dlg.scroll_window)), gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(user_data->dlg.scroll_window)));
1381         gtk_container_add(GTK_CONTAINER(viewport), user_data->dlg.draw_area);
1382         gtk_container_add(GTK_CONTAINER(user_data->dlg.scroll_window), viewport);
1383         gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1384         g_object_set_data(G_OBJECT(user_data->dlg.draw_area), "graph_analysis_data_t", user_data);
1385         GTK_WIDGET_SET_FLAGS(user_data->dlg.draw_area, GTK_CAN_FOCUS);
1386         gtk_widget_grab_focus(user_data->dlg.draw_area);
1387
1388         /* signals needed to handle backing pixmap */
1389         g_signal_connect(user_data->dlg.draw_area, "expose_event", G_CALLBACK(expose_event), NULL);
1390         g_signal_connect(user_data->dlg.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1391
1392         /* signals needed to handle backing pixmap comments*/
1393         g_signal_connect(user_data->dlg.draw_area_comments, "expose_event", G_CALLBACK(expose_event_comments), NULL);
1394         g_signal_connect(user_data->dlg.draw_area_comments, "configure_event", G_CALLBACK(configure_event_comments), user_data);
1395
1396         /* signals needed to handle backing pixmap time*/
1397         g_signal_connect(user_data->dlg.draw_area_time, "expose_event", G_CALLBACK(expose_event_time), NULL);
1398         g_signal_connect(user_data->dlg.draw_area_time, "configure_event", G_CALLBACK(configure_event_time), user_data);
1399
1400         gtk_widget_add_events (user_data->dlg.draw_area, GDK_BUTTON_PRESS_MASK);
1401         g_signal_connect(user_data->dlg.draw_area, "button_press_event", G_CALLBACK(button_press_event), user_data);
1402         g_signal_connect(user_data->dlg.draw_area, "scroll_event",  G_CALLBACK(scroll_event), user_data);
1403         g_signal_connect(user_data->dlg.draw_area, "key_press_event",  G_CALLBACK(key_press_event), user_data);
1404
1405         gtk_widget_show(user_data->dlg.draw_area_time);
1406         gtk_widget_show(user_data->dlg.draw_area);
1407         gtk_widget_show(viewport);
1408         gtk_widget_show(user_data->dlg.draw_area_comments);
1409         gtk_widget_show(viewport_comments);
1410
1411         gtk_widget_show(user_data->dlg.scroll_window);
1412         gtk_widget_show(scroll_window_comments);
1413
1414         gtk_box_pack_start(GTK_BOX(hbox), user_data->dlg.draw_area_time, FALSE, FALSE, 0);
1415
1416                 user_data->dlg.hpane = gtk_hpaned_new();
1417                 gtk_paned_pack1(GTK_PANED (user_data->dlg.hpane), user_data->dlg.scroll_window, FALSE, TRUE);
1418                 gtk_paned_pack2(GTK_PANED (user_data->dlg.hpane), scroll_window_comments, TRUE, TRUE);
1419         g_signal_connect(user_data->dlg.hpane, "notify::position",  G_CALLBACK(pane_callback), user_data);
1420         gtk_widget_show(user_data->dlg.hpane);
1421
1422         gtk_box_pack_start(GTK_BOX(hbox), user_data->dlg.hpane, TRUE, TRUE, 0);
1423
1424        /* create the associated v_scrollbar */
1425         user_data->dlg.v_scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1426         user_data->dlg.v_scrollbar=gtk_vscrollbar_new(user_data->dlg.v_scrollbar_adjustment);
1427         gtk_widget_show(user_data->dlg.v_scrollbar);
1428         gtk_box_pack_end(GTK_BOX(hbox), user_data->dlg.v_scrollbar, FALSE, FALSE, 0);
1429         g_signal_connect(user_data->dlg.v_scrollbar_adjustment, "value_changed", G_CALLBACK(v_scrollbar_changed), user_data);
1430
1431
1432         gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 0);
1433 }
1434 /****************************************************************************/
1435 /* PUBLIC */
1436 /****************************************************************************/
1437
1438
1439 /****************************************************************************/
1440 static void dialog_graph_create_window(graph_analysis_data_t* user_data)
1441 {
1442         GtkWidget *vbox;
1443         GtkWidget *hbuttonbox;
1444         GtkWidget *bt_close;
1445         GtkWidget *bt_save;
1446         GtkTooltips *tooltips = gtk_tooltips_new();
1447         const gchar *title_name_ptr;
1448         gchar   *win_name;
1449
1450         title_name_ptr = cf_get_display_name(&cfile);
1451         win_name = g_strdup_printf("%s - Graph Analysis", title_name_ptr);
1452
1453         /* create the main window */
1454                 if (user_data->dlg.title)
1455                 user_data->dlg.window=window_new(GTK_WINDOW_TOPLEVEL, user_data->dlg.title);
1456                 else
1457                 user_data->dlg.window=window_new(GTK_WINDOW_TOPLEVEL, win_name);
1458
1459
1460         vbox=gtk_vbox_new(FALSE, 0);
1461         gtk_container_add(GTK_CONTAINER(user_data->dlg.window), vbox);
1462         gtk_widget_show(vbox);
1463
1464         create_draw_area(user_data, vbox);
1465
1466         /* button row */
1467         hbuttonbox = gtk_hbutton_box_new ();
1468         gtk_box_pack_start (GTK_BOX (vbox), hbuttonbox, FALSE, FALSE, 0);
1469         gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
1470         gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbuttonbox), 30);
1471         gtk_widget_show(hbuttonbox);
1472
1473         bt_save = gtk_button_new_from_stock(GTK_STOCK_SAVE_AS);
1474         gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_save);
1475         gtk_widget_show(bt_save);
1476         g_signal_connect(bt_save, "clicked", G_CALLBACK(on_save_bt_clicked), user_data);
1477         gtk_tooltips_set_tip (tooltips, bt_save, "Save an ASCII representation of the graph to a file", NULL);
1478
1479         bt_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
1480         gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_close);
1481         GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
1482         gtk_widget_show(bt_close);
1483         gtk_tooltips_set_tip (tooltips, bt_close, "Close this dialog", NULL);
1484         window_set_cancel_button(user_data->dlg.window, bt_close, window_cancel_button_cb);
1485
1486         g_signal_connect(user_data->dlg.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1487         g_signal_connect(user_data->dlg.window, "destroy", G_CALLBACK(on_destroy), user_data);
1488
1489         gtk_widget_show(user_data->dlg.window);
1490         window_present(user_data->dlg.window);
1491
1492         /* Destroy our graph window with our parent if the caller specified the parent */
1493         if(user_data->dlg.parent_w) {
1494                 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.window),
1495                                              GTK_WINDOW(user_data->dlg.parent_w));
1496                 /* Destruction of this child window */
1497                 gtk_window_set_destroy_with_parent(GTK_WINDOW(user_data->dlg.window), TRUE);
1498         }
1499         g_free(win_name);
1500 }
1501
1502 /* Return the index array if the node is in the array. Return -1 if there is room in the array
1503  * and Return -2 if the array is full
1504  */
1505 /****************************************************************************/
1506 static gint add_or_get_node(graph_analysis_data_t* user_data, address* node) {
1507         guint i;
1508
1509         if (node->type == AT_NONE) return NODE_OVERFLOW;
1510
1511         for (i=0; i<MAX_NUM_NODES && i < user_data->num_nodes ; i++){
1512                 if ( CMP_ADDRESS(&(user_data->nodes[i]), node) == 0 ) return i; /* it is in the array */
1513         }
1514
1515         if (i == MAX_NUM_NODES) {
1516                 return  NODE_OVERFLOW;
1517         } else {
1518                 user_data->num_nodes++;
1519                 COPY_ADDRESS(&(user_data->nodes[i]),node);
1520                 return i;
1521         }
1522 }
1523
1524 /* Get the nodes from the list */
1525 /****************************************************************************/
1526 static void get_nodes(graph_analysis_data_t* user_data)
1527 {
1528         GList* list;
1529         graph_analysis_item_t *gai;
1530
1531         /* fill the node array */
1532         list = g_list_first(user_data->graph_info->list);
1533         while (list)
1534         {
1535                 gai = list->data;
1536                 if (gai->display) {
1537                         user_data->num_items++;
1538                         if (!user_data->dlg.inverse) {
1539                                 gai->src_node = (guint16)add_or_get_node(user_data, &(gai->src_addr));
1540                                 gai->dst_node = (guint16)add_or_get_node(user_data, &(gai->dst_addr));
1541                         } else {
1542                                 gai->dst_node = (guint16)add_or_get_node(user_data, &(gai->src_addr));
1543                                 gai->src_node = (guint16)add_or_get_node(user_data, &(gai->dst_addr));
1544                         }
1545                 }
1546                 list = g_list_next(list);
1547         }
1548 }
1549
1550 /****************************************************************************/
1551 graph_analysis_data_t* graph_analysis_init(void)
1552 {
1553         graph_analysis_data_t* user_data;
1554         /* init */
1555         user_data = g_malloc(sizeof(graph_analysis_data_t));
1556
1557         /* init user_data */
1558         graph_analysis_init_dlg(user_data);
1559
1560         return user_data;
1561 }
1562 /****************************************************************************/
1563 /* PUBLIC */
1564 /****************************************************************************/
1565
1566 /****************************************************************************/
1567 void graph_analysis_create(graph_analysis_data_t* user_data)
1568 {
1569         /* reset the data */
1570         graph_analysis_reset(user_data);
1571
1572         /* get nodes (each node is an address) */
1573         get_nodes(user_data);
1574
1575         /* create the graph windows */
1576         dialog_graph_create_window(user_data);
1577
1578         /* redraw the graph */
1579         dialog_graph_redraw(user_data);
1580
1581         return;
1582 }
1583
1584 /****************************************************************************/
1585 void graph_analysis_update(graph_analysis_data_t* user_data)
1586 {
1587         /* reset the data */
1588         graph_analysis_reset(user_data);
1589
1590         /* get nodes (each node is an address) */
1591         get_nodes(user_data);
1592
1593         user_data->dlg.pixmap_width = user_data->num_nodes * NODE_WIDTH;
1594         gtk_widget_set_size_request(user_data->dlg.draw_area, user_data->dlg.pixmap_width, user_data->dlg.pixmap_height);
1595         if ( user_data->num_nodes < 6)
1596                 gtk_widget_set_size_request(user_data->dlg.scroll_window, NODE_WIDTH*user_data->num_nodes, user_data->dlg.pixmap_height);
1597         else
1598                 gtk_widget_set_size_request(user_data->dlg.scroll_window, NODE_WIDTH*5, user_data->dlg.pixmap_height);
1599
1600         /* redraw the graph */
1601         dialog_graph_redraw(user_data);
1602
1603         window_present(user_data->dlg.window);
1604         return;
1605 }
1606
1607
1608 /****************************************************************************/
1609 void graph_analysis_redraw(graph_analysis_data_t* user_data)
1610 {
1611         /* get nodes (each node is an address) */
1612         get_nodes(user_data);
1613
1614         user_data->dlg.pixmap_width = user_data->num_nodes * NODE_WIDTH;
1615     gtk_widget_set_size_request(user_data->dlg.draw_area, user_data->dlg.pixmap_width, user_data->dlg.pixmap_height);
1616         if ( user_data->num_nodes < 6)
1617                         gtk_widget_set_size_request(user_data->dlg.scroll_window, NODE_WIDTH*user_data->num_nodes, user_data->dlg.pixmap_height);
1618                 else
1619                         gtk_widget_set_size_request(user_data->dlg.scroll_window, NODE_WIDTH*5, user_data->dlg.pixmap_height);
1620
1621
1622         /* redraw the graph */
1623         dialog_graph_redraw(user_data);
1624
1625     window_present(user_data->dlg.window);
1626         return;
1627 }