Get rid of a bunch of "Ethereal"s and "ethereal"s in comments, GUI
[obnox/wireshark/wip.git] / gtk / rtp_stream_dlg.c
1 /* rtp_stream_dlg.c
2  * RTP streams summary addition for Wireshark
3  *
4  * $Id$
5  *
6  * Copyright 2003, Alcatel Business Systems
7  * By Lars Ruoff <lars.ruoff@gmx.net>
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 1998 Gerald Combs
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #  include <config.h>
30 #endif
31
32 #include "rtp_stream_dlg.h"
33 #include "rtp_stream.h"
34 #include "rtp_analysis.h"
35
36 #include "globals.h"
37 #include "epan/filesystem.h"
38
39 #include "../stat_menu.h"
40 #include "gui_stat_menu.h"
41 #include "dlg_utils.h"
42 #include "file_dlg.h"
43 #include "gui_utils.h"
44 #include "compat_macros.h"
45 #include "gtkglobals.h"
46
47 #include "image/clist_ascend.xpm"
48 #include "image/clist_descend.xpm"
49
50 #include <epan/rtp_pt.h>
51
52 #include <epan/address.h>
53
54 #include <string.h>
55 #include <locale.h>
56 #include <epan/addr_resolv.h>
57
58
59 static const gchar FWD_LABEL_TEXT[] = "Select a forward stream with left mouse button";
60 static const gchar REV_LABEL_TEXT[] = "Select a reverse stream with SHIFT + left mouse button";
61
62 /****************************************************************************/
63 /* pointer to the one and only dialog window */
64 static GtkWidget *rtp_stream_dlg = NULL;
65
66 /* save as dialog box */
67 static GtkWidget *rtpstream_save_dlg = NULL;
68 static GtkWidget *clist = NULL;
69 static GtkWidget *top_label = NULL;
70 static GtkWidget *label_fwd = NULL;
71 static GtkWidget *label_rev = NULL;
72
73 static rtp_stream_info_t* selected_stream_fwd = NULL;  /* current selection */
74 static rtp_stream_info_t* selected_stream_rev = NULL;  /* current selection for reversed */
75 static GList *last_list = NULL;
76
77 static guint32 streams_nb = 0;     /* number of displayed streams */
78
79 #define NUM_COLS 12
80 static const gchar *titles[NUM_COLS] =  {"Src IP addr", "Src port",  "Dest IP addr", "Dest port", "SSRC", "Payload", "Packets", "Lost", "Max Delta (ms)", "Max Jitter (ms)", "Mean Jitter (ms)", "Pb?"};
81
82 /****************************************************************************/
83 /* append a line to clist */
84 static void add_to_clist(rtp_stream_info_t* strinfo)
85 {
86         gchar label_text[256];
87         gint added_row;
88         gchar *data[NUM_COLS];
89         guint32 expected;
90         gint32 lost;
91         double perc;
92         int i;
93         char *savelocale;
94
95         /* save the current locale */
96         savelocale = setlocale(LC_NUMERIC, NULL);
97         /* switch to "C" locale to avoid problems with localized decimal separators
98                 in g_snprintf("%f") functions */
99         setlocale(LC_NUMERIC, "C");
100
101         data[0] = g_strdup(get_addr_name(&(strinfo->src_addr)));
102         data[1] = g_strdup_printf("%u", strinfo->src_port);
103         data[2] = g_strdup(get_addr_name(&(strinfo->dest_addr)));
104         data[3] = g_strdup_printf("%u", strinfo->dest_port);
105         data[4] = g_strdup_printf("%u", strinfo->ssrc);
106         data[5] = g_strdup(val_to_str(strinfo->pt, rtp_payload_type_vals,
107                 "Unknown (%u)"));
108         data[6] = g_strdup_printf("%u", strinfo->npackets);
109
110         expected = (strinfo->rtp_stats.stop_seq_nr + strinfo->rtp_stats.cycles*65536)
111                 - strinfo->rtp_stats.start_seq_nr + 1;
112         lost = expected - strinfo->rtp_stats.total_nr;
113         if (expected){
114                 perc = (double)(lost*100)/(double)expected;
115         } else {
116                 perc = 0;
117         }
118         data[7] = g_strdup_printf("%d (%.1f%%)", lost, perc);
119         data[8] = g_strdup_printf("%.2f", strinfo->rtp_stats.max_delta*1000);
120         data[9] = g_strdup_printf("%.2f", strinfo->rtp_stats.max_jitter*1000);
121         data[10] = g_strdup_printf("%.2f", strinfo->rtp_stats.mean_jitter*1000);
122         if (strinfo->problem)
123                 data[11] = g_strdup("X");
124         else
125                 data[11] = g_strdup("");
126
127         /* restore previous locale setting */
128         setlocale(LC_NUMERIC, savelocale);
129
130         added_row = gtk_clist_append(GTK_CLIST(clist), data);
131         for (i = 0; i < NUM_COLS; i++)
132                 g_free(data[i]);
133
134         /* set data pointer of last row to point to user data for that row */
135         gtk_clist_set_row_data(GTK_CLIST(clist), added_row, strinfo);
136
137         /* Update the top label with the number of detected streams */
138         sprintf(label_text,
139                 "Detected %d RTP streams. Choose one for forward and reverse direction for analysis",
140                 ++streams_nb);
141         gtk_label_set(GTK_LABEL(top_label), label_text);
142 }
143
144 /****************************************************************************/
145 static void save_stream_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
146 {
147         /* Note that we no longer have a Save voice info dialog box. */
148         rtpstream_save_dlg = NULL;
149 }
150
151 /****************************************************************************/
152 /* save in a file */
153 static void save_stream_ok_cb(GtkWidget *ok_bt _U_, gpointer user_data _U_)
154 {
155         gchar *g_dest;
156
157         if (!selected_stream_fwd)
158                 return;
159
160         g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (rtpstream_save_dlg)));
161
162         /* Perhaps the user specified a directory instead of a file.
163         Check whether they did. */
164         if (test_for_directory(g_dest) == EISDIR) {
165                 /* It's a directory - set the file selection box to display it. */
166                 set_last_open_dir(g_dest);
167                 g_free(g_dest);
168                 file_selection_set_current_folder(rtpstream_save_dlg, get_last_open_dir());
169                 return;
170         }
171
172         /*
173          * Don't dismiss the dialog box if the save operation fails.
174          */
175         if (!rtpstream_save(selected_stream_fwd, g_dest))
176                 return;
177
178         window_destroy(GTK_WIDGET(rtpstream_save_dlg));
179 }
180
181
182 /****************************************************************************/
183 /* CALLBACKS                                                                */
184 /****************************************************************************/
185 static void
186 rtpstream_on_destroy                      (GtkObject       *object _U_,
187                                         gpointer         user_data _U_)
188 {
189         /* Remove the stream tap listener */
190         remove_tap_listener_rtp_stream();
191
192         /* Is there a save voice window open? */
193         if (rtpstream_save_dlg != NULL)
194                 window_destroy(rtpstream_save_dlg);
195
196         /* Clean up memory used by stream tap */
197         rtpstream_reset((rtpstream_tapinfo_t*) rtpstream_get_info());
198
199         /* Note that we no longer have a "RTP Streams" dialog box. */
200         rtp_stream_dlg = NULL;
201 }
202
203
204 /****************************************************************************/
205 static void
206 rtpstream_on_unselect                  (GtkButton       *button _U_,
207                                         gpointer         user_data _U_)
208 {
209         selected_stream_fwd = NULL;
210         selected_stream_rev = NULL;
211         gtk_clist_unselect_all(GTK_CLIST(clist));
212         gtk_label_set_text(GTK_LABEL(label_fwd), FWD_LABEL_TEXT);
213         gtk_label_set_text(GTK_LABEL(label_rev), REV_LABEL_TEXT);
214 }
215
216
217 /****************************************************************************/
218 static gint rtp_stream_info_cmp_reverse(gconstpointer aa, gconstpointer bb)
219 {
220         const struct _rtp_stream_info* a = aa;
221         const struct _rtp_stream_info* b = bb;
222
223         if (a==NULL || b==NULL)
224                 return 1;
225         if ((ADDRESSES_EQUAL(&(a->src_addr), &(b->dest_addr)))
226                 && (a->src_port == b->dest_port)
227                 && (ADDRESSES_EQUAL(&(a->dest_addr), &(b->src_addr)))
228                 && (a->dest_port == b->src_port))
229                 return 0;
230         else
231                 return 1;
232 }
233
234 /****************************************************************************/
235 static void
236 rtpstream_on_findrev                   (GtkButton       *button _U_,
237                                         gpointer         user_data _U_)
238 {
239         gint row;
240         gint start_row;
241         rtp_stream_info_t* pstream = NULL;
242
243         if (selected_stream_fwd==NULL)
244                 return;
245         if (selected_stream_rev==NULL) {
246                 pstream = selected_stream_fwd;
247         }
248         else {
249                 pstream = selected_stream_rev;
250         }
251
252         start_row = gtk_clist_find_row_from_data(GTK_CLIST(clist), pstream);
253         row = start_row+1;
254
255         for (row=start_row+1;
256                 (pstream = gtk_clist_get_row_data(GTK_CLIST(clist), row));
257                 row++) {
258                 if (rtp_stream_info_cmp_reverse(selected_stream_fwd, pstream) == 0) {
259                         gtk_clist_select_row(GTK_CLIST(clist), row, 0);
260                         gtk_clist_moveto(GTK_CLIST(clist), row, 0, 0.5, 0);
261                         return;
262                 }
263         }
264
265         /* wrap around */
266         for (row=0;
267                 (pstream = gtk_clist_get_row_data(GTK_CLIST(clist), row)) && row<start_row;
268                 row++) {
269                 if (rtp_stream_info_cmp_reverse(selected_stream_fwd, pstream) == 0) {
270                         gtk_clist_select_row(GTK_CLIST(clist), row, 0);
271                         gtk_clist_moveto(GTK_CLIST(clist), row, 0, 0.5, 0);
272                         return;
273                 }
274         }
275
276         /* if we didnt find another stream, highlight the current reverse stream */
277         if (selected_stream_rev!=NULL) {
278                 gtk_clist_select_row(GTK_CLIST(clist), row, 0);
279                 gtk_clist_moveto(GTK_CLIST(clist), row, 0, 0.5, 0);
280         }
281 }
282
283
284 /****************************************************************************/
285 /*
286 static void
287 rtpstream_on_goto                      (GtkButton       *button _U_,
288                                         gpointer         user_data _U_)
289 {
290         if (selected_stream_fwd)
291         {
292                 cf_goto_frame(&cfile, selected_stream_fwd->first_frame_num);
293         }
294 }
295 */
296
297
298 /****************************************************************************/
299 static void
300 rtpstream_on_save                      (GtkButton       *button _U_,
301                                         gpointer         data _U_)
302 {
303         rtpstream_tapinfo_t* tapinfo = data;
304
305         GtkWidget *vertb;
306         GtkWidget *ok_bt;
307
308         if (!selected_stream_fwd)
309                 return;
310
311         if (rtpstream_save_dlg != NULL) {
312                 /* There's already a Save dialog box; reactivate it. */
313                 reactivate_window(rtpstream_save_dlg);
314                 return;
315         }
316
317         /* XXX - use file_selection from dlg_utils instead! */
318         rtpstream_save_dlg = gtk_file_selection_new("Wireshark: Save selected stream in rtpdump ('-F dump') format");
319
320         /* Container for each row of widgets */
321         vertb = gtk_vbox_new(FALSE, 0);
322         gtk_container_border_width(GTK_CONTAINER(vertb), 5);
323         gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(rtpstream_save_dlg)->action_area),
324                 vertb, FALSE, FALSE, 0);
325         gtk_widget_show (vertb);
326
327         ok_bt = GTK_FILE_SELECTION(rtpstream_save_dlg)->ok_button;
328         SIGNAL_CONNECT(ok_bt, "clicked", save_stream_ok_cb, tapinfo);
329
330         window_set_cancel_button(rtpstream_save_dlg,
331             GTK_FILE_SELECTION(rtpstream_save_dlg)->cancel_button, window_cancel_button_cb);
332
333         SIGNAL_CONNECT(rtpstream_save_dlg, "delete_event", window_delete_event_cb, NULL);
334         SIGNAL_CONNECT(rtpstream_save_dlg, "destroy", save_stream_destroy_cb,
335                        NULL);
336
337         gtk_widget_show(rtpstream_save_dlg);
338         window_present(rtpstream_save_dlg);
339 }
340
341
342 /****************************************************************************/
343 static void
344 rtpstream_on_mark                      (GtkButton       *button _U_,
345                                         gpointer         user_data _U_)
346 {
347         if (selected_stream_fwd==NULL && selected_stream_rev==NULL)
348                 return;
349         rtpstream_mark(selected_stream_fwd, selected_stream_rev);
350 }
351
352
353 /****************************************************************************/
354 static void
355 rtpstream_on_filter                    (GtkButton       *button _U_,
356                                         gpointer         user_data _U_)
357 {
358         gchar *filter_string = NULL;
359         gchar *filter_string_fwd = NULL;
360         gchar *filter_string_rev = NULL;
361         gchar ip_version[3];
362
363         if (selected_stream_fwd==NULL && selected_stream_rev==NULL)
364                 return;
365
366         if (selected_stream_fwd)
367         {
368                 if (selected_stream_fwd->src_addr.type==AT_IPv6){
369                         strcpy(ip_version,"v6");
370                 }               
371                 else{
372                         strcpy(ip_version,"");
373                 }
374                 filter_string_fwd = g_strdup_printf(
375                         "(ip%s.src==%s && udp.srcport==%u && ip%s.dst==%s && udp.dstport==%u && rtp.ssrc==%u)",
376                         ip_version,
377                         address_to_str(&(selected_stream_fwd->src_addr)),
378                         selected_stream_fwd->src_port,
379                         ip_version,
380                         address_to_str(&(selected_stream_fwd->dest_addr)),
381                         selected_stream_fwd->dest_port,
382                         selected_stream_fwd->ssrc);
383         filter_string = filter_string_fwd;
384         }
385
386         if (selected_stream_rev)
387         {
388                 if (selected_stream_fwd->src_addr.type==AT_IPv6){
389                         strcpy(ip_version,"v6");
390                 }               
391                 else{
392                         strcpy(ip_version,"");
393                 }
394                 filter_string_rev = g_strdup_printf(
395                         "(ip%s.src==%s && udp.srcport==%u && ip%s.dst==%s && udp.dstport==%u && rtp.ssrc==%u)",
396                         ip_version,
397                         address_to_str(&(selected_stream_rev->src_addr)),
398                         selected_stream_rev->src_port,
399                         ip_version,
400                         address_to_str(&(selected_stream_rev->dest_addr)),
401                         selected_stream_rev->dest_port,
402                         selected_stream_rev->ssrc);
403
404                 filter_string = filter_string_rev;
405
406             if (selected_stream_fwd)
407             {
408             filter_string = g_strdup_printf("%s || %s", filter_string, filter_string_rev);
409             g_free(filter_string_fwd);
410             g_free(filter_string_rev);
411         }
412     }
413
414     gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), filter_string);
415     g_free(filter_string);
416
417 /*
418         main_filter_packets(&cfile, filter_string, FALSE);
419         rtpstream_dlg_update(rtpstream_get_info()->strinfo_list);
420 */
421 }
422
423
424 /****************************************************************************/
425 #if (GTK_MAJOR_VERSION >= 2)
426 static void
427 rtpstream_on_copy_as_csv(GtkWindow *win _U_, gpointer data _U_)
428 {
429         int             i,j;
430         gchar           *table_entry;
431         GString         *CSV_str;
432         GtkClipboard    *cb;
433
434         CSV_str = g_string_sized_new(240*(GTK_CLIST(clist)->rows+1));
435         /* Add the column headers to the CSV data */
436         for (j=0; j<NUM_COLS; j++) {
437                 g_string_append(CSV_str, titles[j]);
438                 g_string_append(CSV_str, ",");
439         }
440         g_string_append(CSV_str,"\n");
441
442         /* Add the column values to the CSV data */
443         for (i=0; i<GTK_CLIST(clist)->rows; i++) {
444                 for (j=0; j<NUM_COLS; j++) {
445                         gtk_clist_get_text(GTK_CLIST(clist),i,j,&table_entry);
446                         g_string_append(CSV_str,table_entry);
447                         g_string_append(CSV_str,",");
448                 } 
449                 g_string_append(CSV_str,"\n");
450         }
451
452         /* Now that we have the CSV data, copy it into the default clipboard */
453         cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
454         gtk_clipboard_set_text(cb, CSV_str->str, CSV_str->len);
455         g_string_free(CSV_str, TRUE);
456 }
457 #endif
458
459 /****************************************************************************/
460 static void
461 rtpstream_on_analyse                   (GtkButton       *button _U_,
462                                         gpointer         user_data _U_)
463
464         address ip_src_fwd;
465         guint16 port_src_fwd = 0;
466         address ip_dst_fwd;
467         guint16 port_dst_fwd = 0;
468         guint32 ssrc_fwd = 0;
469         address ip_src_rev;
470         guint16 port_src_rev = 0;
471         address ip_dst_rev;
472         guint16 port_dst_rev = 0;
473         guint32 ssrc_rev = 0;
474
475         if (!(selected_stream_fwd || selected_stream_rev))
476         {
477                 return;
478         }
479         
480         SET_ADDRESS(&ip_src_fwd,AT_NONE,0,NULL);
481         SET_ADDRESS(&ip_dst_fwd,AT_NONE,0,NULL);
482         SET_ADDRESS(&ip_src_rev,AT_NONE,0,NULL);
483         SET_ADDRESS(&ip_dst_rev,AT_NONE,0,NULL);
484         
485         if (selected_stream_fwd) {
486                 COPY_ADDRESS(&(ip_src_fwd), &(selected_stream_fwd->src_addr));
487                 port_src_fwd = selected_stream_fwd->src_port;
488                 COPY_ADDRESS(&(ip_dst_fwd), &(selected_stream_fwd->dest_addr));
489                 port_dst_fwd = selected_stream_fwd->dest_port;
490                 ssrc_fwd = selected_stream_fwd->ssrc;
491         }
492
493         if (selected_stream_rev) {
494                 COPY_ADDRESS(&(ip_src_rev), &(selected_stream_rev->src_addr));
495                 port_src_rev = selected_stream_rev->src_port;
496                 COPY_ADDRESS(&(ip_dst_rev), &(selected_stream_rev->dest_addr));
497                 port_dst_rev = selected_stream_rev->dest_port;
498                 ssrc_rev = selected_stream_rev->ssrc;
499         }
500
501         rtp_analysis(
502                 &ip_src_fwd,
503                 port_src_fwd,
504                 &ip_dst_fwd,
505                 port_dst_fwd,
506                 ssrc_fwd,
507                 &ip_src_rev,
508                 port_src_rev,
509                 &ip_dst_rev,
510                 port_dst_rev,
511                 ssrc_rev
512                 );
513
514 }
515
516
517 /****************************************************************************/
518 /* when the user selects a row in the stream list */
519 static void
520 rtpstream_on_select_row(GtkCList *clist,
521                                             gint row _U_,
522                                             gint column _U_,
523                                             GdkEventButton *event _U_,
524                                             gpointer user_data _U_)
525 {
526         gchar label_text[80];
527
528         /* update the labels */
529         if (event==NULL || event->state & GDK_SHIFT_MASK) {
530                 selected_stream_rev = gtk_clist_get_row_data(GTK_CLIST(clist), row);
531                 g_snprintf(label_text, 80, "Reverse: %s:%u -> %s:%u, SSRC=%u",
532                         get_addr_name(&(selected_stream_rev->src_addr)),
533                         selected_stream_rev->src_port,
534                         get_addr_name(&(selected_stream_rev->dest_addr)),
535                         selected_stream_rev->dest_port,
536                         selected_stream_rev->ssrc
537                 );
538                 gtk_label_set_text(GTK_LABEL(label_rev), label_text);
539         }
540         else {
541                 selected_stream_fwd = gtk_clist_get_row_data(GTK_CLIST(clist), row);
542                 g_snprintf(label_text, 80, "Forward: %s:%u -> %s:%u, SSRC=%u",
543                         get_addr_name(&(selected_stream_fwd->src_addr)),
544                         selected_stream_fwd->src_port,
545                         get_addr_name(&(selected_stream_fwd->dest_addr)),
546                         selected_stream_fwd->dest_port,
547                         selected_stream_fwd->ssrc
548                 );
549                 gtk_label_set_text(GTK_LABEL(label_fwd), label_text);
550         }
551
552 /*
553         gtk_widget_set_sensitive(save_bt, TRUE);
554         gtk_widget_set_sensitive(filter_bt, TRUE);
555         gtk_widget_set_sensitive(mark_bt, TRUE);
556 */
557         /* TODO: activate other buttons when implemented */
558 }
559
560
561 /****************************************************************************/
562 typedef struct column_arrows {
563         GtkWidget *table;
564         GtkWidget *ascend_pm;
565         GtkWidget *descend_pm;
566 } column_arrows;
567
568
569 /****************************************************************************/
570 static void
571 rtpstream_click_column_cb(GtkCList *clist, gint column, gpointer data)
572 {
573         column_arrows *col_arrows = (column_arrows *) data;
574         int i;
575
576         gtk_clist_freeze(clist);
577
578         for (i=0; i<NUM_COLS; i++) {
579                 gtk_widget_hide(col_arrows[i].ascend_pm);
580                 gtk_widget_hide(col_arrows[i].descend_pm);
581         }
582
583         if (column == clist->sort_column) {
584                 if (clist->sort_type == GTK_SORT_ASCENDING) {
585                         clist->sort_type = GTK_SORT_DESCENDING;
586                         gtk_widget_show(col_arrows[column].descend_pm);
587                 } else {
588                         clist->sort_type = GTK_SORT_ASCENDING;
589                         gtk_widget_show(col_arrows[column].ascend_pm);
590                 }
591         } else {
592                 clist->sort_type = GTK_SORT_ASCENDING;
593                 gtk_widget_show(col_arrows[column].ascend_pm);
594                 gtk_clist_set_sort_column(clist, column);
595         }
596         gtk_clist_thaw(clist);
597
598         gtk_clist_sort(clist);
599 }
600
601
602 /****************************************************************************/
603 static gint
604 rtpstream_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
605 {
606         char *text1 = NULL;
607         char *text2 = NULL;
608         int i1, i2;
609
610         const GtkCListRow *row1 = (const GtkCListRow *) ptr1;
611         const GtkCListRow *row2 = (const GtkCListRow *) ptr2;
612
613         text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
614         text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
615
616         switch(clist->sort_column){
617         case 0:
618         case 2:
619         case 5:
620         case 11:
621                 return strcmp (text1, text2);
622         case 1:
623         case 3:
624         case 4:
625         case 6:
626         case 7:
627         case 8:
628         case 9:
629         case 10:
630                 i1=atoi(text1);
631                 i2=atoi(text2);
632                 return i1-i2;
633         }
634         g_assert_not_reached();
635         return 0;
636 }
637
638
639 /****************************************************************************/
640 /* INTERFACE                                                                */
641 /****************************************************************************/
642
643 static void rtpstream_dlg_create (void)
644 {
645     GtkWidget *rtpstream_dlg_w;
646     GtkWidget *main_vb;
647     GtkWidget *scrolledwindow;
648     GtkWidget *hbuttonbox;
649 /*    GtkWidget *bt_goto;*/
650     GtkWidget *bt_unselect;
651     GtkWidget *bt_findrev;
652     GtkWidget *bt_save;
653     GtkWidget *bt_mark;
654     GtkWidget *bt_filter;
655     GtkWidget *bt_analyze;
656     GtkWidget *bt_close;
657 #if (GTK_MAJOR_VERSION >= 2)
658     GtkWidget *bt_copy;
659 #endif           
660     GtkTooltips *tooltips = gtk_tooltips_new();
661
662     column_arrows *col_arrows;
663     GtkWidget *column_lb;
664     int i;
665
666     rtpstream_dlg_w = dlg_window_new("Wireshark: RTP Streams");
667     gtk_window_set_default_size(GTK_WINDOW(rtpstream_dlg_w), 620, 200);
668
669     main_vb = gtk_vbox_new (FALSE, 0);
670     gtk_container_add(GTK_CONTAINER(rtpstream_dlg_w), main_vb);
671     gtk_container_set_border_width (GTK_CONTAINER (main_vb), 12);
672
673     top_label = gtk_label_new ("Detected 0 RTP streams. Choose one for forward and reverse direction for analysis");
674     gtk_box_pack_start (GTK_BOX (main_vb), top_label, FALSE, FALSE, 8);
675
676     scrolledwindow = scrolled_window_new (NULL, NULL);
677     gtk_box_pack_start (GTK_BOX (main_vb), scrolledwindow, TRUE, TRUE, 0);
678
679     clist = gtk_clist_new (NUM_COLS);
680     gtk_container_add (GTK_CONTAINER (scrolledwindow), clist);
681
682     gtk_clist_set_column_width (GTK_CLIST (clist), 0, 88);
683     gtk_clist_set_column_width (GTK_CLIST (clist), 1, 44);
684     gtk_clist_set_column_width (GTK_CLIST (clist), 2, 88);
685     gtk_clist_set_column_width (GTK_CLIST (clist), 3, 44);
686     gtk_clist_set_column_width (GTK_CLIST (clist), 4, 64);
687     gtk_clist_set_column_width (GTK_CLIST (clist), 5, 96);
688     gtk_clist_set_column_width (GTK_CLIST (clist), 6, 50);
689     gtk_clist_set_column_width (GTK_CLIST (clist), 7, 50);
690     gtk_clist_set_column_width (GTK_CLIST (clist), 8, 80);
691     gtk_clist_set_column_width (GTK_CLIST (clist), 9, 80);
692     gtk_clist_set_column_width (GTK_CLIST (clist), 10, 80);
693     gtk_clist_set_column_width (GTK_CLIST (clist), 11, 40);
694
695     gtk_clist_set_column_justification(GTK_CLIST(clist), 0, GTK_JUSTIFY_CENTER);
696     gtk_clist_set_column_justification(GTK_CLIST(clist), 1, GTK_JUSTIFY_CENTER);
697     gtk_clist_set_column_justification(GTK_CLIST(clist), 2, GTK_JUSTIFY_CENTER);
698     gtk_clist_set_column_justification(GTK_CLIST(clist), 3, GTK_JUSTIFY_CENTER);
699     gtk_clist_set_column_justification(GTK_CLIST(clist), 4, GTK_JUSTIFY_CENTER);
700     gtk_clist_set_column_justification(GTK_CLIST(clist), 5, GTK_JUSTIFY_LEFT);
701     gtk_clist_set_column_justification(GTK_CLIST(clist), 6, GTK_JUSTIFY_RIGHT);
702     gtk_clist_set_column_justification(GTK_CLIST(clist), 7, GTK_JUSTIFY_RIGHT);
703     gtk_clist_set_column_justification(GTK_CLIST(clist), 8, GTK_JUSTIFY_RIGHT);
704     gtk_clist_set_column_justification(GTK_CLIST(clist), 9, GTK_JUSTIFY_RIGHT);
705     gtk_clist_set_column_justification(GTK_CLIST(clist), 10, GTK_JUSTIFY_RIGHT);
706     gtk_clist_set_column_justification(GTK_CLIST(clist), 11, GTK_JUSTIFY_LEFT);
707
708     gtk_clist_column_titles_show (GTK_CLIST (clist));
709
710     gtk_clist_set_compare_func(GTK_CLIST(clist), rtpstream_sort_column);
711     gtk_clist_set_sort_column(GTK_CLIST(clist), 0);
712     gtk_clist_set_sort_type(GTK_CLIST(clist), GTK_SORT_ASCENDING);
713
714     gtk_widget_show(rtpstream_dlg_w);
715
716     /* sort by column feature */
717     col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS);
718
719     for (i=0; i<NUM_COLS; i++) {
720         col_arrows[i].table = gtk_table_new(2, 2, FALSE);
721         gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
722         column_lb = gtk_label_new(titles[i]);
723         gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
724         gtk_widget_show(column_lb);
725
726         col_arrows[i].ascend_pm = xpm_to_widget(clist_ascend_xpm);
727         gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
728         col_arrows[i].descend_pm = xpm_to_widget(clist_descend_xpm);
729         gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
730         /* make src-ip be the default sort order */
731         if (i == 0) {
732             gtk_widget_show(col_arrows[i].ascend_pm);
733         }
734         gtk_clist_set_column_widget(GTK_CLIST(clist), i, col_arrows[i].table);
735         gtk_widget_show(col_arrows[i].table);
736     }
737
738     SIGNAL_CONNECT(clist, "click-column", rtpstream_click_column_cb, col_arrows);
739
740     label_fwd = gtk_label_new (FWD_LABEL_TEXT);
741     gtk_box_pack_start (GTK_BOX (main_vb), label_fwd, FALSE, FALSE, 0);
742
743     label_rev = gtk_label_new (REV_LABEL_TEXT);
744     gtk_box_pack_start (GTK_BOX (main_vb), label_rev, FALSE, FALSE, 0);
745
746     /* button row */
747     hbuttonbox = gtk_hbutton_box_new ();
748     gtk_box_pack_start (GTK_BOX (main_vb), hbuttonbox, FALSE, FALSE, 0);
749     gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_END);
750     gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbuttonbox), 0);
751
752     bt_unselect = gtk_button_new_with_label ("Unselect");
753     gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_unselect);
754     gtk_tooltips_set_tip (tooltips, bt_unselect, "Undo stream selection", NULL);
755
756     bt_findrev = gtk_button_new_with_label ("Find Reverse");
757     gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_findrev);
758     gtk_tooltips_set_tip (tooltips, bt_findrev, "Find the reverse stream matching the selected forward stream", NULL);
759 /*
760     bt_goto = BUTTON_NEW_FROM_STOCK(GTK_STOCK_JUMP_TO);
761     gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_goto);
762 */
763     bt_save = BUTTON_NEW_FROM_STOCK(GTK_STOCK_SAVE_AS);
764     gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_save);
765     gtk_tooltips_set_tip (tooltips, bt_save, "Save stream payload in rtpdump format", NULL);
766
767     bt_mark = gtk_button_new_with_label ("Mark Packets");
768     gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_mark);
769     gtk_tooltips_set_tip (tooltips, bt_mark, "Mark packets of the selected stream(s)", NULL);
770
771     bt_filter = gtk_button_new_with_label ("Prepare Filter");
772     gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_filter);
773     gtk_tooltips_set_tip (tooltips, bt_filter, "Prepare a display filter of the selected stream(s)", NULL);
774
775 #if (GTK_MAJOR_VERSION >= 2)
776     /* XXX - maybe we want to have a "Copy as CSV" stock button here? */
777     /*bt_copy = gtk_button_new_with_label ("Copy content to clipboard as CSV");*/
778     bt_copy = BUTTON_NEW_FROM_STOCK(GTK_STOCK_COPY);
779     gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_copy);
780     gtk_tooltips_set_tip(tooltips, bt_copy, 
781         "Copy all statistical values of this page to the clipboard in CSV (Comma Seperated Values) format.", NULL);
782 #endif                 
783
784     bt_analyze = gtk_button_new_with_label ("Analyze");
785     gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_analyze);
786     gtk_tooltips_set_tip (tooltips, bt_analyze, "Open an analyze window of the selected stream(s)", NULL);
787
788     bt_close = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
789     gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_close);
790     gtk_tooltips_set_tip (tooltips, bt_close, "Close this dialog", NULL);
791     GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
792
793     SIGNAL_CONNECT(clist, "select_row", rtpstream_on_select_row, NULL);
794     SIGNAL_CONNECT(bt_unselect, "clicked", rtpstream_on_unselect, NULL);
795     SIGNAL_CONNECT(bt_findrev, "clicked", rtpstream_on_findrev, NULL);
796 /*
797     SIGNAL_CONNECT(bt_goto, "clicked", rtpstream_on_goto, NULL);
798 */
799     SIGNAL_CONNECT(bt_save, "clicked", rtpstream_on_save, NULL);
800     SIGNAL_CONNECT(bt_mark, "clicked", rtpstream_on_mark, NULL);
801     SIGNAL_CONNECT(bt_filter, "clicked", rtpstream_on_filter, NULL);
802 #if (GTK_MAJOR_VERSION >= 2)
803     SIGNAL_CONNECT(bt_copy, "clicked", rtpstream_on_copy_as_csv, NULL);
804 #endif
805     SIGNAL_CONNECT(bt_analyze, "clicked", rtpstream_on_analyse, NULL);
806
807     window_set_cancel_button(rtpstream_dlg_w, bt_close, window_cancel_button_cb);
808
809     SIGNAL_CONNECT(rtpstream_dlg_w, "delete_event", window_delete_event_cb, NULL);
810     SIGNAL_CONNECT(rtpstream_dlg_w, "destroy", rtpstream_on_destroy, NULL);
811
812     gtk_widget_show_all(rtpstream_dlg_w);
813     window_present(rtpstream_dlg_w);
814
815     rtpstream_on_unselect(NULL, NULL);
816
817     rtp_stream_dlg = rtpstream_dlg_w;
818 }
819
820
821 /****************************************************************************/
822 /* PUBLIC                                                                   */
823 /****************************************************************************/
824
825 /****************************************************************************/
826 /* update the contents of the dialog box clist */
827 /* list: pointer to list of rtp_stream_info_t* */
828 void rtpstream_dlg_update(GList *list)
829 {
830         if (rtp_stream_dlg != NULL) {
831                 gtk_clist_clear(GTK_CLIST(clist));
832                 streams_nb = 0;
833
834                 list = g_list_first(list);
835                 while (list)
836                 {
837                         add_to_clist((rtp_stream_info_t*)(list->data));
838                         list = g_list_next(list);
839                 }
840
841                 rtpstream_on_unselect(NULL, NULL);
842         }
843
844         last_list = list;
845 }
846
847
848 /****************************************************************************/
849 /* update the contents of the dialog box clist */
850 /* list: pointer to list of rtp_stream_info_t* */
851 void rtpstream_dlg_show(GList *list)
852 {
853         if (rtp_stream_dlg != NULL) {
854                 /* There's already a dialog box; reactivate it. */
855                 reactivate_window(rtp_stream_dlg);
856                 /* Another list since last call? */
857                 if (list != last_list) {
858                         rtpstream_dlg_update(list);
859                 }
860         }
861         else {
862                 /* Create and show the dialog box */
863                 rtpstream_dlg_create();
864                 rtpstream_dlg_update(list);
865         }
866 }
867
868
869 /****************************************************************************/
870 /* entry point when called via the GTK menu */
871 static void rtpstream_launch(GtkWidget *w _U_, gpointer data _U_)
872 {
873         /* Register the tap listener */
874         register_tap_listener_rtp_stream();
875
876         /* Scan for RTP streams (redissect all packets) */
877         rtpstream_scan();
878
879         /* Show the dialog box with the list of streams */
880         rtpstream_dlg_show(rtpstream_get_info()->strinfo_list);
881
882         /* Tap listener will be removed and cleaned up in rtpstream_on_destroy */
883 }
884
885 /****************************************************************************/
886 void
887 register_tap_listener_rtp_stream_dlg(void)
888 {
889         register_stat_menu_item("RTP/Show All Streams", REGISTER_STAT_GROUP_TELEPHONY,
890             rtpstream_launch, NULL, NULL, NULL);
891 }