Move the common parts of iface_lists.[ch] from ui/gtk/ to ui/. Leave the
[metze/wireshark/wip.git] / ui / 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #  include <config.h>
29 #endif
30 #include <string.h>
31
32 #include <gtk/gtk.h>
33
34 #include <epan/epan.h>
35 #include <epan/packet.h>
36 #include <epan/filesystem.h>
37 #include <epan/stat_cmd_args.h>
38 #include <epan/to_str.h>
39 #include <epan/tap.h>
40 #include <epan/dissectors/packet-tcp.h>
41 #include <epan/strutil.h>
42
43 #include "../stat_menu.h"
44 #include "ui/simple_dialog.h"
45
46 #include "ui/gtk/graph_analysis.h"
47 #include "ui/gtk/gui_stat_menu.h"
48 #include "ui/gtk/dlg_utils.h"
49 #include "ui/gtk/gui_utils.h"
50 #include "ui/gtk/stock_icons.h"
51 #include "ui/gtk/gtkglobals.h"
52 #include "ui/gtk/main.h"
53 #include "ui/gtk/old-gtk-compat.h"
54
55
56 #define TYPE_OF_PACKETS_DISPLAYED 0
57 #define TYPE_OF_PACKETS_ALL 1
58
59 #define TYPE_OF_FLOW_GENERAL 0
60 #define TYPE_OF_FLOW_TCP 1
61
62 #define NODE_ADDR_TYPE_SRCDST 0
63 #define NODE_ADDR_TYPE_NET_SRCDST 1
64
65 static int type_of_packets = TYPE_OF_PACKETS_DISPLAYED;
66 static int type_of_flow    = TYPE_OF_FLOW_GENERAL;
67 static int node_addr_type  = NODE_ADDR_TYPE_SRCDST;
68
69 static int tap_identifier;
70 static gboolean have_frame_tap_listener=FALSE;
71 static gboolean have_tcp_tap_listener=FALSE;
72 static graph_analysis_info_t *graph_analysis = NULL;
73 static graph_analysis_data_t *graph_analysis_data = NULL;
74
75 static GtkWidget *flow_graph_dlg = NULL;
76
77 static GtkWidget *select_all_rb;
78 static GtkWidget *select_displayed_rb;
79 static GtkWidget *select_general_rb;
80 static GtkWidget *select_tcp_rb;
81 static GtkWidget *src_dst_rb;
82 static GtkWidget *net_src_dst_rb;
83
84 /****************************************************************************/
85 /* free up memory and initialize the pointers */
86
87 static void
88 flow_graph_reset(void *ptr _U_)
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 }
111
112 /****************************************************************************/
113 static void
114 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 }
119
120
121 /****************************************************************************/
122 static void
123 remove_tap_listener_flow_graph(void)
124 {
125         protect_thread_critical_region();
126         remove_tap_listener(&(tap_identifier));
127         unprotect_thread_critical_region();
128
129         have_frame_tap_listener=FALSE;
130         have_tcp_tap_listener=FALSE;
131 }
132
133
134 /****************************************************************************/
135 /* CALLBACKS                                                                */
136 /****************************************************************************/
137 static void
138 flow_graph_on_destroy(GObject *object _U_, gpointer user_data _U_)
139 {
140         /* remove_tap_listeners */
141         remove_tap_listener_flow_graph();
142
143         /* Clean up memory used by tap */
144         flow_graph_reset(NULL);
145
146         g_assert(graph_analysis != NULL);
147         g_assert(graph_analysis_data != NULL);
148
149         g_free(graph_analysis);
150         graph_analysis = NULL;
151
152         g_free(graph_analysis_data);
153         graph_analysis_data = NULL;
154
155         /* Note that we no longer have a "Flow Graph" dialog box. */
156         flow_graph_dlg = NULL;
157 }
158
159
160 /****************************************************************************/
161 static void
162 toggle_select_all(GtkWidget *widget _U_, gpointer user_data _U_)
163 {
164         /* is the button now active? */
165         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_all_rb))) {
166                 type_of_packets = TYPE_OF_PACKETS_ALL;
167         }
168 }
169
170 /****************************************************************************/
171 static void
172 toggle_select_displayed(GtkWidget *widget _U_, gpointer user_data _U_)
173 {
174         /* is the button now active? */
175         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_displayed_rb))) {
176                 type_of_packets = TYPE_OF_PACKETS_DISPLAYED;
177         }
178 }
179
180 /****************************************************************************/
181 static void
182 toggle_select_general(GtkWidget *widget _U_, gpointer user_data _U_)
183 {
184         /* is the button now active? */
185         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_general_rb))) {
186                 type_of_flow = TYPE_OF_FLOW_GENERAL;
187         }
188 }
189
190 /****************************************************************************/
191 static void
192 toggle_select_tcp(GtkWidget *widget _U_, gpointer user_data _U_)
193 {
194         /* is the button now active? */
195         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_tcp_rb))) {
196                 type_of_flow = TYPE_OF_FLOW_TCP;
197         }
198 }
199
200 /****************************************************************************/
201 static void
202 toggle_select_srcdst(GtkWidget *widget _U_, gpointer user_data _U_)
203 {
204         /* is the button now active? */
205         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(src_dst_rb))) {
206                 node_addr_type = NODE_ADDR_TYPE_SRCDST;
207         }
208 }
209
210 /****************************************************************************/
211 static void
212 toggle_select_netsrcdst(GtkWidget *widget _U_, gpointer user_data _U_)
213 {
214         /* is the button now active? */
215         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(net_src_dst_rb))) {
216                 node_addr_type = NODE_ADDR_TYPE_NET_SRCDST;
217         }
218 }
219
220 /****************************************************************************/
221 /* Add a new frame into the graph */
222 static int
223 flow_graph_frame_add_to_graph(packet_info *pinfo)
224 {
225         graph_analysis_item_t *gai;
226         int i;
227         gchar *protocol;
228         gchar *colinfo;
229
230         protocol=NULL;
231         colinfo=NULL;
232
233         if (node_addr_type == NODE_ADDR_TYPE_NET_SRCDST) {
234                 if (pinfo->net_src.type!=AT_NONE && pinfo->net_dst.type!=AT_NONE) {
235                         gai = g_malloc(sizeof(graph_analysis_item_t));
236                         COPY_ADDRESS(&(gai->src_addr),&(pinfo->net_src));
237                         COPY_ADDRESS(&(gai->dst_addr),&(pinfo->net_dst));
238                 }
239                 else return 0;
240
241         } else {
242                 if (pinfo->src.type!=AT_NONE && pinfo->dst.type!=AT_NONE) {
243                         gai = g_malloc(sizeof(graph_analysis_item_t));
244                         COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
245                         COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
246                 }
247                 else return 0;
248         }
249
250         gai->fd = pinfo->fd;
251
252         gai->port_src=pinfo->srcport;
253         gai->port_dst=pinfo->destport;
254         gai->comment=NULL;
255         gai->frame_label=NULL;
256
257         /* this code doesn't make sense.
258         g_free(gai->comment);
259         g_free(gai->frame_label);
260         */
261
262         if(pinfo->cinfo) {
263                 if (pinfo->cinfo->col_first[COL_INFO]>=0){
264
265                         for (i = pinfo->cinfo->col_first[COL_INFO]; i <= pinfo->cinfo->col_last[COL_INFO]; i++) {
266                                 if (pinfo->cinfo->fmt_matx[i][COL_INFO]) {
267                                         colinfo = g_strdup(pinfo->cinfo->col_data[i]);
268                                         /* break; ? or g_free(colinfo); before g_strdup() */
269                                 }
270                         }
271                 }
272
273                 if (pinfo->cinfo->col_first[COL_PROTOCOL]>=0){
274
275                         for (i = pinfo->cinfo->col_first[COL_PROTOCOL]; i <= pinfo->cinfo->col_last[COL_PROTOCOL]; i++) {
276                                 if (pinfo->cinfo->fmt_matx[i][COL_PROTOCOL]) {
277                                         protocol = g_strdup(pinfo->cinfo->col_data[i]);
278                                         /* break; ? or g_free(protocol); before g_strdup() */
279                                 }
280                         }
281                 }
282         }
283
284         if (colinfo != NULL) {
285                 if (protocol != NULL) {
286                         gai->frame_label = g_strdup_printf("%.19s", colinfo);
287                         gai->comment = g_strdup_printf("%s: %s", protocol, colinfo);
288                 } else {
289                         gai->frame_label = g_strdup_printf("%.19s", colinfo);
290                         gai->comment = g_strdup_printf("%s", colinfo);
291                 }
292         } else {
293                 /* This will probably never happen...*/
294                 if (protocol != NULL) {
295                         gai->frame_label = g_strdup_printf("%.19s", protocol);
296                         gai->comment = g_strdup_printf("%s", protocol);
297                 }
298         }
299
300         g_free(protocol);
301         g_free(colinfo);
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 /* Add a new tcp frame into the graph */
314 static int
315 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         gboolean flags_found = FALSE;
322         gchar flags[64];
323
324         gai = g_malloc(sizeof(graph_analysis_item_t));
325         gai->fd = pinfo->fd;
326         if (node_addr_type == NODE_ADDR_TYPE_NET_SRCDST) {
327                 COPY_ADDRESS(&(gai->src_addr),&(pinfo->net_src));
328                 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->net_dst));
329         } else {
330                 COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
331                 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
332         }
333         gai->port_src=pinfo->srcport;
334         gai->port_dst=pinfo->destport;
335
336         flags[0] = '\0';
337         for (i = 0; i < 8; i++) {
338                 bpos = 1 << i;
339                 if (tcph->th_flags & bpos) {
340                         if (flags_found) {
341                                 g_strlcat(flags, ", ", sizeof(flags));
342                         }
343                         g_strlcat(flags, fstr[i], sizeof(flags));
344                         flags_found = TRUE;
345                 }
346         }
347         if (flags[0] == '\0') {
348                 g_snprintf (flags, sizeof(flags), "<None>");
349         }
350
351         if ((tcph->th_have_seglen)&&(tcph->th_seglen!=0)){
352                 gai->frame_label = g_strdup_printf("%s - Len: %u",flags, tcph->th_seglen);
353         }
354         else{
355                 gai->frame_label = g_strdup(flags);
356         }
357
358         if (tcph->th_flags & TH_ACK)
359                 gai->comment = g_strdup_printf("Seq = %u Ack = %u",tcph->th_seq, tcph->th_ack);
360         else
361                 gai->comment = g_strdup_printf("Seq = %u",tcph->th_seq);
362
363         gai->line_style=1;
364         gai->conv_num=0;
365         gai->display=TRUE;
366
367         graph_analysis->list = g_list_append(graph_analysis->list, gai);
368
369         return 1;
370 }
371
372
373
374 /****************************************************************************/
375 /* whenever a frame packet is seen by the tap listener */
376 static gboolean
377 flow_graph_frame_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *dummy _U_)
378 {
379         if ((type_of_packets == TYPE_OF_PACKETS_ALL)||(pinfo->fd->flags.passed_dfilter==1)){
380                 flow_graph_frame_add_to_graph(pinfo);
381         }
382
383         return TRUE;
384 }
385
386 /****************************************************************************/
387 /* whenever a TCP packet is seen by the tap listener */
388 static gboolean
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 == TYPE_OF_PACKETS_ALL)||(pinfo->fd->flags.passed_dfilter==1)){
394                 flow_graph_tcp_add_to_graph(pinfo,tcph);
395         }
396
397         return TRUE;
398 }
399
400
401 static void
402 flow_graph_packet_draw(void *prs _U_)
403 {
404         return;
405 }
406
407 /****************************************************************************/
408 static void
409 flow_graph_on_ok                    (GtkButton       *button _U_,
410                                      gpointer         user_data)
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 == 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                                 TL_REQUIRES_COLUMNS,
429                                 flow_graph_reset,
430                                 flow_graph_frame_packet,
431                                 flow_graph_packet_draw
432                                 );
433                         have_frame_tap_listener=TRUE;
434                 }
435
436                 cf_retap_packets(&cfile);
437         }
438         else if (type_of_flow == TYPE_OF_FLOW_TCP){
439         /* Register the tap listener */
440
441                 if(have_tcp_tap_listener==FALSE)
442                 {
443                         /* don't register tap listener, if we have it already */
444                         register_tap_listener("tcp", &tap_identifier, NULL,
445                                 0,
446                                 flow_graph_reset,
447                                 flow_graph_tcp_packet,
448                                 flow_graph_packet_draw
449                                 );
450                         have_tcp_tap_listener=TRUE;
451                 }
452
453                 cf_retap_packets(&cfile);
454         }
455
456         if (graph_analysis_data->dlg.window != NULL){ /* if we still have a window */
457                 graph_analysis_update(graph_analysis_data);             /* refresh it xxx */
458         }
459         else{
460                 graph_analysis_data->dlg.parent_w = user_data;
461                 graph_analysis_create(graph_analysis_data);
462         }
463 }
464
465
466 /****************************************************************************/
467 /* INTERFACE                                                                */
468 /****************************************************************************/
469
470 static void
471 flow_graph_dlg_create (void)
472 {
473         GtkWidget *flow_graph_dlg_w;
474         GtkWidget *main_vb;
475         GtkWidget *hbuttonbox;
476         GtkWidget *bt_cancel, *bt_ok;
477 #if 0
478         GtkWidget *top_label = NULL;
479 #endif
480         GtkWidget *flow_type_fr, *range_fr, *range_tb, *flow_type_tb, *node_addr_fr, *node_addr_tb;
481
482         flow_graph_dlg_w = dlg_window_new("Wireshark: Flow Graph");  /* transient_for top_level */
483         gtk_window_set_destroy_with_parent (GTK_WINDOW(flow_graph_dlg_w), TRUE);
484
485         gtk_window_set_default_size(GTK_WINDOW(flow_graph_dlg_w), 250, 150);
486
487         main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
488         gtk_container_add(GTK_CONTAINER(flow_graph_dlg_w), main_vb);
489         gtk_container_set_border_width (GTK_CONTAINER (main_vb), 7);
490
491 #if 0
492         top_label = gtk_label_new ("Choose packets to include in the graph");
493         gtk_box_pack_start (GTK_BOX (main_vb), top_label, FALSE, FALSE, 8);
494 #endif
495
496         gtk_widget_show(flow_graph_dlg_w);
497
498         /*** Packet range frame ***/
499         range_fr = gtk_frame_new("Choose packets");
500         gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 5);
501
502         range_tb = gtk_table_new(4, 4, FALSE);
503         gtk_container_set_border_width(GTK_CONTAINER(range_tb), 5);
504         gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
505
506         /* Process all packets */
507         select_all_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_All packets");
508         gtk_widget_set_tooltip_text (select_all_rb, ("Process all packets"));
509         g_signal_connect(select_all_rb, "toggled", G_CALLBACK(toggle_select_all), NULL);
510         gtk_table_attach_defaults(GTK_TABLE(range_tb), select_all_rb, 0, 1, 0, 1);
511         if (type_of_packets == TYPE_OF_PACKETS_ALL) {
512                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_all_rb),TRUE);
513         }
514         gtk_widget_show(select_all_rb);
515
516         /* Process displayed packets */
517         select_displayed_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(select_all_rb),
518                                                                              "_Displayed packets");
519         gtk_widget_set_tooltip_text (select_displayed_rb, ("Process displayed packets"));
520         g_signal_connect(select_displayed_rb, "toggled", G_CALLBACK(toggle_select_displayed), NULL);
521         gtk_table_attach_defaults(GTK_TABLE(range_tb), select_displayed_rb, 0, 1, 1, 2);
522         if (type_of_packets == TYPE_OF_PACKETS_DISPLAYED) {
523                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_displayed_rb),TRUE);
524         }
525         gtk_widget_show(select_displayed_rb);
526
527         gtk_widget_show(range_tb);
528         gtk_widget_show(range_fr);
529
530         /*** Flow type frame ***/
531         flow_type_fr = gtk_frame_new("Choose flow type");
532         gtk_box_pack_start(GTK_BOX(main_vb), flow_type_fr, FALSE, FALSE, 5);
533
534         flow_type_tb = gtk_table_new(4, 4, FALSE);
535         gtk_container_set_border_width(GTK_CONTAINER(flow_type_tb), 5);
536         gtk_container_add(GTK_CONTAINER(flow_type_fr), flow_type_tb);
537
538         /* General information */
539         select_general_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_General flow");
540         gtk_widget_set_tooltip_text (select_general_rb, ("Show all packets, with general information"));
541         g_signal_connect(select_general_rb, "toggled", G_CALLBACK(toggle_select_general), NULL);
542         gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_general_rb, 0, 1, 0, 1);
543         if (type_of_flow == TYPE_OF_FLOW_GENERAL) {
544                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_general_rb),TRUE);
545         }
546         gtk_widget_show(select_general_rb);
547
548         /* TCP specific information */
549         select_tcp_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(select_general_rb),
550                                                                        "_TCP flow");
551         gtk_widget_set_tooltip_text (select_tcp_rb, ("Show only TCP packets, with TCP specific information"));
552         g_signal_connect(select_tcp_rb, "toggled", G_CALLBACK(toggle_select_tcp), NULL);
553         gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_tcp_rb, 0, 1, 1, 2);
554         if (type_of_flow == TYPE_OF_FLOW_TCP) {
555                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_tcp_rb),TRUE);
556         }
557         gtk_widget_show(select_tcp_rb);
558
559         gtk_widget_show(flow_type_tb);
560         gtk_widget_show(flow_type_fr);
561
562         /*** Node address type frame ***/
563         node_addr_fr = gtk_frame_new("Choose node address type");
564         gtk_box_pack_start(GTK_BOX(main_vb), node_addr_fr, FALSE, FALSE, 5);
565
566         node_addr_tb = gtk_table_new(4, 4, FALSE);
567         gtk_container_set_border_width(GTK_CONTAINER(node_addr_tb), 5);
568         gtk_container_add(GTK_CONTAINER(node_addr_fr), node_addr_tb);
569
570         /* Source / Dest address */
571         src_dst_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_Standard source/destination addresses");
572         gtk_widget_set_tooltip_text (src_dst_rb,
573                 ("Nodes in the diagram are identified with source and destination addresses"));
574         g_signal_connect(src_dst_rb, "toggled", G_CALLBACK(toggle_select_srcdst), NULL);
575         gtk_table_attach_defaults(GTK_TABLE(node_addr_tb), src_dst_rb, 0, 1, 0, 1);
576         if (node_addr_type == NODE_ADDR_TYPE_SRCDST) {
577                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(src_dst_rb),TRUE);
578         }
579         gtk_widget_show(src_dst_rb);
580
581         /* Network source / dest address */
582         net_src_dst_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(src_dst_rb),
583                                                                         "_Network source/destination addresses");
584         gtk_widget_set_tooltip_text (net_src_dst_rb,
585                 ("Nodes in the diagram are identified with network source and destination addresses"));
586         g_signal_connect(net_src_dst_rb, "toggled", G_CALLBACK(toggle_select_netsrcdst), NULL);
587         gtk_table_attach_defaults(GTK_TABLE(node_addr_tb), net_src_dst_rb, 0, 1, 1, 2);
588         if (node_addr_type == NODE_ADDR_TYPE_NET_SRCDST) {
589                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(net_src_dst_rb),TRUE);
590         }
591         gtk_widget_show(net_src_dst_rb);
592
593         gtk_widget_show(node_addr_tb);
594         gtk_widget_show(node_addr_fr);
595
596         /* button row */
597         hbuttonbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
598         gtk_box_pack_start (GTK_BOX (main_vb), hbuttonbox, FALSE, FALSE, 5);
599         gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
600         gtk_box_set_spacing (GTK_BOX (hbuttonbox), 30);
601
602         bt_ok = gtk_button_new_from_stock(GTK_STOCK_OK);
603         gtk_box_pack_start(GTK_BOX(hbuttonbox), bt_ok, TRUE, TRUE, 0);
604         gtk_widget_set_tooltip_text (bt_ok, "Show the flow graph");
605         g_signal_connect(bt_ok, "clicked", G_CALLBACK(flow_graph_on_ok), flow_graph_dlg_w);
606         gtk_widget_show(bt_ok);
607
608         bt_cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
609         gtk_box_pack_start(GTK_BOX(hbuttonbox), bt_cancel, TRUE, TRUE, 0);
610         gtk_widget_set_can_default(bt_cancel, TRUE);
611         gtk_widget_set_tooltip_text (bt_cancel, "Cancel this dialog");
612         window_set_cancel_button(flow_graph_dlg_w, bt_cancel, window_cancel_button_cb);
613
614         g_signal_connect(flow_graph_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
615         g_signal_connect(flow_graph_dlg_w, "destroy", G_CALLBACK(flow_graph_on_destroy), NULL);
616
617         gtk_widget_show_all(flow_graph_dlg_w);
618         window_present(flow_graph_dlg_w);
619
620         flow_graph_dlg = flow_graph_dlg_w;
621 }
622
623 /****************************************************************************/
624 /* PUBLIC                                                                   */
625 /****************************************************************************/
626
627 /* init function for tap */
628 static void
629 flow_graph_init_tap(const char *dummy _U_, void* userdata _U_)
630 {
631         /* The storage allocated by flow_graph_data_init() and graph_analysis_init()  */
632         /*  will be considered to be "associated with" the flow_graph_dlg dialog box. */
633         /* It will be freed when the flow_graph_dlg dialog box is destroyed.          */
634         if (flow_graph_dlg != NULL) {
635                 g_assert(graph_analysis != NULL);
636                 g_assert(graph_analysis_data != NULL);
637                 /* There's already a dialog box; reactivate it. */
638                 reactivate_window(flow_graph_dlg);
639         } else {
640                 g_assert(graph_analysis == NULL);
641                 g_assert(graph_analysis_data == NULL);
642
643                 /* initialize graph items store */
644                 flow_graph_data_init();
645
646                 /* init the Graph Analysis */
647                 graph_analysis_data = graph_analysis_init();
648                 graph_analysis_data->graph_info = graph_analysis;
649
650                 flow_graph_dlg_create();
651         }
652 }
653
654
655 /****************************************************************************/
656 /* entry point when called via the GTK menu */
657 void
658 flow_graph_launch(GtkAction *action _U_, gpointer user_data _U_)
659 {
660         flow_graph_init_tap("",NULL);
661 }
662
663 /****************************************************************************/
664 void
665 register_tap_listener_flow_graph(void)
666 {
667         register_stat_cmd_arg("flow_graph",flow_graph_init_tap,NULL);
668 }
669