Trivial reformatting
[obnox/wireshark/wip.git] / gtk / flow_graph.c
1 /* flow_graph.c
2  * $Id$
3  * Allows to display a flow graph of the currently displayed packets
4  *
5  * Copyright 2004, Ericsson , Spain
6  * By Francisco Alcoba <francisco.alcoba@ericsson.com>
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #  include <config.h>
29 #endif
30 #include <string.h>
31
32 #include <gtk/gtk.h>
33
34 #include <epan/epan.h>
35 #include <epan/packet.h>
36 #include <epan/filesystem.h>
37 #include <epan/stat_cmd_args.h>
38 #include <epan/to_str.h>
39 #include <epan/tap.h>
40 #include <epan/dissectors/packet-tcp.h>
41 #include <epan/strutil.h>
42
43 #include "../register.h"
44 #include "../globals.h"
45 #include "../stat_menu.h"
46 #include "../simple_dialog.h"
47
48 #include "gtk/graph_analysis.h"
49 #include "gtk/gui_stat_menu.h"
50 #include "gtk/dlg_utils.h"
51 #include "gtk/gui_utils.h"
52 #include "gtk/stock_icons.h"
53 #include "gtk/gtkglobals.h"
54 #include "gtk/main.h"
55
56
57
58 #define DISPLAYED 0
59 #define ALL 1
60 #define GENERAL 0
61 #define TCP 1
62 #define SRCDST 0
63 #define NET_SRCDST 1
64
65 static int type_of_packets = DISPLAYED;
66 static int type_of_flow = GENERAL;
67 static int node_addr_type = SRCDST;
68
69 static int tap_identifier;
70 static gboolean have_frame_tap_listener=FALSE;
71 static gboolean have_tcp_tap_listener=FALSE;
72 static graph_analysis_info_t *graph_analysis = NULL;
73 static graph_analysis_data_t *graph_analysis_data;
74
75 static GtkWidget *flow_graph_dlg = NULL;
76
77 static GtkWidget *select_all_rb;
78 static GtkWidget *select_displayed_rb;
79 static GtkWidget *select_general_rb;
80 static GtkWidget *select_tcp_rb;
81 static GtkWidget *src_dst_rb;
82 static GtkWidget *net_src_dst_rb;
83
84 void flow_graph_data_init(void);
85
86
87 /****************************************************************************/
88 /* free up memory and initialize the pointers */
89
90 static void flow_graph_reset(void *ptr _U_)
91 {
92
93         graph_analysis_item_t *graph_item;
94
95         GList* list;
96
97         if (graph_analysis !=NULL){
98
99                 /* free the graph data items */
100                 list = g_list_first(graph_analysis->list);
101                 while (list)
102                 {
103                         graph_item = list->data;
104                         g_free(graph_item->frame_label);
105                         g_free(graph_item->comment);
106                         g_free(list->data);
107                         list = g_list_next(list);
108                 }
109                 g_list_free(graph_analysis->list);
110                 graph_analysis->nconv = 0;
111                 graph_analysis->list = NULL;
112         }
113         return;
114 }
115
116 /****************************************************************************/
117 void flow_graph_data_init(void) {
118         graph_analysis = g_malloc(sizeof(graph_analysis_info_t));
119         graph_analysis->nconv = 0;
120         graph_analysis->list = NULL;
121         return;
122 }
123
124
125 /****************************************************************************/
126 static void
127 remove_tap_listener_flow_graph(void)
128 {
129         protect_thread_critical_region();
130         remove_tap_listener(&(tap_identifier));
131         unprotect_thread_critical_region();
132
133         have_frame_tap_listener=FALSE;
134         have_tcp_tap_listener=FALSE;
135 }
136
137
138 /****************************************************************************/
139 /* CALLBACKS                                                                */
140 /****************************************************************************/
141 static void
142 flow_graph_on_destroy(GtkObject *object _U_, gpointer user_data _U_)
143 {
144         /* remove_tap_listeners */
145         remove_tap_listener_flow_graph();
146
147         /* Clean up memory used by tap */
148         flow_graph_reset(NULL);
149
150         /* Note that we no longer have a "Flow Graph" dialog box. */
151         flow_graph_dlg = NULL;
152 }
153
154
155 /****************************************************************************/
156 static void
157 toggle_select_all(GtkWidget *widget _U_, gpointer user_data _U_)
158 {
159   /* is the button now active? */
160         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_all_rb))) {
161                 type_of_packets = ALL;
162         }
163 }
164
165 /****************************************************************************/
166 static void
167 toggle_select_displayed(GtkWidget *widget _U_, gpointer user_data _U_)
168 {
169   /* is the button now active? */
170         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_displayed_rb))) {
171                 type_of_packets = DISPLAYED;
172         }
173 }
174
175 /****************************************************************************/
176 static void
177 toggle_select_general(GtkWidget *widget _U_, gpointer user_data _U_)
178 {
179   /* is the button now active? */
180         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_general_rb))) {
181                 type_of_flow = GENERAL;
182         }
183 }
184
185 /****************************************************************************/
186 static void
187 toggle_select_tcp(GtkWidget *widget _U_, gpointer user_data _U_)
188 {
189   /* is the button now active? */
190         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_tcp_rb))) {
191                 type_of_flow = TCP;
192         }
193 }
194
195 /****************************************************************************/
196 static void
197 toggle_select_srcdst(GtkWidget *widget _U_, gpointer user_data _U_)
198 {
199   /* is the button now active? */
200         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(src_dst_rb))) {
201                 node_addr_type = SRCDST;
202         }
203 }
204
205 /****************************************************************************/
206 static void
207 toggle_select_netsrcdst(GtkWidget *widget _U_, gpointer user_data _U_)
208 {
209   /* is the button now active? */
210         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(net_src_dst_rb))) {
211                 node_addr_type = NET_SRCDST;
212         }
213 }
214
215 /****************************************************************************/
216 /* Add a new frame into the graph */
217 static int flow_graph_frame_add_to_graph(packet_info *pinfo)
218 {
219         graph_analysis_item_t *gai;
220         int i;
221         gchar *protocol;
222         gchar *colinfo;
223
224         protocol=NULL;
225         colinfo=NULL;
226
227         if (node_addr_type == NET_SRCDST) {
228                 if (pinfo->net_src.type!=AT_NONE && pinfo->net_dst.type!=AT_NONE) {
229                         gai = g_malloc(sizeof(graph_analysis_item_t));
230                         COPY_ADDRESS(&(gai->src_addr),&(pinfo->net_src));
231                         COPY_ADDRESS(&(gai->dst_addr),&(pinfo->net_dst));
232                 }
233                 else return 0;
234
235         } else {
236                 if (pinfo->src.type!=AT_NONE && pinfo->dst.type!=AT_NONE) {
237                         gai = g_malloc(sizeof(graph_analysis_item_t));
238                         COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
239                         COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
240                 }
241                 else return 0;
242         }
243
244         gai->frame_num = pinfo->fd->num;
245         gai->time= nstime_to_sec(&pinfo->fd->rel_ts);
246
247         gai->port_src=pinfo->srcport;
248         gai->port_dst=pinfo->destport;
249         gai->comment=NULL;
250         gai->frame_label=NULL;
251
252         /* this code doesn't make sense.
253         g_free(gai->comment);
254         g_free(gai->frame_label);
255         */
256
257         if(pinfo->cinfo) {
258                 if (pinfo->cinfo->col_first[COL_INFO]>=0){
259
260                         for (i = pinfo->cinfo->col_first[COL_INFO]; i <= pinfo->cinfo->col_last[COL_INFO]; i++) {
261                                 if (pinfo->cinfo->fmt_matx[i][COL_INFO]) {
262                                         colinfo = g_strdup(pinfo->cinfo->col_data[i]);
263                                         /* break; ? or g_free(colinfo); before g_strdup() */
264                                 }
265                         }
266                 }
267
268                 if (pinfo->cinfo->col_first[COL_PROTOCOL]>=0){
269
270                         for (i = pinfo->cinfo->col_first[COL_PROTOCOL]; i <= pinfo->cinfo->col_last[COL_PROTOCOL]; i++) {
271                                 if (pinfo->cinfo->fmt_matx[i][COL_PROTOCOL]) {
272                                         protocol = g_strdup(pinfo->cinfo->col_data[i]);
273                                         /* break; ? or g_free(protocol); before g_strdup() */
274                                 }
275                         }
276                 }
277         }
278
279         if (colinfo != NULL) {
280                 if (protocol != NULL) {
281                         gai->frame_label = g_strdup_printf("%.19s", colinfo);
282                         gai->comment = g_strdup_printf("%s: %s", protocol, colinfo);
283                 } else {
284                         gai->frame_label = g_strdup_printf("%.19s", colinfo);
285                         gai->comment = g_strdup_printf("%s", colinfo);
286                 }
287         } else {
288                 /* This will probably never happen...*/
289                 if (protocol != NULL) {
290                         gai->frame_label = g_strdup_printf("%.19s", protocol);
291                         gai->comment = g_strdup_printf("%s", protocol);
292                 }
293         }
294
295         g_free(protocol);
296         g_free(colinfo);
297
298         gai->line_style=1;
299         gai->conv_num=0;
300         gai->display=TRUE;
301
302         graph_analysis->list = g_list_append(graph_analysis->list, gai);
303
304         return 1;
305
306 }
307
308 /****************************************************************************/
309 /* Add a new tcp frame into the graph */
310 static int flow_graph_tcp_add_to_graph(packet_info *pinfo, const struct tcpheader *tcph)
311 {
312   graph_analysis_item_t *gai;
313   /* copied from packet-tcp */
314   const gchar *fstr[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECN", "CWR" };
315   guint i, bpos;
316   gboolean flags_found = FALSE;
317   gchar flags[64];
318
319   gai = g_malloc(sizeof(graph_analysis_item_t));
320   gai->frame_num = pinfo->fd->num;
321   gai->time= nstime_to_sec(&pinfo->fd->rel_ts);
322   if (node_addr_type == NET_SRCDST) {
323     COPY_ADDRESS(&(gai->src_addr),&(pinfo->net_src));
324     COPY_ADDRESS(&(gai->dst_addr),&(pinfo->net_dst));
325   } else {
326     COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
327     COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
328   }
329   gai->port_src=pinfo->srcport;
330   gai->port_dst=pinfo->destport;
331
332   flags[0] = '\0';
333   for (i = 0; i < 8; i++) {
334     bpos = 1 << i;
335     if (tcph->th_flags & bpos) {
336       if (flags_found) {
337         g_strlcat(flags, ", ", sizeof(flags));
338       }
339       g_strlcat(flags, fstr[i], sizeof(flags));
340       flags_found = TRUE;
341     }
342   }
343   if (flags[0] == '\0') {
344     g_snprintf (flags, sizeof(flags), "<None>");
345   }
346
347   if ((tcph->th_have_seglen)&&(tcph->th_seglen!=0)){
348     gai->frame_label = g_strdup_printf("%s - Len: %u",flags, tcph->th_seglen);
349   }
350   else{
351     gai->frame_label = g_strdup(flags);
352   }
353
354   gai->comment = g_strdup_printf("Seq = %u Ack = %u",tcph->th_seq, tcph->th_ack);
355
356   gai->line_style=1;
357   gai->conv_num=0;
358   gai->display=TRUE;
359
360   graph_analysis->list = g_list_append(graph_analysis->list, gai);
361
362   return 1;
363
364 }
365
366
367
368 /****************************************************************************/
369 /* whenever a frame packet is seen by the tap listener */
370 static int
371 flow_graph_frame_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *dummy _U_)
372 {
373
374         if ((type_of_packets == ALL)||(pinfo->fd->flags.passed_dfilter==1)){
375                 flow_graph_frame_add_to_graph(pinfo);
376         }
377
378         return 1;
379 }
380
381 /****************************************************************************/
382 /* whenever a TCP packet is seen by the tap listener */
383 static int
384 flow_graph_tcp_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *tcp_info)
385 {
386         const struct tcpheader *tcph = tcp_info;
387
388         if ((type_of_packets == ALL)||(pinfo->fd->flags.passed_dfilter==1)){
389                 flow_graph_tcp_add_to_graph(pinfo,tcph);
390         }
391
392         return 1;
393 }
394
395
396 static void flow_graph_packet_draw(void *prs _U_)
397 {
398         return;
399 }
400
401 /****************************************************************************/
402 static void
403 flow_graph_on_ok                    (GtkButton       *button _U_,
404                                         gpointer         user_data)
405 {
406
407         if ((have_frame_tap_listener==TRUE)
408                 ||(have_tcp_tap_listener==TRUE))
409         {
410                 /* remove_tap_listeners */
411                 remove_tap_listener_flow_graph();
412         }
413
414         /* Scan for displayed packets (retap all packets) */
415
416         if (type_of_flow == GENERAL){
417                 /* Register the tap listener */
418
419                 if(have_frame_tap_listener==FALSE)
420                 {
421                         /* don't register tap listener, if we have it already */
422                         register_tap_listener("frame", &tap_identifier, NULL,
423                                 TL_REQUIRES_COLUMNS,
424                                 flow_graph_reset,
425                                 flow_graph_frame_packet,
426                                 flow_graph_packet_draw
427                                 );
428                         have_frame_tap_listener=TRUE;
429                 }
430
431                 cf_retap_packets(&cfile);
432         }
433         else if (type_of_flow == TCP){
434         /* Register the tap listener */
435
436                 if(have_tcp_tap_listener==FALSE)
437                 {
438                         /* don't register tap listener, if we have it already */
439                         register_tap_listener("tcp", &tap_identifier, NULL,
440                                 0,
441                                 flow_graph_reset,
442                                 flow_graph_tcp_packet,
443                                 flow_graph_packet_draw
444                                 );
445                         have_tcp_tap_listener=TRUE;
446                 }
447
448                 cf_retap_packets(&cfile);
449         }
450
451         if (graph_analysis_data->dlg.window != NULL){ /* if we still have a window */
452                 graph_analysis_update(graph_analysis_data);             /* refresh it xxx */
453         }
454         else{
455                 graph_analysis_data->dlg.parent_w = user_data;
456                 graph_analysis_create(graph_analysis_data);
457         }
458
459 }
460
461
462 /****************************************************************************/
463 /* INTERFACE                                                                */
464 /****************************************************************************/
465
466 static void flow_graph_dlg_create (void)
467 {
468
469         GtkWidget *flow_graph_dlg_w;
470         GtkWidget *main_vb;
471         GtkWidget *hbuttonbox;
472         GtkWidget *bt_cancel, *bt_ok;
473 #if 0
474         GtkWidget *top_label = NULL;
475 #endif
476         GtkWidget *flow_type_fr, *range_fr, *range_tb, *flow_type_tb, *node_addr_fr, *node_addr_tb;
477
478
479         GtkTooltips *tooltips = gtk_tooltips_new();
480
481         flow_graph_dlg_w = dlg_window_new("Wireshark: Flow Graph");  /* transient_for top_level */
482         gtk_window_set_destroy_with_parent (GTK_WINDOW(flow_graph_dlg_w), TRUE);
483
484         gtk_window_set_default_size(GTK_WINDOW(flow_graph_dlg_w), 250, 150);
485
486         main_vb = gtk_vbox_new (FALSE, 0);
487         gtk_container_add(GTK_CONTAINER(flow_graph_dlg_w), main_vb);
488         gtk_container_set_border_width (GTK_CONTAINER (main_vb), 7);
489
490 #if 0
491         top_label = gtk_label_new ("Choose packets to include in the graph");
492         gtk_box_pack_start (GTK_BOX (main_vb), top_label, FALSE, FALSE, 8);
493 #endif
494
495         gtk_widget_show(flow_graph_dlg_w);
496
497         /*** Packet range frame ***/
498         range_fr = gtk_frame_new("Choose packets");
499         gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 5);
500
501         range_tb = gtk_table_new(4, 4, FALSE);
502         gtk_container_set_border_width(GTK_CONTAINER(range_tb), 5);
503         gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
504
505         /* Process all packets */
506         select_all_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_All packets");
507         gtk_tooltips_set_tip (tooltips, select_all_rb,
508                 ("Process all packets"), NULL);
509         g_signal_connect(select_all_rb, "toggled", G_CALLBACK(toggle_select_all), NULL);
510         gtk_table_attach_defaults(GTK_TABLE(range_tb), select_all_rb, 0, 1, 0, 1);
511         if (type_of_packets == ALL) {
512                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_all_rb),TRUE);
513         }
514         gtk_widget_show(select_all_rb);
515
516         /* Process displayed packets */
517         select_displayed_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(select_all_rb), "_Displayed packets");
518         gtk_tooltips_set_tip (tooltips, select_displayed_rb,
519                 ("Process displayed packets"), NULL);
520         g_signal_connect(select_displayed_rb, "toggled", G_CALLBACK(toggle_select_displayed), NULL);
521         gtk_table_attach_defaults(GTK_TABLE(range_tb), select_displayed_rb, 0, 1, 1, 2);
522         if (type_of_packets == DISPLAYED) {
523                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_displayed_rb),TRUE);
524         }
525         gtk_widget_show(select_displayed_rb);
526
527         gtk_widget_show(range_tb);
528         gtk_widget_show(range_fr);
529
530         /*** Flow type frame ***/
531         flow_type_fr = gtk_frame_new("Choose flow type");
532         gtk_box_pack_start(GTK_BOX(main_vb), flow_type_fr, FALSE, FALSE, 5);
533
534         flow_type_tb = gtk_table_new(4, 4, FALSE);
535         gtk_container_set_border_width(GTK_CONTAINER(flow_type_tb), 5);
536         gtk_container_add(GTK_CONTAINER(flow_type_fr), flow_type_tb);
537
538         /* General information */
539         select_general_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_General flow");
540         gtk_tooltips_set_tip (tooltips, select_general_rb,
541                 ("Show all packets, with general information"), NULL);
542         g_signal_connect(select_general_rb, "toggled", G_CALLBACK(toggle_select_general), NULL);
543         gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_general_rb, 0, 1, 0, 1);
544         if (type_of_flow == GENERAL) {
545                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_general_rb),TRUE);
546         }
547         gtk_widget_show(select_general_rb);
548
549         /* TCP specific information */
550         select_tcp_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(select_general_rb), "_TCP flow");
551         gtk_tooltips_set_tip (tooltips, select_tcp_rb,
552                 ("Show only TCP packets, with TCP specific information"), NULL);
553         g_signal_connect(select_tcp_rb, "toggled", G_CALLBACK(toggle_select_tcp), NULL);
554         gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_tcp_rb, 0, 1, 1, 2);
555         if (type_of_flow == TCP) {
556                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_tcp_rb),TRUE);
557         }
558         gtk_widget_show(select_tcp_rb);
559
560         gtk_widget_show(flow_type_tb);
561         gtk_widget_show(flow_type_fr);
562
563         /*** Node address type frame ***/
564         node_addr_fr = gtk_frame_new("Choose node address type");
565         gtk_box_pack_start(GTK_BOX(main_vb), node_addr_fr, FALSE, FALSE, 5);
566
567         node_addr_tb = gtk_table_new(4, 4, FALSE);
568         gtk_container_set_border_width(GTK_CONTAINER(node_addr_tb), 5);
569         gtk_container_add(GTK_CONTAINER(node_addr_fr), node_addr_tb);
570
571         /* Source / Dest address */
572         src_dst_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_Standard source/destination addresses");
573         gtk_tooltips_set_tip (tooltips, src_dst_rb,
574                 ("Nodes in the diagram are identified with source and destination addresses"), NULL);
575         g_signal_connect(src_dst_rb, "toggled", G_CALLBACK(toggle_select_srcdst), NULL);
576         gtk_table_attach_defaults(GTK_TABLE(node_addr_tb), src_dst_rb, 0, 1, 0, 1);
577         if (node_addr_type == SRCDST) {
578                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(src_dst_rb),TRUE);
579         }
580         gtk_widget_show(src_dst_rb);
581
582         /* Network source / dest address */
583         net_src_dst_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(src_dst_rb), "_Network source/destination addresses");
584         gtk_tooltips_set_tip (tooltips, net_src_dst_rb,
585                 ("Nodes in the diagram are identified with network source and destination addresses"), NULL);
586         g_signal_connect(net_src_dst_rb, "toggled", G_CALLBACK(toggle_select_netsrcdst), NULL);
587         gtk_table_attach_defaults(GTK_TABLE(node_addr_tb), net_src_dst_rb, 0, 1, 1, 2);
588         if (node_addr_type == NET_SRCDST) {
589                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(net_src_dst_rb),TRUE);
590         }
591         gtk_widget_show(net_src_dst_rb);
592
593         gtk_widget_show(node_addr_tb);
594         gtk_widget_show(node_addr_fr);
595
596         /* button row */
597         hbuttonbox = gtk_hbutton_box_new ();
598         gtk_box_pack_start (GTK_BOX (main_vb), hbuttonbox, FALSE, FALSE, 5);
599         gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
600         gtk_box_set_spacing (GTK_BOX (hbuttonbox), 30);
601
602         bt_ok = gtk_button_new_from_stock(GTK_STOCK_OK);
603         gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_ok);
604         gtk_tooltips_set_tip (tooltips, bt_ok, "Show the flow graph", NULL);
605         g_signal_connect(bt_ok, "clicked", G_CALLBACK(flow_graph_on_ok), flow_graph_dlg_w);
606         gtk_widget_show(bt_ok);
607
608         bt_cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
609         gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_cancel);
610         GTK_WIDGET_SET_FLAGS(bt_cancel, GTK_CAN_DEFAULT);
611         gtk_tooltips_set_tip (tooltips, bt_cancel, "Cancel this dialog", NULL);
612         window_set_cancel_button(flow_graph_dlg_w, bt_cancel, window_cancel_button_cb);
613
614         g_signal_connect(flow_graph_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
615         g_signal_connect(flow_graph_dlg_w, "destroy", G_CALLBACK(flow_graph_on_destroy), NULL);
616
617         gtk_widget_show_all(flow_graph_dlg_w);
618         window_present(flow_graph_dlg_w);
619
620         flow_graph_dlg = flow_graph_dlg_w;
621 }
622
623 /****************************************************************************/
624 /* PUBLIC                                                                   */
625 /****************************************************************************/
626
627 /* init function for tap */
628 static void
629 flow_graph_init_tap(const char *dummy _U_, void* userdata _U_)
630 {
631
632         /* initialize graph items store */
633         flow_graph_data_init();
634
635         /* init the Graph Analysys */
636         graph_analysis_data = graph_analysis_init();
637         graph_analysis_data->graph_info = graph_analysis;
638
639         /* create dialog box if necessary */
640         if (flow_graph_dlg == NULL) {
641                 flow_graph_dlg_create();
642         } else {
643                 /* There's already a dialog box; reactivate it. */
644                 reactivate_window(flow_graph_dlg);
645         }
646
647 }
648
649
650 /****************************************************************************/
651 /* entry point when called via the GTK menu */
652 static void flow_graph_launch(GtkWidget *w _U_, gpointer data _U_)
653 {
654         flow_graph_init_tap("",NULL);
655 }
656
657 /****************************************************************************/
658 void
659 register_tap_listener_flow_graph(void)
660 {
661         register_stat_cmd_arg("flow_graph",flow_graph_init_tap,NULL);
662         register_stat_menu_item_stock("Flo_w Graph...",
663         REGISTER_STAT_GROUP_UNSORTED, WIRESHARK_STOCK_FLOW_GRAPH,
664             flow_graph_launch, NULL, NULL, NULL);
665
666 }
667