name change
[metze/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->col_first[COL_INFO]>=0){
260                 
261                 for (i = pinfo->cinfo->col_first[COL_INFO]; i <= pinfo->cinfo->col_last[COL_INFO]; i++) {
262                 if (pinfo->cinfo->fmt_matx[i][COL_INFO]) {
263                                 colinfo = g_strdup(pinfo->cinfo->col_data[i]);
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
274                         }
275                 }
276         }
277                 
278         if (colinfo != NULL) {
279                 if (protocol != NULL) {
280                         gai->frame_label = g_strdup_printf("%.19s", colinfo);
281                         gai->comment = g_strdup_printf("%s: %s", protocol, colinfo);
282                 } else {
283                         gai->frame_label = g_strdup_printf("%.19s", colinfo);
284                         gai->comment = g_strdup_printf("%s", colinfo);
285                 }
286         } else {
287                 /* This will probably never happen...*/
288                 if (protocol != NULL) {
289                         gai->frame_label = g_strdup_printf("%.19s", protocol);
290                         gai->comment = g_strdup_printf("%s", protocol);
291                 } else {
292                         gai->frame_label = NULL;
293                         gai->comment = NULL;
294                 }
295         }
296  
297         if (protocol!=NULL){
298                 g_free(protocol);
299         }
300         if (colinfo!=NULL){
301                 g_free(colinfo);
302         }
303
304         gai->line_style=1;
305         gai->conv_num=0;
306         gai->display=TRUE;
307
308         graph_analysis->list = g_list_append(graph_analysis->list, gai);
309
310         return 1;
311
312 }
313
314 /****************************************************************************/
315 /* Add a new tcp frame into the graph */
316 static int flow_graph_tcp_add_to_graph(packet_info *pinfo, const struct tcpheader *tcph)
317 {
318   graph_analysis_item_t *gai;
319   /* copied from packet-tcp */
320   const gchar *fstr[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECN", "CWR" };
321   guint i, bpos;
322   guint fpos = 0;
323   gchar flags[64] = "<None>";
324
325   gai = g_malloc(sizeof(graph_analysis_item_t));
326   gai->frame_num = pinfo->fd->num;
327   gai->time= nstime_to_sec(&pinfo->fd->rel_ts);
328   if (node_addr_type == NET_SRCDST) {
329     COPY_ADDRESS(&(gai->src_addr),&(pinfo->net_src));
330     COPY_ADDRESS(&(gai->dst_addr),&(pinfo->net_dst));
331   } else {
332     COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
333     COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
334   }
335   gai->port_src=pinfo->srcport;
336   gai->port_dst=pinfo->destport;
337
338   for (i = 0; i < 8; i++) {
339     bpos = 1 << i;
340     if (tcph->th_flags & bpos) {
341       if (fpos) {
342         strcpy(&flags[fpos], ", ");
343         fpos += 2;
344       }
345       strcpy(&flags[fpos], fstr[i]);
346       fpos += 3;
347     }
348   }
349   flags[fpos] = '\0';
350   if ((tcph->th_have_seglen)&&(tcph->th_seglen!=0)){
351     gai->frame_label = g_strdup_printf("%s - Len: %u",flags, tcph->th_seglen);
352   }
353   else{
354     gai->frame_label = g_strdup(flags);
355   }
356
357   gai->comment = g_strdup_printf("Seq = %u Ack = %u",tcph->th_seq, tcph->th_ack);
358
359   gai->line_style=1;
360   gai->conv_num=0;
361   gai->display=TRUE;
362
363   graph_analysis->list = g_list_append(graph_analysis->list, gai);
364
365   return 1;
366
367 }
368
369
370
371 /****************************************************************************/
372 /* whenever a frame packet is seen by the tap listener */
373 static int 
374 flow_graph_frame_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *dummy _U_)
375 {
376
377         if ((type_of_packets == ALL)||(pinfo->fd->flags.passed_dfilter==1)){
378                 flow_graph_frame_add_to_graph(pinfo);  
379         }
380         
381         return 1;
382 }
383
384 /****************************************************************************/
385 /* whenever a TCP packet is seen by the tap listener */
386 static int 
387 flow_graph_tcp_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *tcp_info)
388 {
389         const struct tcpheader *tcph = tcp_info;
390
391         if ((type_of_packets == ALL)||(pinfo->fd->flags.passed_dfilter==1)){
392                 flow_graph_tcp_add_to_graph(pinfo,tcph);  
393         }
394         
395         return 1;
396 }
397
398
399 static void flow_graph_packet_draw(void *prs _U_)
400 {
401         return; 
402 }
403
404 /****************************************************************************/
405 static void
406 flow_graph_on_ok                    (GtkButton       *button _U_,
407                                         gpointer         user_data _U_)
408 {
409
410         if ((have_frame_tap_listener==TRUE)
411                 ||(have_tcp_tap_listener==TRUE))
412         { 
413                 /* remove_tap_listeners */
414                 remove_tap_listener_flow_graph();
415         }
416         
417         /* Scan for displayed packets (retap all packets) */
418
419         if (type_of_flow == GENERAL){
420                 /* Register the tap listener */
421
422                 if(have_frame_tap_listener==FALSE)
423                 {
424                         /* don't register tap listener, if we have it already */
425                         register_tap_listener("frame", &tap_identifier, NULL,
426                                 flow_graph_reset, 
427                                 flow_graph_frame_packet, 
428                                 flow_graph_packet_draw
429                                 );
430                         have_frame_tap_listener=TRUE;
431                 }
432
433                 cf_retap_packets(&cfile, TRUE);
434         }
435         else if (type_of_flow == TCP){
436         /* Register the tap listener */
437
438                 if(have_tcp_tap_listener==FALSE)
439                 {
440                         /* don't register tap listener, if we have it already */
441                         register_tap_listener("tcp", &tap_identifier, NULL,
442                                 flow_graph_reset, 
443                                 flow_graph_tcp_packet, 
444                                 flow_graph_packet_draw
445                                 );
446                         have_tcp_tap_listener=TRUE;
447                 }
448
449                 cf_retap_packets(&cfile, FALSE);
450         }
451
452         if (graph_analysis_data->dlg.window != NULL){ /* if we still have a window */
453                 graph_analysis_update(graph_analysis_data);             /* refresh it xxx */
454         }
455         else{
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_close, *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 #if GTK_MAJOR_VERSION < 2
478   GtkAccelGroup *accel_group;
479 #endif
480
481
482         GtkTooltips *tooltips = gtk_tooltips_new();
483
484         flow_graph_dlg_w=window_new(GTK_WINDOW_TOPLEVEL, "Ethereal: Flow Graph");
485
486         gtk_window_set_default_size(GTK_WINDOW(flow_graph_dlg_w), 350, 150);
487
488         main_vb = gtk_vbox_new (FALSE, 0);
489         gtk_container_add(GTK_CONTAINER(flow_graph_dlg_w), main_vb);
490         gtk_container_set_border_width (GTK_CONTAINER (main_vb), 12);
491
492 #if GTK_MAJOR_VERSION < 2
493   /* Accelerator group for the accelerators (or, as they're called in
494      Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
495      Ctrl+<key> is an accelerator). */
496   accel_group = gtk_accel_group_new();
497   gtk_window_add_accel_group(GTK_WINDOW(main_vb), accel_group);
498 #endif
499
500 #if 0
501         top_label = gtk_label_new ("Choose packets to include in the graph");
502         gtk_box_pack_start (GTK_BOX (main_vb), top_label, FALSE, FALSE, 8);
503 #endif
504         
505         gtk_widget_show(flow_graph_dlg_w);
506
507         /*** Packet range frame ***/
508         range_fr = gtk_frame_new("Choose packets");
509         gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 0);
510
511     range_tb = gtk_table_new(4, 4, FALSE);
512     gtk_container_border_width(GTK_CONTAINER(range_tb), 5);
513         gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
514
515         /* Process all packets */
516         select_all_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "_All packets", accel_group);
517         gtk_tooltips_set_tip (tooltips, select_all_rb, 
518                 ("Process all packets"), NULL);
519         SIGNAL_CONNECT(select_all_rb, "toggled", toggle_select_all, NULL);
520         gtk_table_attach_defaults(GTK_TABLE(range_tb), select_all_rb, 0, 1, 0, 1);
521         if (type_of_packets == ALL) {
522                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_all_rb),TRUE);
523         }
524         gtk_widget_show(select_all_rb);
525
526         /* Process displayed packets */
527         select_displayed_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(select_all_rb, "_Displayed packets", accel_group);
528         gtk_tooltips_set_tip (tooltips, select_displayed_rb, 
529                 ("Process displayed packets"), NULL);
530         SIGNAL_CONNECT(select_displayed_rb, "toggled", toggle_select_displayed, NULL);
531         gtk_table_attach_defaults(GTK_TABLE(range_tb), select_displayed_rb, 1, 2, 0, 1);
532         if (type_of_packets == DISPLAYED) {
533                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_displayed_rb),TRUE);
534         }
535         gtk_widget_show(select_displayed_rb);
536
537         gtk_widget_show(range_tb);
538
539         gtk_widget_show(range_fr);
540
541         /*** Flow type frame ***/
542         flow_type_fr = gtk_frame_new("Choose flow type");
543         gtk_box_pack_start(GTK_BOX(main_vb), flow_type_fr, FALSE, FALSE, 0);
544
545     flow_type_tb = gtk_table_new(4, 4, FALSE);
546     gtk_container_border_width(GTK_CONTAINER(flow_type_tb), 5);
547         gtk_container_add(GTK_CONTAINER(flow_type_fr), flow_type_tb);
548
549         /* General information */
550         select_general_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "_General flow", accel_group);
551         gtk_tooltips_set_tip (tooltips, select_general_rb, 
552                 ("Show all packets, with general information"), NULL);
553         SIGNAL_CONNECT(select_general_rb, "toggled", toggle_select_general, NULL);
554         gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_general_rb, 0, 1, 0, 1);
555         if (type_of_flow == GENERAL) {
556                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_general_rb),TRUE);
557         }
558         gtk_widget_show(select_general_rb);
559
560         /* TCP specific information */
561         select_tcp_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(select_general_rb, "_TCP flow", accel_group);
562         gtk_tooltips_set_tip (tooltips, select_tcp_rb, 
563                 ("Show only TCP packets, with TCP specific information"), NULL);
564         SIGNAL_CONNECT(select_tcp_rb, "toggled", toggle_select_tcp, NULL);
565         gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_tcp_rb, 1, 2, 0, 1);
566         if (type_of_flow == TCP) {
567                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_tcp_rb),TRUE);
568         }
569
570         gtk_widget_show(select_tcp_rb);
571
572         gtk_widget_show(flow_type_tb);
573         gtk_widget_show(flow_type_fr);
574
575         /*** Node address type frame ***/
576         node_addr_fr = gtk_frame_new("Choose node address type");
577         gtk_box_pack_start(GTK_BOX(main_vb), node_addr_fr, FALSE, FALSE, 0);
578
579     node_addr_tb = gtk_table_new(4, 4, FALSE);
580     gtk_container_border_width(GTK_CONTAINER(node_addr_tb), 5);
581         gtk_container_add(GTK_CONTAINER(node_addr_fr), node_addr_tb);
582
583         /* Source / Dest address */
584         src_dst_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "_Standard source/destination addresses", accel_group);
585         gtk_tooltips_set_tip (tooltips, src_dst_rb, 
586                 ("Nodes in the diagram are identified with source and destination addresses"), NULL);
587         SIGNAL_CONNECT(src_dst_rb, "toggled", toggle_select_srcdst, NULL);
588         gtk_table_attach_defaults(GTK_TABLE(node_addr_tb), src_dst_rb, 0, 1, 0, 1);
589         if (node_addr_type == SRCDST) {
590                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(src_dst_rb),TRUE);
591         }
592         gtk_widget_show(src_dst_rb);
593
594         /* Network source / dest address */
595         net_src_dst_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(src_dst_rb, "_Network source/destination addresses", accel_group);
596         gtk_tooltips_set_tip (tooltips, net_src_dst_rb, 
597                 ("Nodes in the diagram are identified with network source and destination addresses"), NULL);
598         SIGNAL_CONNECT(net_src_dst_rb, "toggled", toggle_select_netsrcdst, NULL);
599         gtk_table_attach_defaults(GTK_TABLE(node_addr_tb), net_src_dst_rb, 1, 2, 0, 1);
600         if (node_addr_type == NET_SRCDST) {
601                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(net_src_dst_rb),TRUE);
602         }
603
604         gtk_widget_show(net_src_dst_rb);
605
606         gtk_widget_show(node_addr_tb);
607         gtk_widget_show(node_addr_fr);
608
609         /* button row */
610         hbuttonbox = gtk_hbutton_box_new ();
611         gtk_box_pack_start (GTK_BOX (main_vb), hbuttonbox, FALSE, FALSE, 0);
612         gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
613         gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbuttonbox), 30);
614
615         bt_ok = BUTTON_NEW_FROM_STOCK(GTK_STOCK_OK);
616         gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_ok);
617         gtk_tooltips_set_tip (tooltips, bt_ok, "Show the flow graph", NULL);
618         SIGNAL_CONNECT(bt_ok, "clicked", flow_graph_on_ok, NULL);
619         gtk_widget_show(bt_ok);
620
621         bt_close = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
622         gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_close);
623         GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
624         gtk_tooltips_set_tip (tooltips, bt_close, "Close this dialog", NULL);
625         window_set_cancel_button(flow_graph_dlg_w, bt_close, window_cancel_button_cb);
626
627         SIGNAL_CONNECT(flow_graph_dlg_w, "delete_event", window_delete_event_cb, NULL);
628         SIGNAL_CONNECT(flow_graph_dlg_w, "destroy", flow_graph_on_destroy, NULL);
629
630         gtk_widget_show_all(flow_graph_dlg_w);
631         window_present(flow_graph_dlg_w);
632
633         flow_graph_dlg = flow_graph_dlg_w;
634 }       
635
636 /****************************************************************************/
637 /* PUBLIC                                                                   */
638 /****************************************************************************/
639
640 /* init function for tap */
641 static void
642 flow_graph_init_tap(const char *dummy _U_, void* userdata _U_)
643 {
644
645         /* initialize graph items store */
646         flow_graph_data_init();
647         
648         /* init the Graph Analysys */
649         graph_analysis_data = graph_analysis_init();
650         graph_analysis_data->graph_info = graph_analysis;
651
652         /* create dialog box if necessary */
653         if (flow_graph_dlg == NULL) {
654                 flow_graph_dlg_create();
655         } else {
656                 /* There's already a dialog box; reactivate it. */
657                 reactivate_window(flow_graph_dlg);
658         }
659
660 }
661
662
663 /****************************************************************************/
664 /* entry point when called via the GTK menu */
665 static void flow_graph_launch(GtkWidget *w _U_, gpointer data _U_)
666 {
667         flow_graph_init_tap("",NULL);
668 }
669
670 /****************************************************************************/
671 void
672 register_tap_listener_flow_graph(void)
673 {
674         register_stat_cmd_arg("flow_graph",flow_graph_init_tap,NULL);
675         register_stat_menu_item("Flo_w Graph...", REGISTER_STAT_GROUP_NONE,
676             flow_graph_launch, NULL, NULL, NULL);
677             
678 }
679