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