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