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