from Lars Ruoff
[obnox/wireshark/wip.git] / gtk / rtp_analysis.c
1 /* rtp_analysis.c
2  * RTP analysis addition for ethereal
3  *
4  * $Id$
5  *
6  * Copyright 2003, Alcatel Business Systems
7  * By Lars Ruoff <lars.ruoff@gmx.net>
8  *
9  * based on tap_rtp.c
10  * Copyright 2003, Iskratel, Ltd, Kranj
11  * By Miha Jemec <m.jemec@iskratel.si>
12  *
13  * Graph. Copyright 2004, Verso Technology
14  * By Alejandro Vaquero <alejandro.vaquero@verso.com>
15  * Based on io_stat.c by Ronnie Sahlberg 
16  *
17  * Ethereal - Network traffic analyzer
18  * By Gerald Combs <gerald@ethereal.com>
19  * Copyright 1998 Gerald Combs
20  *
21  * This program is free software; you can redistribute it and/or
22  * modify it under the terms of the GNU General Public License
23  * as published by the Free Software Foundation; either version 2
24  * of the License, or (at your option) any later version.
25  *
26  * This program is distributed in the hope that it will be useful,
27  * but WITHOUT ANY WARRANTY; without even the implied warranty of
28  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29  * GNU General Public License for more details.
30  *
31  * You should have received a copy of the GNU General Public License
32  * along with this program; if not, write to the Free Software
33  * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
34  */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 /*do not define this symbol. will be added soon*/
41 /*#define USE_CONVERSATION_GRAPH 1*/
42
43 #include "rtp_analysis.h"
44 #include "rtp_stream.h"
45 #include "rtp_stream_dlg.h"
46
47 #ifdef USE_CONVERSATION_GRAPH
48 #include "../graph/graph.h"
49 #endif
50
51 #include <epan/epan_dissect.h>
52 #include <epan/filesystem.h>
53
54 #include "util.h"
55 #include <epan/tap.h>
56 #include "register.h"
57 #include <epan/dissectors/packet-rtp.h>
58 #include "g711.h"
59 #include "rtp_pt.h"
60 #include <epan/addr_resolv.h>
61
62 /* in /gtk ... */
63 #include <gtk/gtk.h>
64 #include "gtkglobals.h"
65
66 #include "dlg_utils.h"
67 #include "ui_util.h"
68 #include "alert_box.h"
69 #include "simple_dialog.h"
70 #include "tap_menu.h"
71 #include "main.h"
72 #include "progress_dlg.h"
73 #include "compat_macros.h"
74 #include "../color.h"
75
76 #include "image/clist_ascend.xpm"
77 #include "image/clist_descend.xpm"
78
79 #include <math.h>
80 #include <fcntl.h>
81 #include <string.h>
82 #include <locale.h>
83
84 #ifdef HAVE_UNISTD_H
85 #include <unistd.h>
86 #endif
87
88 #ifdef HAVE_FCNTL_H
89 #include <fcntl.h>
90 #endif
91
92 #ifdef HAVE_IO_H
93 #include <io.h> /* open/close on win32 */
94 #endif
95
96 /* Win32 needs the O_BINARY flag for open() */
97 #ifndef O_BINARY
98 #define O_BINARY 0
99 #endif
100
101 /****************************************************************************/
102
103 typedef struct column_arrows {
104         GtkWidget *table;
105         GtkWidget *ascend_pm;
106         GtkWidget *descend_pm;
107 } column_arrows;
108
109 #define NUM_COLS 9
110 #define NUM_GRAPH_ITEMS 100000
111 #define MAX_YSCALE 16
112 #define AUTO_MAX_YSCALE 0
113 #define MAX_GRAPHS 4
114 #define GRAPH_FWD_JITTER 0
115 #define GRAPH_FWD_DIFF 1
116 #define GRAPH_REV_JITTER 2
117 #define GRAPH_REV_DIFF 3
118 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
119
120 #define MAX_PIXELS_PER_TICK 4
121 #define DEFAULT_PIXELS_PER_TICK 1
122 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
123 static char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
124 /* unit is in ms */
125 #define MAX_TICK_VALUES 5
126 #define DEFAULT_TICK_VALUE 1
127 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
128 typedef struct _dialog_graph_graph_item_t {
129         guint32 value;
130         guint32 flags;
131 } dialog_graph_graph_item_t;
132
133 typedef struct _dialog_graph_graph_t {
134         struct _user_data_t *ud;        
135         dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
136         int plot_style;
137         gboolean display;
138         GtkWidget *display_button;
139         int hf_index;
140         GdkColor color;
141         GdkGC *gc;
142         gchar title[100];
143 } dialog_graph_graph_t;
144
145
146 typedef struct _dialog_graph_t {
147         gboolean needs_redraw;
148         gint32 interval;    /* measurement interval in ms */
149         guint32 last_interval;
150         guint32 max_interval; /* XXX max_interval and num_items are redundant */
151         guint32 num_items;
152         struct _dialog_graph_graph_t graph[MAX_GRAPHS];
153         GtkWidget *window;
154         GtkWidget *draw_area;
155         GdkPixmap *pixmap;
156         GtkAdjustment *scrollbar_adjustment;
157         GtkWidget *scrollbar;
158         int pixmap_width;
159         int pixmap_height;
160         int pixels_per_tick;
161         int max_y_units;
162         double start_time;
163 } dialog_graph_t;       
164
165 typedef struct _dialog_data_t {
166         GtkWidget *window;
167         GtkCList *clist_fwd;
168         GtkCList *clist_rev;
169         GtkWidget *label_stats_fwd;
170         GtkWidget *label_stats_rev;
171         column_arrows *col_arrows_fwd;
172         column_arrows *col_arrows_rev;
173         GtkWidget *notebook;
174         GtkCList *selected_clist;
175         GtkWidget *save_voice_as_w;
176         GtkWidget *save_csv_as_w;
177         gint notebook_signal_id;
178         gint selected_row;
179         dialog_graph_t dialog_graph;
180 #ifdef USE_CONVERSATION_GRAPH
181         GtkWidget *graph_window;
182 #endif
183 } dialog_data_t;
184
185 #define OK_TEXT "[ Ok ]"
186
187 typedef struct _key_value {
188   guint32  key;
189   guint32  value;
190 } key_value;
191
192
193 /* RTP sampling clock rates for fixed payload types as defined in
194  http://www.iana.org/assignments/rtp-parameters */
195 static const key_value clock_map[] = {
196         {PT_PCMU,       8000},
197         {PT_1016,       8000},
198         {PT_G721,       8000},
199         {PT_GSM,        8000},
200         {PT_G723,       8000},
201         {PT_DVI4_8000,  8000},
202         {PT_DVI4_16000, 16000},
203         {PT_LPC,        8000},
204         {PT_PCMA,       8000},
205         {PT_G722,       8000},
206         {PT_L16_STEREO, 44100},
207         {PT_L16_MONO,   44100},
208         {PT_QCELP,      8000},
209         {PT_CN,         8000},
210         {PT_MPA,        90000},
211         {PT_G728,       8000},
212         {PT_G728,       8000},
213         {PT_DVI4_11025, 11025},
214         {PT_DVI4_22050, 22050},
215         {PT_G729,       8000},
216         {PT_CN_OLD,     8000},
217         {PT_CELB,       90000},
218         {PT_JPEG,       90000},
219         {PT_NV,         90000},
220         {PT_H261,       90000},
221         {PT_MPV,        90000},
222         {PT_MP2T,       90000},
223         {PT_H263,       90000},
224 };
225
226 #define NUM_CLOCK_VALUES        (sizeof clock_map / sizeof clock_map[0])
227
228 static guint32
229 get_clock_rate(guint32 key)
230 {
231         size_t i;
232
233         for (i = 0; i < NUM_CLOCK_VALUES; i++) {
234                 if (clock_map[i].key == key)
235                         return clock_map[i].value;
236         }
237         return 1;
238 }
239
240
241 /* type of error when saving voice in a file didn't succeed */
242 typedef enum {
243         TAP_RTP_WRONG_CODEC,
244         TAP_RTP_WRONG_LENGTH,
245         TAP_RTP_PADDING_ERROR,
246         TAP_RTP_SHORT_FRAME,
247         TAP_RTP_FILE_OPEN_ERROR,
248         TAP_RTP_NO_DATA
249 } error_type_t; 
250
251 #if GTK_MAJOR_VERSION < 2
252 GtkRcStyle *rc_style;
253 GdkColormap *colormap;
254 #endif
255
256 typedef struct _tap_rtp_save_info_t {
257         FILE *fp;
258         guint32 count;
259         error_type_t error_type;
260         gboolean saved;
261 } tap_rtp_save_info_t;
262
263
264 /* structure that holds the information about the forward and reversed direction */
265 struct _info_direction {
266         tap_rtp_stat_t statinfo;
267         tap_rtp_save_info_t saveinfo;
268 };
269
270 #define TMPNAMSIZE 100
271
272 /* structure that holds general information about the connection 
273 * and structures for both directions */
274 typedef struct _user_data_t {
275         /* tap associated data*/
276         address ip_src_fwd;
277         guint16 port_src_fwd;
278         address ip_dst_fwd;
279         guint16 port_dst_fwd;
280         guint32 ssrc_fwd;
281         address ip_src_rev;
282         guint16 port_src_rev;
283         address ip_dst_rev;
284         guint16 port_dst_rev;
285         guint32 ssrc_rev;
286
287         struct _info_direction forward;
288         struct _info_direction reversed;
289
290         char f_tempname[TMPNAMSIZE];
291         char r_tempname[TMPNAMSIZE];
292
293         /* dialog associated data */
294         dialog_data_t dlg;
295
296 #ifdef USE_CONVERSATION_GRAPH
297         time_series_t series_fwd;
298         time_series_t series_rev;
299 #endif
300 } user_data_t;
301
302
303 /* Column titles. */
304 static gchar *titles[9] =  {
305         "Packet",
306         "Sequence",
307         "Delta (ms)",
308         "Jitter (ms)",
309         "IP BW (kbps)",
310         "Marker",
311         "Status",
312         "Date",
313         "Length"
314 };
315
316
317 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_);
318 /****************************************************************************/
319 static void enable_graph(dialog_graph_graph_t *dgg)
320 {
321         
322         dgg->display=TRUE;
323
324 }
325
326 static void dialog_graph_reset(user_data_t* user_data);
327
328
329
330 /****************************************************************************/
331 /* TAP FUNCTIONS */
332
333 /****************************************************************************/
334 /* when there is a [re]reading of packet's */
335 static void
336 rtp_reset(void *user_data_arg)
337 {
338         user_data_t *user_data = user_data_arg;
339         user_data->forward.statinfo.first_packet = TRUE;
340         user_data->reversed.statinfo.first_packet = TRUE;
341         user_data->forward.statinfo.max_delta = 0;
342         user_data->reversed.statinfo.max_delta = 0;
343         user_data->forward.statinfo.max_jitter = 0;
344         user_data->reversed.statinfo.max_jitter = 0;
345         user_data->forward.statinfo.mean_jitter = 0;
346         user_data->reversed.statinfo.mean_jitter = 0;
347         user_data->forward.statinfo.delta = 0;
348         user_data->reversed.statinfo.delta = 0;
349         user_data->forward.statinfo.diff = 0;
350         user_data->reversed.statinfo.diff = 0;
351         user_data->forward.statinfo.jitter = 0;
352         user_data->reversed.statinfo.jitter = 0;
353         user_data->forward.statinfo.bandwidth = 0;
354         user_data->reversed.statinfo.bandwidth = 0;
355         user_data->forward.statinfo.total_bytes = 0;
356         user_data->reversed.statinfo.total_bytes = 0;
357         user_data->forward.statinfo.bw_start_index = 0;
358         user_data->reversed.statinfo.bw_start_index = 0;
359         user_data->forward.statinfo.bw_index = 0;
360         user_data->reversed.statinfo.bw_index = 0;
361         user_data->forward.statinfo.timestamp = 0;
362         user_data->reversed.statinfo.timestamp = 0;
363         user_data->forward.statinfo.max_nr = 0;
364         user_data->reversed.statinfo.max_nr = 0;
365         user_data->forward.statinfo.total_nr = 0;
366         user_data->reversed.statinfo.total_nr = 0;
367         user_data->forward.statinfo.sequence = 0;
368         user_data->reversed.statinfo.sequence = 0;
369         user_data->forward.statinfo.start_seq_nr = 0;
370         user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
371         user_data->forward.statinfo.stop_seq_nr = 0;
372         user_data->reversed.statinfo.stop_seq_nr = 0;
373         user_data->forward.statinfo.cycles = 0;
374         user_data->reversed.statinfo.cycles = 0;
375         user_data->forward.statinfo.under = FALSE;
376         user_data->reversed.statinfo.under = FALSE;
377         user_data->forward.statinfo.start_time = 0;
378         user_data->reversed.statinfo.start_time = 0;
379         user_data->forward.statinfo.time = 0;
380         user_data->reversed.statinfo.time = 0;
381         user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
382         user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
383
384         user_data->forward.saveinfo.count = 0;
385         user_data->reversed.saveinfo.count = 0;
386         user_data->forward.saveinfo.saved = FALSE;
387         user_data->reversed.saveinfo.saved = FALSE;
388
389         /* clear the dialog box clists */
390         gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_fwd));
391         gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_rev));
392
393         /* reset graph info */
394         dialog_graph_reset(user_data);
395
396 #ifdef USE_CONVERSATION_GRAPH
397         if (user_data->dlg.graph_window != NULL)
398                 window_destroy(user_data->dlg.graph_window);
399         
400         g_array_free(user_data->series_fwd.value_pairs, TRUE);
401         user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
402
403         g_array_free(user_data->series_rev.value_pairs, TRUE);
404         user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
405 #endif
406
407         /* XXX check for error at fclose? */
408         if (user_data->forward.saveinfo.fp != NULL)
409                 fclose(user_data->forward.saveinfo.fp); 
410         if (user_data->reversed.saveinfo.fp != NULL)
411                 fclose(user_data->reversed.saveinfo.fp); 
412         user_data->forward.saveinfo.fp = fopen(user_data->f_tempname, "wb"); 
413         if (user_data->forward.saveinfo.fp == NULL)
414                 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
415         user_data->reversed.saveinfo.fp = fopen(user_data->r_tempname, "wb");
416         if (user_data->reversed.saveinfo.fp == NULL)
417                 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
418         return;
419 }
420
421 /****************************************************************************/
422 static int rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
423 {
424         dialog_graph_graph_item_t *it;
425         int idx;
426         double rtp_time;
427
428         /* we sometimes get called when dgg is disabled.
429         this is a bug since the tap listener should be removed first */
430         if(!dgg->display){
431                 return 0;
432         }
433
434         dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
435
436         /*
437         * Find which interval this is supposed to to in and store the
438         * interval index as idx
439         */
440         if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
441                 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
442         }
443         rtp_time = ((double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000) - dgg->ud->dlg.dialog_graph.start_time;
444         if(rtp_time<0){
445                 return FALSE;
446         }
447         idx = (guint32)(rtp_time*1000)/dgg->ud->dlg.dialog_graph.interval;
448
449         /* some sanity checks */
450         if((idx<0)||(idx>=NUM_GRAPH_ITEMS)){
451                 return FALSE;
452         }
453
454         /* update num_items */
455         if((guint32)idx > dgg->ud->dlg.dialog_graph.num_items){
456                 dgg->ud->dlg.dialog_graph.num_items=idx;
457                 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
458         }
459
460         /*
461         * Find the appropriate dialog_graph_graph_item_t structure
462         */
463         it=&dgg->items[idx];
464
465         /*
466         * Use the max value to highlight RTP problems
467         */
468         if (value > it->value) {
469                 it->value=value;
470         }
471         it->flags = it->flags | statinfo->flags;
472
473         return TRUE;
474 }
475
476 /****************************************************************************/
477 /* here we can redraw the output */
478 /* not used yet */
479 static void rtp_draw(void *prs _U_)
480 {
481         return;
482 }
483
484 /* forward declarations */
485 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
486                          double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
487                          gchar *timeStr, guint32 pkt_len, GdkColor *color);
488
489 static int rtp_packet_add_info(GtkCList *clist,
490         tap_rtp_stat_t *statinfo, packet_info *pinfo,
491         const struct _rtp_info *rtpinfo);
492
493 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo, 
494                                    tap_rtp_stat_t *statinfo,
495                                    packet_info *pinfo,
496                                    const struct _rtp_info *rtpinfo);
497
498
499 /****************************************************************************/
500 /* whenever a RTP packet is seen by the tap listener */
501 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg)
502 {
503         user_data_t *user_data = user_data_arg;
504         const struct _rtp_info *rtpinfo = rtpinfo_arg;
505 #ifdef USE_CONVERSATION_GRAPH
506         value_pair_t vp;
507 #endif
508         /* we ignore packets that are not displayed */
509         if (pinfo->fd->flags.passed_dfilter == 0)
510                 return 0;
511         /* also ignore RTP Version != 2 */
512         else if (rtpinfo->info_version !=2)
513                 return 0;
514         /* is it the forward direction?  */
515         else if (user_data->ssrc_fwd == rtpinfo->info_sync_src
516                 && CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
517                 && user_data->port_src_fwd == pinfo->srcport
518                 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
519                 && user_data->port_dst_fwd == pinfo->destport)  {
520 #ifdef USE_CONVERSATION_GRAPH
521                 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
522                 vp.fnumber = pinfo->fd->num;
523                 g_array_append_val(user_data->series_fwd.value_pairs, vp);
524 #endif
525                 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
526                 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_JITTER]), &(user_data->forward.statinfo), pinfo, (guint32)(user_data->forward.statinfo.jitter*1000000));
527                 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DIFF]), &(user_data->forward.statinfo), pinfo, (guint32)(user_data->forward.statinfo.diff*1000000));
528                 rtp_packet_add_info(user_data->dlg.clist_fwd,
529                         &(user_data->forward.statinfo), pinfo, rtpinfo);
530                 rtp_packet_save_payload(&(user_data->forward.saveinfo),
531                         &(user_data->forward.statinfo), pinfo, rtpinfo);
532         }
533         /* is it the reversed direction? */
534         else if (user_data->ssrc_rev == rtpinfo->info_sync_src
535                 && CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
536                 && user_data->port_src_rev == pinfo->srcport
537                 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
538                 && user_data->port_dst_rev == pinfo->destport)  {
539 #ifdef USE_CONVERSATION_GRAPH
540                 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
541                 vp.fnumber = pinfo->fd->num;
542                 g_array_append_val(user_data->series_rev.value_pairs, vp);
543 #endif
544                 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
545                 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_JITTER]), &(user_data->reversed.statinfo), pinfo, (guint32)(user_data->reversed.statinfo.jitter*1000000));
546                 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DIFF]), &(user_data->reversed.statinfo), pinfo, (guint32)(user_data->reversed.statinfo.diff*1000000));
547                 rtp_packet_add_info(user_data->dlg.clist_rev,
548                         &(user_data->reversed.statinfo), pinfo, rtpinfo);
549                 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
550                         &(user_data->reversed.statinfo), pinfo, rtpinfo);
551         }
552
553         return 0;
554 }
555
556
557 /****************************************************************************/
558 int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
559                               packet_info *pinfo,
560                               const struct _rtp_info *rtpinfo)
561 {
562         double current_time;
563         double current_jitter;
564         double current_diff;
565         guint32 clock_rate;
566
567         statinfo->flags = 0;
568         /* check payload type */
569         if (rtpinfo->info_payload_type == PT_CN
570                 || rtpinfo->info_payload_type == PT_CN_OLD)
571                 statinfo->flags |= STAT_FLAG_PT_CN;
572         if (statinfo->pt == PT_CN
573                 || statinfo->pt == PT_CN_OLD)
574                 statinfo->flags |= STAT_FLAG_FOLLOW_PT_CN;
575         if (rtpinfo->info_payload_type != statinfo->pt)
576                 statinfo->flags |= STAT_FLAG_PT_CHANGE;
577         statinfo->pt = rtpinfo->info_payload_type;
578         /*
579          * XXX - should "get_clock_rate()" return 0 for unknown
580          * payload types, presumably meaning that we should
581          * just ignore this packet?
582          */
583         clock_rate = get_clock_rate(statinfo->pt);
584
585         /* store the current time and calculate the current jitter */
586         current_time = (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
587         current_diff = fabs (current_time - (statinfo->time) - ((double)(rtpinfo->info_timestamp)-(double)(statinfo->timestamp))/clock_rate);
588         current_jitter = statinfo->jitter + ( current_diff - statinfo->jitter)/16;
589         statinfo->delta = current_time-(statinfo->time);
590         statinfo->jitter = current_jitter;
591         statinfo->diff = current_diff;
592
593         /* calculate the BW in Kbps adding the IP+UDP header to the RTP -> 20bytes(IP)+8bytes(UDP) = 28bytes */
594         statinfo->bw_history[statinfo->bw_index].bytes = rtpinfo->info_data_len + 28;
595         statinfo->bw_history[statinfo->bw_index].time = current_time;
596         /* check if there are more than 1sec in the history buffer to calculate BW in bps. If so, remove those for the calculation */
597         while ((statinfo->bw_history[statinfo->bw_start_index].time+1)<current_time){
598                 statinfo->total_bytes -= statinfo->bw_history[statinfo->bw_start_index].bytes;  
599                 statinfo->bw_start_index++;
600                 if (statinfo->bw_start_index == BUFF_BW) statinfo->bw_start_index=0;
601         };
602         statinfo->total_bytes += rtpinfo->info_data_len + 28;
603         statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
604         statinfo->bw_index++;
605         if (statinfo->bw_index == BUFF_BW) statinfo->bw_index = 0;      
606
607
608         /*  is this the first packet we got in this direction? */
609         if (statinfo->first_packet) {
610                 statinfo->start_seq_nr = rtpinfo->info_seq_num;
611                 statinfo->start_time = current_time;
612                 statinfo->delta = 0;
613                 statinfo->jitter = 0;
614                 statinfo->diff = 0;
615                 statinfo->flags |= STAT_FLAG_FIRST;
616                 statinfo->first_packet = FALSE;
617         }
618         /* is it a packet with the mark bit set? */
619         if (rtpinfo->info_marker_set) {
620                 if (rtpinfo->info_timestamp > statinfo->timestamp){
621                         statinfo->delta_timestamp = rtpinfo->info_timestamp - statinfo->timestamp;
622                         statinfo->flags |= STAT_FLAG_MARKER;
623                 }
624                 else{
625                         statinfo->flags |= STAT_FLAG_WRONG_TIMESTAMP;
626                 }
627         }
628         /* is it a regular packet? */
629         if (!(statinfo->flags & STAT_FLAG_FIRST)
630                 && !(statinfo->flags & STAT_FLAG_MARKER)
631                 && !(statinfo->flags & STAT_FLAG_PT_CN)
632                 && !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
633                 && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
634                 /* include it in maximum delta calculation */
635                 if (statinfo->delta > statinfo->max_delta) {
636                         statinfo->max_delta = statinfo->delta;
637                         statinfo->max_nr = pinfo->fd->num;
638                 }
639                 /* maximum and mean jitter calculation */
640                 if (statinfo->jitter > statinfo->max_jitter) {
641                         statinfo->max_jitter = statinfo->jitter;
642                 }
643                 statinfo->mean_jitter = (statinfo->mean_jitter*statinfo->total_nr + current_diff) / (statinfo->total_nr+1);
644         }
645         /* regular payload change? (CN ignored) */
646         if (!(statinfo->flags & STAT_FLAG_FIRST)
647                 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
648                 if ((statinfo->pt != statinfo->reg_pt)
649                         && (statinfo->reg_pt != PT_UNDEFINED)) {
650                         statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
651                 }
652         }
653
654         /* set regular payload*/
655         if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
656                 statinfo->reg_pt = statinfo->pt;
657         }
658
659
660         /* When calculating expected rtp packets the seq number can wrap around
661         * so we have to count the number of cycles
662         * Variable cycles counts the wraps around in forwarding connection and
663         * under is flag that indicates where we are
664         *
665         * XXX how to determine number of cycles with all possible lost, late
666         * and duplicated packets without any doubt? It seems to me, that
667         * because of all possible combination of late, duplicated or lost
668         * packets, this can only be more or less good approximation
669         *
670         * There are some combinations (rare but theoretically possible),
671         * where below code won't work correctly - statistic may be wrong then.
672         */
673
674         /* so if the current sequence number is less than the start one
675         * we assume, that there is another cycle running */
676         if ((rtpinfo->info_seq_num < statinfo->start_seq_nr) && (statinfo->under == FALSE)){
677                 statinfo->cycles++;
678                 statinfo->under = TRUE;
679         }
680         /* what if the start seq nr was 0? Then the above condition will never
681         * be true, so we add another condition. XXX The problem would arise
682         * if one of the packets with seq nr 0 or 65535 would be lost or late */
683         else if ((rtpinfo->info_seq_num == 0) && (statinfo->stop_seq_nr == 65535) &&
684                 (statinfo->under == FALSE)){
685                 statinfo->cycles++;
686                 statinfo->under = TRUE;
687         }
688         /* the whole round is over, so reset the flag */
689         else if ((rtpinfo->info_seq_num > statinfo->start_seq_nr) && (statinfo->under != FALSE)) {
690                 statinfo->under = FALSE;
691         }
692
693         /* Since it is difficult to count lost, duplicate or late packets separately,
694         * we would like to know at least how many times the sequence number was not ok */
695
696         /* if the current seq number equals the last one or if we are here for
697         * the first time, then it is ok, we just store the current one as the last one */
698         if ( (statinfo->seq_num+1 == rtpinfo->info_seq_num) || (statinfo->flags & STAT_FLAG_FIRST) )
699                 statinfo->seq_num = rtpinfo->info_seq_num;
700         /* if the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */
701         else if ( (statinfo->seq_num == 65535) && (rtpinfo->info_seq_num == 0) )
702                 statinfo->seq_num = rtpinfo->info_seq_num;
703         /* lost packets */
704         else if (statinfo->seq_num+1 < rtpinfo->info_seq_num) {
705                 statinfo->seq_num = rtpinfo->info_seq_num;
706                 statinfo->sequence++;
707                 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
708         }
709         /* late or duplicated */
710         else if (statinfo->seq_num+1 > rtpinfo->info_seq_num) {
711                 statinfo->sequence++;
712                 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
713         }
714         statinfo->time = current_time;
715         statinfo->timestamp = rtpinfo->info_timestamp;
716         statinfo->stop_seq_nr = rtpinfo->info_seq_num;
717         statinfo->total_nr++;
718
719         return 0;
720 }
721
722
723 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
724 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
725 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
726 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
727
728 /****************************************************************************/
729 /* adds statistics information from the packet to the clist */
730 static int rtp_packet_add_info(GtkCList *clist,
731         tap_rtp_stat_t *statinfo, packet_info *pinfo,
732         const struct _rtp_info *rtpinfo)
733 {
734         guint16 msecs;
735         gchar timeStr[32];
736         struct tm *tm_tmp;
737         time_t then;
738         gchar status[40];
739         GdkColor color = COLOR_DEFAULT;
740         then = pinfo->fd->abs_secs;
741         msecs = (guint16)(pinfo->fd->abs_usecs/1000);
742         tm_tmp = localtime(&then);
743         g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
744                 tm_tmp->tm_mon + 1,
745                 tm_tmp->tm_mday,
746                 tm_tmp->tm_year + 1900,
747                 tm_tmp->tm_hour,
748                 tm_tmp->tm_min,
749                 tm_tmp->tm_sec,
750                 msecs);
751
752         if (statinfo->pt == PT_CN) {
753                 g_snprintf(status,sizeof(status),"Comfort noise (PT=13, RFC 3389)");
754                 color = COLOR_CN;
755         }
756         else if (statinfo->pt == PT_CN_OLD) {
757                 g_snprintf(status,sizeof(status),"Comfort noise (PT=19, reserved)");
758                 color = COLOR_CN;
759         }
760         else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
761                 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
762                 color = COLOR_ERROR;
763         }
764         else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
765                 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
766                 color = COLOR_WARNING;
767         }
768         else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
769                 g_snprintf(status,sizeof(status),"Incorrect timestamp");
770                 color = COLOR_WARNING;
771         }
772         else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
773                 &&  !(statinfo->flags & STAT_FLAG_FIRST)
774                 &&  !(statinfo->flags & STAT_FLAG_PT_CN)
775                 &&  (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
776                 &&  !(statinfo->flags & STAT_FLAG_MARKER)) {
777                 g_snprintf(status,sizeof(status),"Marker missing?");
778                 color = COLOR_WARNING;
779         }
780         else {
781                 if (statinfo->flags & STAT_FLAG_MARKER) {
782                         color = COLOR_WARNING;
783                 }
784                 g_snprintf(status,sizeof(status),OK_TEXT);
785         }
786         /*  is this the first packet we got in this direction? */
787         if (statinfo->flags & STAT_FLAG_FIRST) {
788                 add_to_clist(clist,
789                         pinfo->fd->num, rtpinfo->info_seq_num,
790                         0,
791                         0,
792                         statinfo->bandwidth,
793                         status,
794                         rtpinfo->info_marker_set,
795                         timeStr, pinfo->fd->pkt_len,
796                         &color);
797         }
798         else {
799                 add_to_clist(clist,
800                         pinfo->fd->num, rtpinfo->info_seq_num,
801                         statinfo->delta*1000,
802                         statinfo->jitter*1000,
803                         statinfo->bandwidth,
804                         status,
805                         rtpinfo->info_marker_set,
806                         timeStr, pinfo->fd->pkt_len,
807                         &color);
808         }
809         return 0;
810 }
811
812
813 /****************************************************************************/
814 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo, 
815                                    tap_rtp_stat_t *statinfo,
816                                    packet_info *pinfo,
817                                    const struct _rtp_info *rtpinfo)
818 {
819         guint i;
820         const guint8 *data;
821         gint16 tmp;
822
823         /*  is this the first packet we got in this direction? */
824         if (statinfo->flags & STAT_FLAG_FIRST) {
825                 if (saveinfo->fp == NULL) {
826                         saveinfo->saved = FALSE;
827                         saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
828                 }
829                 else
830                         saveinfo->saved = TRUE;
831         }
832
833         /* save the voice information */
834         /* if there was already an error, we quit */
835         if (saveinfo->saved == FALSE)
836                 return 0;
837
838         /* if the captured length and packet length aren't equal, we quit
839         * because there is some information missing */
840         if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
841                 saveinfo->saved = FALSE;
842                 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
843                 return 0;
844         }
845
846         /* if padding bit is set, but the padding count is bigger
847         * then the whole RTP data - error with padding count */
848         if ( (rtpinfo->info_padding_set != FALSE) &&
849                 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
850                 saveinfo->saved = FALSE;
851                 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
852                 return 0;
853         }
854
855         /* do we need to insert some silence? */
856         if ((rtpinfo->info_marker_set) &&
857                 !(statinfo->flags & STAT_FLAG_FIRST) &&
858                 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) )  {
859                 /* the amount of silence should be the difference between
860                 * the last timestamp and the current one minus x
861                 * x should equal the amount of information in the last frame
862                 * XXX not done yet */
863                 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
864                         rtpinfo->info_padding_count); i++) {
865                         tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
866                         fwrite(&tmp, 2, 1, saveinfo->fp);
867                         saveinfo->count++;
868                 }
869                 fflush(saveinfo->fp);
870         }
871
872         /* ulaw? */
873         if (rtpinfo->info_payload_type == PT_PCMU) {
874                 if (!rtpinfo->info_all_data_present) {
875                         /* Not all the data was captured. */
876                         saveinfo->saved = FALSE;
877                         saveinfo->error_type = TAP_RTP_SHORT_FRAME;
878                         return 0;
879                 }
880
881                 /* we put the pointer at the beginning of the RTP
882                 * payload, that is, at the beginning of the RTP data
883                 * plus the offset of the payload from the beginning
884                 * of the RTP data */
885                 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
886                 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
887                         tmp = (gint16 )ulaw2linear((unsigned char)*data);
888                         fwrite(&tmp, 2, 1, saveinfo->fp);
889                         saveinfo->count++;
890                 }
891                 fflush(saveinfo->fp);
892                 saveinfo->saved = TRUE;
893                 return 0;
894         }
895
896         /* alaw? */
897         else if (rtpinfo->info_payload_type == PT_PCMA) {
898                 if (!rtpinfo->info_all_data_present) {
899                         /* Not all the data was captured. */
900                         saveinfo->saved = FALSE;
901                         saveinfo->error_type = TAP_RTP_SHORT_FRAME;
902                         return 0;
903                 }
904
905                 /* we put the pointer at the beginning of the RTP
906                 * payload, that is, at the beginning of the RTP data
907                 * plus the offset of the payload from the beginning
908                 * of the RTP data */
909                 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
910                 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
911                         tmp = (gint16 )alaw2linear((unsigned char)*data);
912                         fwrite(&tmp, 2, 1, saveinfo->fp);
913                         saveinfo->count++;
914                 }
915                 fflush(saveinfo->fp);
916                 saveinfo->saved = TRUE;
917                 return 0;
918         }
919         /* comfort noise? - do nothing */
920         else if (rtpinfo->info_payload_type == PT_CN
921                 || rtpinfo->info_payload_type == PT_CN_OLD) {
922         }
923         /* unsupported codec or XXX other error */
924         else {
925                 saveinfo->saved = FALSE;
926                 saveinfo->error_type = TAP_RTP_WRONG_CODEC;
927                 return 0;
928         }
929
930         return 0;
931 }
932
933
934 /****************************************************************************/
935 /* CALLBACKS */
936
937 /****************************************************************************/
938 /* XXX just copied from gtk/rpc_stat.c */
939 void protect_thread_critical_region(void);
940 void unprotect_thread_critical_region(void);
941
942
943 /****************************************************************************/
944 /* close the dialog window and remove the tap listener */
945 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data _U_)
946 {
947         /* remove tap listener */
948         protect_thread_critical_region();
949         remove_tap_listener(user_data);
950         unprotect_thread_critical_region();
951
952         /* close and remove temporary files */
953         if (user_data->forward.saveinfo.fp != NULL)
954                 fclose(user_data->forward.saveinfo.fp);
955         if (user_data->reversed.saveinfo.fp != NULL)
956                 fclose(user_data->reversed.saveinfo.fp);
957         /*XXX: test for error **/
958         remove(user_data->f_tempname);
959         remove(user_data->r_tempname);
960
961         /* destroy save_voice_as window if open */
962         if (user_data->dlg.save_voice_as_w != NULL)
963                 window_destroy(user_data->dlg.save_voice_as_w);
964
965         /* destroy graph window if open */
966         if (user_data->dlg.dialog_graph.window != NULL)
967                 window_destroy(user_data->dlg.dialog_graph.window);
968
969 #ifdef USE_CONVERSATION_GRAPH
970         /* destroy graph window if open */
971         if (user_data->dlg.graph_window != NULL)
972                 window_destroy(user_data->dlg.graph_window);
973 #endif
974
975         /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exeption using GTK1*/
976         gtk_signal_disconnect(GTK_OBJECT(user_data->dlg.notebook), user_data->dlg.notebook_signal_id);
977
978         g_free(user_data->dlg.col_arrows_fwd);
979         g_free(user_data->dlg.col_arrows_rev);
980         g_free(user_data);
981 }
982
983
984 /****************************************************************************/
985 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
986                                     GtkNotebookPage *page _U_,
987                                     gint page_num _U_,
988                                     user_data_t *user_data _U_)
989 {
990         user_data->dlg.selected_clist =
991                 (page_num==0) ? user_data->dlg.clist_fwd : user_data->dlg.clist_rev ;
992         user_data->dlg.selected_row = 0;
993 }
994
995 /****************************************************************************/
996 static void on_clist_select_row(GtkCList        *clist _U_,
997                                 gint             row _U_,
998                                 gint             column _U_,
999                                 GdkEvent        *event _U_,
1000                                 user_data_t     *user_data _U_)
1001 {
1002         user_data->dlg.selected_clist = clist;
1003         user_data->dlg.selected_row = row;
1004 }
1005
1006
1007 #ifdef USE_CONVERSATION_GRAPH
1008 /****************************************************************************/
1009 /* when the graph window gets destroyed */
1010 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
1011 {
1012         /* note that graph window has been destroyed */
1013         user_data->dlg.graph_window = NULL;
1014 }
1015
1016 /****************************************************************************/
1017 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
1018 {
1019         guint row;
1020         GtkCList *clist = NULL;
1021         if (vp.fnumber != 0) {
1022                 clist = GTK_CLIST(user_data->dlg.clist_fwd);
1023                 row = gtk_clist_find_row_from_data(clist,
1024                                 GUINT_TO_POINTER(vp.fnumber));
1025                 if (row==-1) {
1026                         clist = GTK_CLIST(user_data->dlg.clist_rev);
1027                         row = gtk_clist_find_row_from_data(clist,
1028                                         GUINT_TO_POINTER(vp.fnumber));
1029                 }
1030                 if (row!=-1) {
1031                         gtk_notebook_set_page(GTK_NOTEBOOK(user_data->dlg.notebook),
1032                                 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
1033                         gtk_clist_select_row(clist, row, 0);
1034                         gtk_clist_moveto(clist, row, 0, 0.5, 0);
1035                 }
1036         }
1037 }
1038
1039
1040 /****************************************************************************/
1041 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1042 {
1043         gchar title1[80];
1044         gchar title2[80];
1045         GList *list = NULL;
1046         
1047         if (user_data->dlg.graph_window != NULL) {
1048                 /* There's already a graph window; reactivate it. */
1049                 reactivate_window(user_data->dlg.graph_window);
1050                 return;
1051         }
1052         list = g_list_append(list, &(user_data->series_fwd));
1053         list = g_list_append(list, &(user_data->series_rev));
1054
1055         user_data->series_fwd.color.pixel = 0;
1056         user_data->series_fwd.color.red = 0x80ff;
1057         user_data->series_fwd.color.green = 0xe0ff;
1058         user_data->series_fwd.color.blue = 0xffff;
1059         user_data->series_fwd.yvalue = 0.5;
1060
1061         user_data->series_rev.color.pixel = 0;
1062         user_data->series_rev.color.red = 0x60ff;
1063         user_data->series_rev.color.green = 0xc0ff;
1064         user_data->series_rev.color.blue = 0xffff;
1065         user_data->series_rev.yvalue = -0.5;
1066
1067         g_snprintf(title1, 80, "Forward: %s:%u to %s:%u (SSRC=%u)",
1068                 get_addr_name(&(user_data->ip_src_fwd)), 
1069                 user_data->port_src_fwd,
1070                 get_addr_name(&(user_data->ip_dst_fwd)),
1071                 user_data->port_dst_fwd,
1072                 user_data->ssrc_fwd);
1073
1074         g_snprintf(title2, 80, "Reverse: %s:%u to %s:%u (SSRC=%u)",
1075                 get_addr_name(&(user_data->ip_src_rev)),
1076                 user_data->port_src_rev,
1077                 get_addr_name(&(user_data->ip_dst_rev)),
1078                 user_data->port_dst_rev,
1079                 user_data->ssrc_rev);
1080
1081         user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
1082                 &graph_selection_callback, user_data);
1083         SIGNAL_CONNECT(user_data->dlg.graph_window, "destroy",
1084                         on_destroy_graph, user_data);
1085 }
1086 #endif /*USE_CONVERSATION_GRAPH*/
1087
1088 /****************************************************************************/
1089 static void dialog_graph_set_title(user_data_t* user_data)
1090 {
1091         char            *title;
1092         if (!user_data->dlg.dialog_graph.window){
1093                 return;
1094         }
1095         title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u   Reverse: %s:%u to %s:%u",
1096                         get_addr_name(&(user_data->ip_src_fwd)),
1097                         user_data->port_src_fwd,
1098                         get_addr_name(&(user_data->ip_dst_fwd)),
1099                         user_data->port_dst_fwd,
1100                         get_addr_name(&(user_data->ip_src_rev)),
1101                         user_data->port_src_rev,
1102                         get_addr_name(&(user_data->ip_dst_rev)),
1103                         user_data->port_dst_rev);
1104
1105         gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
1106         g_free(title);  
1107
1108 }
1109
1110
1111 /****************************************************************************/
1112 static void dialog_graph_reset(user_data_t* user_data)
1113 {
1114         int i, j;
1115
1116         user_data->dlg.dialog_graph.needs_redraw=TRUE;
1117         for(i=0;i<MAX_GRAPHS;i++){
1118                 for(j=0;j<NUM_GRAPH_ITEMS;j++){
1119                         dialog_graph_graph_item_t *dggi;
1120                         dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
1121                         dggi->value=0;
1122                         dggi->flags=0;
1123                 }
1124         }
1125         user_data->dlg.dialog_graph.last_interval=0xffffffff;
1126         user_data->dlg.dialog_graph.max_interval=0;
1127         user_data->dlg.dialog_graph.num_items=0;
1128
1129         /* create the color titles near the filter buttons */
1130         for(i=0;i<MAX_GRAPHS;i++){
1131                 /* it is forward */ 
1132                 if (i<2){
1133                         g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=%u)",
1134                         graph_descr[i],
1135                         get_addr_name(&(user_data->ip_src_fwd)),
1136                         user_data->port_src_fwd,
1137                         get_addr_name(&(user_data->ip_dst_fwd)),
1138                         user_data->port_dst_fwd,
1139                         user_data->ssrc_fwd);
1140                 /* it is reverse */
1141                 } else {
1142                         g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=%u)",
1143                         graph_descr[i],
1144                         get_addr_name(&(user_data->ip_src_rev)),
1145                         user_data->port_src_rev,
1146                         get_addr_name(&(user_data->ip_dst_rev)),
1147                         user_data->port_dst_rev,
1148                         user_data->ssrc_rev);
1149                 }
1150         }
1151
1152         dialog_graph_set_title(user_data);      
1153 }
1154
1155 /****************************************************************************/
1156 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
1157 {
1158         dialog_graph_graph_item_t *it;
1159
1160         it=&dgg->items[idx];
1161
1162         return it->value;
1163 }
1164
1165 /****************************************************************************/
1166 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
1167 {
1168         if(t>=10000000){
1169                 g_snprintf(buf, buf_len, "%ds",t/1000000);
1170         } else if(t>=1000000){
1171                 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
1172         } else if(t>=10000){
1173                 g_snprintf(buf, buf_len, "%dms",t/1000);
1174         } else if(t>=1000){
1175                 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
1176         } else {
1177                 g_snprintf(buf, buf_len, "%dus",t);
1178         }
1179 }
1180
1181 /****************************************************************************/
1182 static void dialog_graph_draw(user_data_t* user_data)
1183 {
1184         int i, lwidth;
1185         guint32 last_interval, first_interval, interval_delta, delta_multiplier;
1186         gint32 current_interval;
1187         guint32 left_x_border;
1188         guint32 right_x_border;
1189         guint32 top_y_border;
1190         guint32 bottom_y_border;
1191 #if GTK_MAJOR_VERSION < 2
1192         GdkFont *font;
1193 #else
1194         PangoLayout  *layout;
1195 #endif
1196         guint32 label_width, label_height;
1197         guint32 draw_width, draw_height;
1198         char label_string[15];
1199
1200         /* new variables */
1201         guint32 num_time_intervals;
1202         guint32 max_value;              /* max value of seen data */
1203         guint32 max_y;                  /* max value of the Y scale */
1204
1205 #if GTK_MAJOR_VERSION <2
1206         font = user_data->dlg.dialog_graph.draw_area->style->font;
1207 #endif
1208         if(!user_data->dlg.dialog_graph.needs_redraw){
1209                 return;
1210         }
1211         user_data->dlg.dialog_graph.needs_redraw=FALSE;
1212
1213         /*
1214          * Find the length of the intervals we have data for
1215          * so we know how large arrays we need to malloc()
1216          */
1217         num_time_intervals=user_data->dlg.dialog_graph.num_items;
1218         /* if there isnt anything to do, just return */
1219         if(num_time_intervals==0){
1220                 return;
1221         }
1222         num_time_intervals+=1;
1223         /* XXX move this check to _packet() */
1224         if(num_time_intervals>NUM_GRAPH_ITEMS){
1225                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
1226                 return;
1227         }
1228
1229         /*
1230          * find the max value so we can autoscale the y axis
1231          */
1232         max_value=0;
1233         for(i=0;i<MAX_GRAPHS;i++){
1234                 int idx;
1235
1236                 if(!user_data->dlg.dialog_graph.graph[i].display){
1237                         continue;
1238                 }
1239                 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
1240                         guint32 val;
1241
1242                         val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
1243
1244                         /* keep track of the max value we have encountered */
1245                         if(val>max_value){
1246                                 max_value=val;
1247                         }
1248                 }
1249         }
1250         
1251         /*
1252          * Clear out old plot
1253          */
1254         gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1255                            user_data->dlg.dialog_graph.draw_area->style->white_gc,
1256                            TRUE,
1257                            0, 0,
1258                            user_data->dlg.dialog_graph.draw_area->allocation.width,
1259                            user_data->dlg.dialog_graph.draw_area->allocation.height);
1260
1261
1262         /*
1263          * Calculate the y scale we should use
1264          */
1265         if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
1266                 max_y=yscale_max[MAX_YSCALE-1];
1267                 for(i=MAX_YSCALE-1;i>0;i--){
1268                         if(max_value<yscale_max[i]){
1269                                 max_y=yscale_max[i];
1270                         }
1271                 }
1272         } else {
1273                 /* the user had specified an explicit y scale to use */
1274                 max_y=user_data->dlg.dialog_graph.max_y_units;
1275         }
1276
1277         /*
1278          * Calculate size of borders surrounding the plot
1279          * The border on the right side needs to be adjusted depending
1280          * on the width of the text labels. For simplicity we assume that the
1281          * top y scale label will be the widest one
1282          */
1283          print_time_scale_string(label_string, 15, max_y);
1284 #if GTK_MAJOR_VERSION < 2
1285         label_width=gdk_string_width(font, label_string);
1286         label_height=gdk_string_height(font, label_string);
1287 #else
1288         layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1289         pango_layout_get_pixel_size(layout, &label_width, &label_height);
1290 #endif
1291         left_x_border=10;
1292         right_x_border=label_width+20;
1293         top_y_border=10;
1294         bottom_y_border=label_height+20;
1295
1296
1297         /*
1298          * Calculate the size of the drawing area for the actual plot
1299          */
1300         draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
1301         draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
1302
1303
1304         /*
1305          * Draw the y axis and labels
1306          * (we always draw the y scale with 11 ticks along the axis)
1307          */
1308         gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1309                 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1310                 top_y_border,
1311                 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1312                 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
1313         for(i=0;i<=10;i++){
1314                 int xwidth, lwidth;
1315
1316                 xwidth=5;
1317                 if(!(i%5)){
1318                         /* first, middle and last tick are slightly longer */
1319                         xwidth=10;
1320                 }
1321                 /* draw the tick */
1322                 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1323                         user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1324                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1325                         user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1326                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1327                 /* draw the labels */
1328                 if(i==0){
1329                         print_time_scale_string(label_string, 15, (max_y*i/10));
1330 #if GTK_MAJOR_VERSION < 2
1331                         lwidth=gdk_string_width(font, label_string);
1332                         gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1333                                         font,
1334                                         user_data->dlg.dialog_graph.draw_area->style->black_gc,
1335                                         user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1336                                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1337                                         label_string);
1338 #else
1339                         pango_layout_set_text(layout, label_string, -1);
1340                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1341                         gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1342                                         user_data->dlg.dialog_graph.draw_area->style->black_gc,
1343                                         user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1344                                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1345                                         layout);
1346 #endif
1347                 }
1348                 if(i==5){
1349                         print_time_scale_string(label_string, 15, (max_y*i/10));
1350 #if GTK_MAJOR_VERSION < 2
1351                         lwidth=gdk_string_width(font, label_string);
1352                         gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1353                                         font,
1354                                         user_data->dlg.dialog_graph.draw_area->style->black_gc,
1355                                         user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1356                                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1357                                         label_string);
1358 #else
1359                         pango_layout_set_text(layout, label_string, -1);
1360                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1361                         gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1362                                         user_data->dlg.dialog_graph.draw_area->style->black_gc,
1363                                         user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1364                                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1365                                         layout);
1366 #endif
1367                 }
1368                 if(i==10){
1369                         print_time_scale_string(label_string, 15, (max_y*i/10));
1370 #if GTK_MAJOR_VERSION < 2
1371                         lwidth=gdk_string_width(font, label_string);
1372                         gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1373                                         font,
1374                                         user_data->dlg.dialog_graph.draw_area->style->black_gc,
1375                                         user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1376                                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1377                                         label_string);
1378 #else
1379                         pango_layout_set_text(layout, label_string, -1);
1380                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1381                         gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1382                                         user_data->dlg.dialog_graph.draw_area->style->black_gc,
1383                                         user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1384                                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1385                                         layout);
1386 #endif
1387                 }
1388         }
1389
1390
1391
1392         /*
1393          * if we have not specified the last_interval via the gui,
1394          * then just pick the current end of the capture so that is scrolls
1395          * nicely when doing live captures
1396          */
1397         if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1398                 last_interval=user_data->dlg.dialog_graph.max_interval;
1399         } else {
1400                 last_interval=user_data->dlg.dialog_graph.last_interval;
1401         }
1402
1403
1404
1405
1406 /*XXX*/
1407         /* plot the x-scale */
1408         gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc, left_x_border, user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1, user_data->dlg.dialog_graph.pixmap_width-right_x_border+1, user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1);
1409
1410         if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1411                 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1412                 first_interval*=user_data->dlg.dialog_graph.interval;
1413         } else {
1414                 first_interval=0;
1415         }
1416
1417         interval_delta=1;
1418         delta_multiplier=5;
1419         while(interval_delta<((last_interval-first_interval)/10)){
1420                 interval_delta*=delta_multiplier;
1421                 if(delta_multiplier==5){
1422                         delta_multiplier=2;
1423                 } else {
1424                         delta_multiplier=5;
1425                 }
1426         }
1427
1428         for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1429                 int x, xlen;
1430
1431                 /* if pixels_per_tick is <5, only draw every 10 ticks */
1432                 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1433                         continue;
1434                 }
1435
1436                 if(current_interval%interval_delta){
1437                         xlen=5;
1438                 } else {
1439                         xlen=17;
1440                 }
1441
1442                 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1443                 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1444                         x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1445                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1446                         x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1447                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1448
1449                 if(xlen==17){
1450                         int lwidth;
1451                         if(user_data->dlg.dialog_graph.interval>=1000){
1452                                 g_snprintf(label_string, 15, "%ds", current_interval/1000);
1453                         } else if(user_data->dlg.dialog_graph.interval>=100){
1454                                 g_snprintf(label_string, 15, "%d.%1ds", current_interval/1000,(current_interval/100)%10)
1455 ;
1456                         } else if(user_data->dlg.dialog_graph.interval>=10){
1457                                 g_snprintf(label_string, 15, "%d.%2ds", current_interval/1000,(current_interval/10)%100)
1458 ;
1459                         } else {
1460                                 g_snprintf(label_string, 15, "%d.%3ds", current_interval/1000,current_interval%1000);
1461                         }
1462 #if GTK_MAJOR_VERSION < 2
1463                         lwidth=gdk_string_width(font, label_string);
1464                         gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1465                                         font,
1466                                         user_data->dlg.dialog_graph.draw_area->style->black_gc,
1467                                         x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1468                                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20+label_height,
1469                                         label_string);
1470 #else
1471                         pango_layout_set_text(layout, label_string, -1);
1472                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1473                         gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1474                                         user_data->dlg.dialog_graph.draw_area->style->black_gc,
1475                                         x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1476                                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1477                                         layout);
1478 #endif
1479                 }
1480
1481         }
1482
1483
1484
1485
1486
1487
1488         /*
1489          * Draw "x" for Sequence Errors and "m" for Marks
1490          */
1491         /* Draw the labels Fwd and Rev */
1492         strcpy(label_string,"<-Fwd");
1493 #if GTK_MAJOR_VERSION < 2
1494         lwidth=gdk_string_width(font, label_string);
1495         gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1496                 font,
1497                 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1498                 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1499                 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+label_height,
1500                 label_string);
1501 #else
1502         pango_layout_set_text(layout, label_string, -1);
1503         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1504         gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1505                 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1506                 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1507                 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1508                 layout);
1509 #endif
1510         strcpy(label_string,"<-Rev");
1511 #if GTK_MAJOR_VERSION < 2
1512         lwidth=gdk_string_width(font, label_string);
1513         gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1514                 font,
1515                 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1516                 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1517                 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9+label_height,
1518                 label_string);
1519 #else
1520         pango_layout_set_text(layout, label_string, -1);
1521         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1522         gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1523                 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1524                 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1525                 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1526                 layout);
1527 #endif
1528
1529         /* Draw the marks */    
1530         for(i=MAX_GRAPHS-1;i>=0;i--){
1531                 guint32 interval;
1532                 guint32 x_pos, prev_x_pos;
1533
1534                 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1535                 if (!user_data->dlg.dialog_graph.graph[i].display){
1536                         continue;
1537                 }
1538                 /* initialize prev x/y to the low left corner of the graph */
1539                 prev_x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-first_interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1540
1541                 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1542                         x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1543
1544                         if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1545                                 int lwidth;
1546                                 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1547                                         strcpy(label_string,"x");
1548                                 } else {
1549                                         strcpy(label_string,"m");
1550                                 }
1551                                         
1552 #if GTK_MAJOR_VERSION < 2
1553                                 lwidth=gdk_string_width(font, label_string);
1554                                 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1555                                         font,
1556                                         user_data->dlg.dialog_graph.draw_area->style->black_gc,
1557                                         x_pos-1-lwidth/2,
1558                                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2)+label_height,
1559                                         label_string);
1560 #else                           
1561                                 pango_layout_set_text(layout, label_string, -1);
1562                                 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1563                                 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1564                                         user_data->dlg.dialog_graph.draw_area->style->black_gc,
1565                                         x_pos-1-lwidth/2,
1566                                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1567                                         layout);
1568 #endif
1569                         }
1570
1571                         prev_x_pos=x_pos;
1572                 }
1573         }
1574
1575 #if GTK_MAJOR_VERSION >= 2
1576         g_object_unref(G_OBJECT(layout));
1577 #endif
1578
1579
1580         /*
1581          * Loop over all graphs and draw them
1582          */
1583         for(i=MAX_GRAPHS-1;i>=0;i--){
1584                 guint32 interval;
1585                 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1586                 if (!user_data->dlg.dialog_graph.graph[i].display){
1587                         continue;
1588                 }       
1589                 /* initialize prev x/y to the low left corner of the graph */
1590                 prev_x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-first_interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1591                 prev_y_pos=draw_height-1+top_y_border;
1592                 
1593                 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1594                         guint32 val;
1595                         x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1596                         val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1597                         if(val>max_y){
1598                                 y_pos=0;
1599                         } else {
1600                                 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1601                         }
1602
1603                         /* dont need to draw anything if the segment
1604                          * is entirely above the top of the graph
1605                          */
1606                         if( (prev_y_pos==0) && (y_pos==0) ){
1607                                 prev_y_pos=y_pos;
1608                                 prev_x_pos=x_pos;
1609                                 continue;
1610                         }
1611                 
1612                         if(val){
1613                                 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1614                                 x_pos, draw_height-1+top_y_border,
1615                                 x_pos, y_pos);
1616                         }
1617
1618                         prev_y_pos=y_pos;
1619                         prev_x_pos=x_pos;
1620                 }
1621         }
1622
1623
1624         gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1625                         user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1626                         user_data->dlg.dialog_graph.pixmap,
1627                         0, 0,
1628                         0, 0,
1629                         user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1630
1631
1632         /* update the scrollbar */
1633         user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1634         user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1635         user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1636         if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1637                 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1638         } else {
1639                 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1640         }
1641         user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1642         gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1643         gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1644
1645 }
1646
1647 /****************************************************************************/
1648 static void dialog_graph_redraw(user_data_t* user_data)
1649 {
1650         user_data->dlg.dialog_graph.needs_redraw=TRUE;
1651         dialog_graph_draw(user_data); 
1652 }
1653
1654 /****************************************************************************/
1655 static gint quit(GtkWidget *widget, GdkEventExpose *event _U_)
1656 {
1657         user_data_t *user_data;
1658
1659         user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1660
1661         user_data->dlg.dialog_graph.window = NULL;
1662         return TRUE;
1663 }
1664
1665 /****************************************************************************/
1666 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1667 {
1668         user_data_t *user_data;
1669
1670         user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1671         if(!user_data){
1672                 exit(10);
1673         }
1674
1675
1676         gdk_draw_pixmap(widget->window,
1677                         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1678                         user_data->dlg.dialog_graph.pixmap,
1679                         event->area.x, event->area.y,
1680                         event->area.x, event->area.y,
1681                         event->area.width, event->area.height);
1682
1683         return FALSE;
1684 }
1685
1686 /****************************************************************************/
1687 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1688 {
1689         user_data_t *user_data;
1690         int i;
1691
1692         user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1693
1694         if(!user_data){
1695                 exit(10);
1696         }
1697
1698         if(user_data->dlg.dialog_graph.pixmap){
1699                 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1700                 user_data->dlg.dialog_graph.pixmap=NULL;
1701         }
1702
1703         user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1704                         widget->allocation.width,
1705                         widget->allocation.height,
1706                         -1);
1707         user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1708         user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1709
1710         gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1711                         widget->style->white_gc,
1712                         TRUE,
1713                         0, 0,
1714                         widget->allocation.width,
1715                         widget->allocation.height);
1716
1717         /* set up the colors and the GC structs for this pixmap */
1718         for(i=0;i<MAX_GRAPHS;i++){
1719                 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1720 #if GTK_MAJOR_VERSION < 2
1721                 colormap = gtk_widget_get_colormap (widget);
1722                 if (!gdk_color_alloc (colormap, &user_data->dlg.dialog_graph.graph[i].color)){
1723                         g_warning ("Couldn't allocate color");
1724                 }
1725
1726                 gdk_gc_set_foreground(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1727 #else
1728                 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1729 #endif
1730         }
1731
1732         dialog_graph_redraw(user_data);
1733         return TRUE;
1734 }
1735
1736 /****************************************************************************/
1737 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1738 {
1739         user_data_t *user_data=(user_data_t *)data;
1740         guint32 mi;
1741
1742         mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1743         if(user_data->dlg.dialog_graph.last_interval==mi){
1744                 return TRUE;
1745         }
1746         if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1747         &&  (mi==user_data->dlg.dialog_graph.max_interval) ){
1748                 return TRUE;
1749         }
1750
1751         user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1752
1753         dialog_graph_redraw(user_data);
1754         return TRUE;
1755 }
1756
1757 /****************************************************************************/
1758 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1759 {
1760         user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1761         SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "destroy", quit, user_data);
1762         OBJECT_SET_DATA(user_data->dlg.dialog_graph.draw_area, "user_data_t", user_data);
1763
1764         WIDGET_SET_SIZE(user_data->dlg.dialog_graph.draw_area, user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1765
1766         /* signals needed to handle backing pixmap */
1767         SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "expose_event", expose_event, NULL);
1768         SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "configure_event", configure_event, user_data);
1769
1770         gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1771         gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1772
1773         /* create the associated scrollbar */
1774         user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1775         user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1776         gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1777         gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1778         SIGNAL_CONNECT(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", scrollbar_changed, user_data);
1779 }
1780
1781 /****************************************************************************/
1782 static void disable_graph(dialog_graph_graph_t *dgg)
1783 {
1784         if (dgg->display) {
1785                 dgg->display=FALSE;
1786                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1787                     FALSE);
1788         }
1789 }
1790
1791 /****************************************************************************/
1792 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1793 {
1794         /* this graph is not active, just update display and redraw */
1795         if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1796                 disable_graph(dgg); 
1797                 dialog_graph_redraw(dgg->ud);
1798                 return 0;
1799         }
1800
1801         enable_graph(dgg);
1802         cf_retap_packets(&cfile);
1803         dialog_graph_redraw(dgg->ud);
1804
1805         return 0;
1806 }
1807
1808 /****************************************************************************/
1809 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1810 {
1811         GtkWidget *hbox;
1812         GtkWidget *label;
1813         char str[256];
1814
1815         hbox=gtk_hbox_new(FALSE, 3);
1816         gtk_container_add(GTK_CONTAINER(box), hbox);
1817         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1818         gtk_widget_show(hbox);
1819
1820         g_snprintf(str, 256, "Graph %d", num);
1821         dgg->display_button=gtk_toggle_button_new_with_label(str);
1822         gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1823         gtk_widget_show(dgg->display_button);
1824         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1825         SIGNAL_CONNECT(dgg->display_button, "toggled", filter_callback, dgg);
1826
1827         label=gtk_label_new(dgg->title);
1828         gtk_widget_show(label);
1829         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1830
1831 #if GTK_MAJOR_VERSION < 2
1832     /* setting the color of the display button doesn't work */
1833         rc_style = gtk_rc_style_new ();
1834         rc_style->fg[GTK_STATE_NORMAL] = dgg->color;
1835         rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG;
1836         rc_style->fg[GTK_STATE_ACTIVE] = dgg->color;
1837         rc_style->color_flags[GTK_STATE_ACTIVE] |= GTK_RC_FG;
1838         rc_style->fg[GTK_STATE_PRELIGHT] = dgg->color;
1839         rc_style->color_flags[GTK_STATE_PRELIGHT] |= GTK_RC_FG;
1840         rc_style->fg[GTK_STATE_SELECTED] = dgg->color;
1841         rc_style->color_flags[GTK_STATE_SELECTED] |= GTK_RC_FG;
1842         rc_style->fg[GTK_STATE_INSENSITIVE] = dgg->color;
1843         rc_style->color_flags[GTK_STATE_INSENSITIVE] |= GTK_RC_FG;
1844         gtk_widget_modify_style (label, rc_style);
1845         gtk_rc_style_unref (rc_style);
1846 #else
1847         gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1848         gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1849         gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1850         gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1851         gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1852 #endif
1853
1854         return;
1855 }
1856
1857 /****************************************************************************/
1858 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1859 {
1860         GtkWidget *frame;
1861         GtkWidget *vbox;
1862         int i;
1863         GtkWidget *label;
1864
1865         frame=gtk_frame_new("Graphs");
1866         gtk_container_add(GTK_CONTAINER(box), frame);
1867         gtk_widget_show(frame);
1868
1869         vbox=gtk_vbox_new(FALSE, 1);
1870         gtk_container_add(GTK_CONTAINER(frame), vbox);
1871         gtk_container_border_width(GTK_CONTAINER(vbox), 3);
1872         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1873         gtk_widget_show(vbox);
1874
1875         for(i=0;i<MAX_GRAPHS;i++){
1876                 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1877         }
1878
1879         label=gtk_label_new("Label:    x = Wrong Seq. number      m = Mark set");
1880         gtk_widget_show(label);
1881         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1882
1883         return;
1884 }
1885
1886 /****************************************************************************/
1887 static void yscale_select(GtkWidget *item, gpointer key)
1888 {
1889         int val;
1890         user_data_t *user_data;
1891         
1892         user_data=(user_data_t *)key;
1893         val=(int)OBJECT_GET_DATA(item, "yscale_max");
1894
1895         user_data->dlg.dialog_graph.max_y_units=val;
1896         dialog_graph_redraw(user_data);
1897 }
1898
1899 /****************************************************************************/
1900 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1901 {
1902         int val;
1903         user_data_t *user_data;
1904
1905         user_data=(user_data_t *)key;
1906         val=(int)OBJECT_GET_DATA(item, "pixels_per_tick");
1907         user_data->dlg.dialog_graph.pixels_per_tick=val;
1908         dialog_graph_redraw(user_data);
1909 }
1910
1911 /****************************************************************************/
1912 static void tick_interval_select(GtkWidget *item, gpointer key)
1913 {
1914         int val;
1915         user_data_t *user_data;
1916
1917         user_data=(user_data_t *)key;
1918         val=(int)OBJECT_GET_DATA(item, "tick_interval");
1919
1920         user_data->dlg.dialog_graph.interval=val;
1921         cf_retap_packets(&cfile);
1922         dialog_graph_redraw(user_data);
1923 }
1924
1925 /****************************************************************************/
1926 static void create_yscale_max_menu_items(user_data_t* user_data, GtkWidget *menu)
1927 {
1928         char str[15];
1929         GtkWidget *menu_item;
1930         int i;
1931
1932         for(i=0;i<MAX_YSCALE;i++){
1933                 if(yscale_max[i]==AUTO_MAX_YSCALE){
1934                         strcpy(str,"Auto");
1935                 } else {
1936                         g_snprintf(str, 15, "%u ms", yscale_max[i]/1000);
1937                 }
1938                 menu_item=gtk_menu_item_new_with_label(str);
1939                 OBJECT_SET_DATA(menu_item, "yscale_max",
1940                                 GUINT_TO_POINTER(yscale_max[i]));
1941                 SIGNAL_CONNECT(menu_item, "activate", yscale_select, user_data);
1942                 gtk_widget_show(menu_item);
1943                 gtk_menu_append(GTK_MENU(menu), menu_item);
1944         }
1945         return;
1946 }
1947
1948 /****************************************************************************/
1949 static void create_pixels_per_tick_menu_items(user_data_t* user_data, GtkWidget *menu)
1950 {
1951         char str[5];
1952         GtkWidget *menu_item;
1953         int i;
1954
1955         for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1956                 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
1957                 menu_item=gtk_menu_item_new_with_label(str);
1958
1959                 OBJECT_SET_DATA(menu_item, "pixels_per_tick",
1960                                 GUINT_TO_POINTER(pixels_per_tick[i]));
1961                 SIGNAL_CONNECT(menu_item, "activate", pixels_per_tick_select, user_data);
1962                 gtk_widget_show(menu_item);
1963                 gtk_menu_append(GTK_MENU(menu), menu_item);
1964         }
1965         gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
1966         return;
1967 }
1968
1969
1970 /****************************************************************************/
1971 static void create_tick_interval_menu_items(user_data_t* user_data, GtkWidget *menu)
1972 {
1973         char str[15];
1974         GtkWidget *menu_item;
1975         int i;
1976
1977         for(i=0;i<MAX_TICK_VALUES;i++){
1978                 if(tick_interval_values[i]>=1000){
1979                         g_snprintf(str, 15, "%u sec", tick_interval_values[i]/1000);
1980                 } else if(tick_interval_values[i]>=100){
1981                         g_snprintf(str, 15, "0.%1u sec", (tick_interval_values[i]/100)%10);
1982                 } else if(tick_interval_values[i]>=10){
1983                         g_snprintf(str, 15, "0.%02u sec", (tick_interval_values[i]/10)%10);
1984                 } else {
1985                         g_snprintf(str, 15, "0.%03u sec", (tick_interval_values[i])%10);
1986                 }
1987
1988                 menu_item=gtk_menu_item_new_with_label(str);
1989                 OBJECT_SET_DATA(menu_item, "tick_interval",
1990                                 GUINT_TO_POINTER(tick_interval_values[i]));
1991                 SIGNAL_CONNECT(menu_item, "activate", tick_interval_select, (gpointer)user_data);
1992                 gtk_widget_show(menu_item);
1993                 gtk_menu_append(GTK_MENU(menu), menu_item);
1994         }
1995         gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
1996         return;
1997 }
1998
1999 /****************************************************************************/
2000 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, char *name, void (*func)(user_data_t* user_data, GtkWidget *menu))
2001 {
2002         GtkWidget *hbox;
2003         GtkWidget *label;
2004         GtkWidget *option_menu;
2005         GtkWidget *menu;
2006
2007         hbox=gtk_hbox_new(FALSE, 0);
2008         gtk_container_add(GTK_CONTAINER(box), hbox);
2009         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2010         gtk_widget_show(hbox);
2011
2012         label=gtk_label_new(name);
2013         gtk_widget_show(label);
2014         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2015
2016         option_menu=gtk_option_menu_new();
2017         menu=gtk_menu_new();
2018         (*func)(user_data, menu);
2019         gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
2020         gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
2021         gtk_widget_show(option_menu);
2022 }
2023
2024 /****************************************************************************/
2025 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
2026 {
2027         GtkWidget *frame_vbox;
2028         GtkWidget *frame;
2029         GtkWidget *vbox;
2030
2031         frame_vbox=gtk_vbox_new(FALSE, 0);
2032         gtk_container_add(GTK_CONTAINER(box), frame_vbox);
2033         gtk_widget_show(frame_vbox);
2034
2035         frame = gtk_frame_new("X Axis");
2036         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
2037         gtk_widget_show(frame);
2038
2039         vbox=gtk_vbox_new(FALSE, 0);
2040         gtk_container_add(GTK_CONTAINER(frame), vbox);
2041         gtk_container_border_width(GTK_CONTAINER(vbox), 3);
2042         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
2043         gtk_widget_show(vbox);
2044
2045         create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
2046         create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
2047
2048         frame = gtk_frame_new("Y Axis");
2049         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
2050         gtk_widget_show(frame);
2051
2052         vbox=gtk_vbox_new(FALSE, 0);
2053         gtk_container_add(GTK_CONTAINER(frame), vbox);
2054         gtk_container_border_width(GTK_CONTAINER(vbox), 3);
2055         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
2056         gtk_widget_show(vbox);
2057
2058         create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
2059
2060         return;
2061 }
2062
2063 /****************************************************************************/
2064 static void dialog_graph_init_window(user_data_t* user_data)
2065 {
2066         GtkWidget *vbox;
2067         GtkWidget *hbox;
2068         GtkWidget *bt_close;
2069
2070         /* create the main window */
2071         user_data->dlg.dialog_graph.window=window_new(GTK_WINDOW_TOPLEVEL, "I/O Graphs");
2072
2073         vbox=gtk_vbox_new(FALSE, 0);
2074         gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
2075         gtk_widget_show(vbox);
2076
2077         create_draw_area(user_data, vbox);
2078
2079         hbox=gtk_hbox_new(FALSE, 3);
2080         gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2081         gtk_container_border_width(GTK_CONTAINER(hbox), 3);
2082         gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2083         gtk_widget_show(hbox);
2084
2085         create_filter_area(user_data, hbox);
2086         create_ctrl_area(user_data, hbox);
2087
2088         dialog_graph_set_title(user_data);
2089
2090     hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
2091         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2092     gtk_widget_show(hbox);
2093
2094     bt_close = OBJECT_GET_DATA(hbox, GTK_STOCK_CLOSE);
2095     window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
2096
2097     SIGNAL_CONNECT(user_data->dlg.dialog_graph.window, "delete_event", window_delete_event_cb, NULL);
2098
2099     gtk_widget_show(user_data->dlg.dialog_graph.window);
2100     window_present(user_data->dlg.dialog_graph.window);
2101
2102 }
2103
2104
2105 /****************************************************************************/
2106 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2107 {
2108         if (user_data->dlg.dialog_graph.window != NULL) {
2109                 /* There's already a graph window; reactivate it. */
2110                 reactivate_window(user_data->dlg.dialog_graph.window);
2111                 return;
2112         }
2113
2114         dialog_graph_init_window(user_data);    
2115
2116 }
2117
2118 /****************************************************************************/
2119 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2120 {
2121         guint fnumber;
2122
2123         if (user_data->dlg.selected_clist!=NULL) {
2124                 fnumber = GPOINTER_TO_UINT(gtk_clist_get_row_data(
2125                         GTK_CLIST(user_data->dlg.selected_clist), user_data->dlg.selected_row) );
2126                 cf_goto_frame(&cfile, fnumber);
2127         }
2128 }
2129
2130
2131 static void draw_stat(user_data_t *user_data);
2132
2133 /****************************************************************************/
2134 /* re-dissects all packets */
2135 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2136 {
2137         GString *error_string;
2138         
2139         /* remove tap listener */
2140         protect_thread_critical_region();
2141         remove_tap_listener(user_data);
2142         unprotect_thread_critical_region();
2143
2144         /* register tap listener */
2145         error_string = register_tap_listener("rtp", user_data, NULL,
2146                 rtp_reset, rtp_packet, rtp_draw);
2147         if (error_string != NULL) {
2148                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
2149                         g_string_free(error_string, TRUE);
2150                 return;
2151         }
2152
2153         /* retap all packets */
2154         cf_retap_packets(&cfile);
2155
2156         /* draw statistics info */
2157         draw_stat(user_data);
2158
2159         gtk_clist_sort(user_data->dlg.clist_fwd);
2160         gtk_clist_sort(user_data->dlg.clist_rev);
2161 }
2162
2163 /****************************************************************************/
2164 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2165 {
2166         GtkCList *clist;
2167         gchar *text;
2168         gint row;
2169         if (user_data->dlg.selected_clist==NULL)
2170                 return;
2171
2172         clist = user_data->dlg.selected_clist;
2173         row = user_data->dlg.selected_row + 1;
2174
2175         while (gtk_clist_get_text(clist,row,6,&text)) {
2176                 if (strcmp(text, OK_TEXT) != 0) {
2177                         gtk_clist_select_row(clist, row, 0);
2178                         gtk_clist_moveto(clist, row, 0, 0.5, 0);
2179                         return;
2180                 }
2181                 ++row;
2182         }
2183
2184         /* wrap around */
2185         row = 0;
2186         while (gtk_clist_get_text(clist,row,6,&text) && row<user_data->dlg.selected_row) {
2187                 if (strcmp(text, OK_TEXT) != 0) {
2188                         gtk_clist_select_row(clist, row, 0);
2189                         gtk_clist_moveto(clist, row, 0, 0.5, 0);
2190                         return;
2191                 }
2192                 ++row;
2193         }
2194 }
2195
2196 /****************************************************************************/
2197 /* when we want to save the information */
2198 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
2199 {
2200         gchar *g_dest;
2201         GtkWidget *rev, *forw, *both;
2202         user_data_t *user_data;
2203         
2204         FILE *fp;
2205         char *columnText;
2206         int i,j;
2207         
2208         g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
2209         
2210         /* Perhaps the user specified a directory instead of a file.
2211         Check whether they did. */
2212         if (test_for_directory(g_dest) == EISDIR) {
2213                 /* It's a directory - set the file selection box to display it. */
2214                 set_last_open_dir(g_dest);
2215                 g_free(g_dest);
2216                 file_selection_set_current_folder(fs, get_last_open_dir());
2217                 return;
2218         }
2219         
2220         rev = (GtkWidget*)OBJECT_GET_DATA(bt, "reversed_rb");
2221         forw = (GtkWidget*)OBJECT_GET_DATA(bt, "forward_rb");
2222         both = (GtkWidget*)OBJECT_GET_DATA(bt, "both_rb");
2223         user_data = (user_data_t*)OBJECT_GET_DATA(bt, "user_data");
2224         
2225         if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
2226                 fp = fopen(g_dest, "w");
2227                 if (fp == NULL) {
2228                         open_failure_alert_box(g_dest, errno, TRUE);
2229                         return;
2230                 }
2231                 
2232                 if (GTK_TOGGLE_BUTTON(both)->active) {
2233                         fprintf(fp, "Forward\n");
2234                         if (ferror(fp)) {
2235                                 write_failure_alert_box(g_dest, errno);
2236                                 fclose(fp);
2237                                 return;
2238                         }
2239                 }
2240                 
2241                 for(j = 0; j < NUM_COLS; j++) {
2242                         if (j == 0) {
2243                                 fprintf(fp,"%s",titles[j]);
2244                         } else {
2245                                 fprintf(fp,",%s",titles[j]);
2246                         }
2247                 }
2248                 fprintf(fp,"\n");
2249                 if (ferror(fp)) {
2250                         write_failure_alert_box(g_dest, errno);
2251                         fclose(fp);
2252                         return;
2253                 }
2254                 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_fwd)->rows; i++) {
2255                         for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
2256                                 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_fwd),i,j,&columnText);
2257                                 if (j == 0) {
2258                                         fprintf(fp,"%s",columnText);
2259                                 } else {
2260                                         fprintf(fp,",%s",columnText);
2261                                 }
2262                         }
2263                         fprintf(fp,"\n");
2264                         if (ferror(fp)) {
2265                                 write_failure_alert_box(g_dest, errno);
2266                                 fclose(fp);
2267                                 return;
2268                         }
2269                 }
2270                 
2271                 if (fclose(fp) == EOF) {
2272                         write_failure_alert_box(g_dest, errno);
2273                         return;
2274                 }
2275         }
2276         
2277         if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
2278                 
2279                 if (GTK_TOGGLE_BUTTON(both)->active) {
2280                         fp = fopen(g_dest, "a");
2281                         if (fp == NULL) {
2282                                 open_failure_alert_box(g_dest, errno, TRUE);
2283                                 return;
2284                         }
2285                         fprintf(fp, "\nReverse\n");
2286                         if (ferror(fp)) {
2287                                 write_failure_alert_box(g_dest, errno);
2288                                 fclose(fp);
2289                                 return;
2290                         }
2291                 } else {
2292                         fp = fopen(g_dest, "w");
2293                         if (fp == NULL) {
2294                                 open_failure_alert_box(g_dest, errno, TRUE);
2295                                 return;
2296                         }
2297                 }
2298                 for(j = 0; j < NUM_COLS; j++) {
2299                         if (j == 0) {
2300                                 fprintf(fp,"%s",titles[j]);
2301                         } else {
2302                                 fprintf(fp,",%s",titles[j]);
2303                         }
2304                 }
2305                 fprintf(fp,"\n");
2306                 if (ferror(fp)) {
2307                         write_failure_alert_box(g_dest, errno);
2308                         fclose(fp);
2309                         return;
2310                 }
2311                 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_rev)->rows; i++) {
2312                         for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
2313                                 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_rev),i,j,&columnText);
2314                                 if (j == 0) {
2315                                         fprintf(fp,"%s",columnText);
2316                                 } else {
2317                                         fprintf(fp,",%s",columnText);
2318                                 }
2319                         }
2320                         fprintf(fp,"\n");
2321                         if (ferror(fp)) {
2322                                 write_failure_alert_box(g_dest, errno);
2323                                 fclose(fp);
2324                                 return;
2325                         }
2326                 }
2327                 if (fclose(fp) == EOF) {
2328                         write_failure_alert_box(g_dest, errno);
2329                         return;
2330                 }
2331         }
2332
2333         window_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
2334 }
2335
2336 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2337 {
2338         user_data->dlg.save_csv_as_w = NULL;
2339 }
2340
2341 /* when the user wants to save the csv information in a file */
2342 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
2343 {
2344         GtkWidget *vertb;
2345         GtkWidget *table1;
2346         GtkWidget *label_format;
2347         GtkWidget *channels_label;
2348         GSList *channels_group = NULL;
2349         GtkWidget *forward_rb;
2350         GtkWidget *reversed_rb;
2351         GtkWidget *both_rb;
2352         GtkWidget *ok_bt;
2353         
2354         if (user_data->dlg.save_csv_as_w != NULL) {
2355                 /* There's already a Save CSV info dialog box; reactivate it. */
2356                 reactivate_window(user_data->dlg.save_csv_as_w);
2357                 return;
2358         }
2359         
2360         user_data->dlg.save_csv_as_w = gtk_file_selection_new("Ethereal: Save Data As CSV");
2361         
2362         /* Container for each row of widgets */
2363         vertb = gtk_vbox_new(FALSE, 0);
2364         gtk_container_border_width(GTK_CONTAINER(vertb), 5);
2365         gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->action_area),
2366                 vertb, FALSE, FALSE, 0);
2367         gtk_widget_show (vertb);
2368         
2369         table1 = gtk_table_new (2, 4, FALSE);
2370         gtk_widget_show (table1);
2371         gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2372         gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2373         gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2374         
2375         label_format = gtk_label_new ("Format: Comma Separated Values");
2376         gtk_widget_show (label_format);
2377         gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2378                 (GtkAttachOptions) (GTK_FILL),
2379                 (GtkAttachOptions) (0), 0, 0);
2380         
2381         
2382         channels_label = gtk_label_new ("Channels:");
2383         gtk_widget_show (channels_label);
2384         gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2385                 (GtkAttachOptions) (GTK_FILL),
2386                 (GtkAttachOptions) (0), 0, 0);
2387         gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2388         
2389         forward_rb = gtk_radio_button_new_with_label (channels_group, "forward  ");
2390         channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
2391         gtk_widget_show (forward_rb);
2392         gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2393                 (GtkAttachOptions) (GTK_FILL),
2394                 (GtkAttachOptions) (0), 0, 0);
2395         
2396         reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2397         channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
2398         gtk_widget_show (reversed_rb);
2399         gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2400                 (GtkAttachOptions) (GTK_FILL),
2401                 (GtkAttachOptions) (0), 0, 0);
2402         
2403         both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2404         channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
2405         gtk_widget_show (both_rb);
2406         gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2407                 (GtkAttachOptions) (GTK_FILL),
2408                 (GtkAttachOptions) (0), 0, 0);
2409         
2410         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2411         
2412         ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->ok_button;
2413         OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
2414         OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
2415         OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
2416         OBJECT_SET_DATA(ok_bt, "user_data", user_data);
2417         SIGNAL_CONNECT(ok_bt, "clicked", save_csv_as_ok_cb,
2418                 user_data->dlg.save_csv_as_w);
2419         
2420         window_set_cancel_button(user_data->dlg.save_csv_as_w, 
2421                 GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button, NULL);
2422         
2423         SIGNAL_CONNECT(user_data->dlg.save_csv_as_w, "delete_event", window_delete_event_cb, NULL);
2424         SIGNAL_CONNECT(user_data->dlg.save_csv_as_w, "destroy",
2425                 save_csv_as_destroy_cb, user_data);
2426         
2427         gtk_widget_show(user_data->dlg.save_csv_as_w);
2428         window_present(user_data->dlg.save_csv_as_w);
2429 }
2430
2431
2432 /****************************************************************************/
2433 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2434 {
2435         /* Note that we no longer have a Save voice info dialog box. */
2436         user_data->dlg.save_voice_as_w = NULL;
2437 }
2438
2439 /****************************************************************************/
2440 /* here we save it into a file that user specified */
2441 /* XXX what about endians here? could go something wrong? */
2442 static gboolean copy_file(gchar *dest, gint channels, /*gint format,*/ user_data_t *user_data)
2443 {
2444         int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
2445         gint16 f_pd;
2446         gint16 r_pd;
2447         gchar pd[1];
2448         guint32 f_write_silence = 0;
2449         guint32 r_write_silence = 0;
2450         progdlg_t *progbar;
2451         guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2452         gboolean stop_flag = FALSE;
2453
2454         forw_fd = open(user_data->f_tempname, O_RDONLY | O_BINARY);
2455         if (forw_fd < 0) 
2456                 return FALSE;
2457         rev_fd = open(user_data->r_tempname, O_RDONLY | O_BINARY);
2458         if (rev_fd < 0) {
2459                 close(forw_fd); 
2460                 return FALSE;
2461         }
2462
2463         /* open file for saving */
2464         to_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
2465         if (to_fd < 0) {
2466                 close(forw_fd);
2467                 close(rev_fd);
2468                 return FALSE;
2469         }
2470
2471         progbar = create_progress_dlg("Saving voice in a file", dest, &stop_flag);
2472
2473         /* First we write the .au header. XXX Hope this is endian independant */
2474         /* the magic word 0x2e736e64 == .snd */
2475         *pd = (unsigned char)0x2e; write(to_fd, pd, 1);
2476         *pd = (unsigned char)0x73; write(to_fd, pd, 1);
2477         *pd = (unsigned char)0x6e; write(to_fd, pd, 1);
2478         *pd = (unsigned char)0x64; write(to_fd, pd, 1);
2479         /* header offset == 24 bytes */
2480         *pd = (unsigned char)0x00; write(to_fd, pd, 1);
2481         write(to_fd, pd, 1);
2482         write(to_fd, pd, 1);
2483         *pd = (unsigned char)0x18; write(to_fd, pd, 1);
2484         /* total length, it is permited to set this to 0xffffffff */
2485         *pd = (unsigned char)0xff; write(to_fd, pd, 1); 
2486         write(to_fd, pd, 1); 
2487         write(to_fd, pd, 1); 
2488         write(to_fd, pd, 1);
2489         /* encoding format == 8 bit ulaw */
2490         *pd = (unsigned char)0x00; write(to_fd, pd, 1);
2491         write(to_fd, pd, 1);
2492         write(to_fd, pd, 1);
2493         *pd = (unsigned char)0x01; write(to_fd, pd, 1);
2494         /* sample rate == 8000 Hz */
2495         *pd = (unsigned char)0x00; write(to_fd, pd, 1);
2496         write(to_fd, pd, 1);
2497         *pd = (unsigned char)0x1f; write(to_fd, pd, 1);
2498         *pd = (unsigned char)0x40; write(to_fd, pd, 1);
2499         /* channels == 1 */
2500         *pd = (unsigned char)0x00; write(to_fd, pd, 1);
2501         write(to_fd, pd, 1);
2502         write(to_fd, pd, 1);
2503         *pd = (unsigned char)0x01; write(to_fd, pd, 1);
2504         
2505         switch (channels) {
2506                 /* only forward direction */
2507                 case 1: {
2508                         progbar_count = user_data->forward.saveinfo.count;
2509                         progbar_quantum = user_data->forward.saveinfo.count/100;
2510                         while ((fread = read(forw_fd, &f_pd, 2)) > 0) {
2511                                 if(stop_flag) 
2512                                         break;
2513                                 if((count > progbar_nextstep) && (count <= progbar_count)) {
2514                                         update_progress_dlg(progbar, 
2515                                                 (gfloat) count/progbar_count, "Saving");
2516                                         progbar_nextstep = progbar_nextstep + progbar_quantum;
2517                                 }
2518                                 count++;
2519                                 *pd = (unsigned char)linear2ulaw(f_pd);
2520                                 fwritten = write(to_fd, pd, 1);
2521                                 if ((fwritten*2 < fread) || (fwritten < 0) || (fread < 0)) {
2522                                         close(forw_fd);
2523                                         close(rev_fd);
2524                                         close(to_fd);
2525                                         destroy_progress_dlg(progbar);
2526                                         return FALSE;
2527                                 }
2528                         }
2529                         break;
2530                 }
2531                 /* only reversed direction */
2532                 case 2: {
2533                         progbar_count = user_data->reversed.saveinfo.count;
2534                         progbar_quantum = user_data->reversed.saveinfo.count/100;
2535                         while ((rread = read(rev_fd, &r_pd, 2)) > 0) {
2536                                 if(stop_flag) 
2537                                         break;
2538                                 if((count > progbar_nextstep) && (count <= progbar_count)) {
2539                                         update_progress_dlg(progbar, 
2540                                                 (gfloat) count/progbar_count, "Saving");
2541                                         progbar_nextstep = progbar_nextstep + progbar_quantum;
2542                                 }
2543                                 count++;
2544                                 *pd = (unsigned char)linear2ulaw(r_pd);
2545                                 rwritten = write(to_fd, pd, 1);
2546                                 if ((rwritten*2 < rread) || (rwritten < 0) || (rread < 0)) {
2547                                         close(forw_fd);
2548                                         close(rev_fd);
2549                                         close(to_fd);
2550                                         destroy_progress_dlg(progbar);
2551                                         return FALSE;
2552                                 }
2553                         }
2554                         break;
2555                 }
2556                 /* both directions */
2557                 default: {
2558                         (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ? 
2559                                         (progbar_count = user_data->forward.saveinfo.count) : 
2560                                                 (progbar_count = user_data->reversed.saveinfo.count);
2561                         progbar_quantum = progbar_count/100;
2562                         /* since conversation in one way can start later than in the other one, 
2563                          * we have to write some silence information for one channel */
2564                         if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2565                                 f_write_silence = (guint32)
2566                                         ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2567                         }
2568                         else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2569                                 r_write_silence = (guint32)
2570                                         ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2571                         }
2572                         for(;;) {
2573                                 if(stop_flag) 
2574                                         break;
2575                                 if((count > progbar_nextstep) && (count <= progbar_count)) {
2576                                         update_progress_dlg(progbar, 
2577                                                 (gfloat) count/progbar_count, "Saving");
2578                                         progbar_nextstep = progbar_nextstep + progbar_quantum;
2579                                 }
2580                                 count++;
2581                                 if(f_write_silence > 0) {
2582                                         rread = read(rev_fd, &r_pd, 2);
2583                                         f_pd = 0;
2584                                         fread = 1;
2585                                         f_write_silence--;
2586                                 }
2587                                 else if(r_write_silence > 0) {
2588                                         fread = read(forw_fd, &f_pd, 2);
2589                                         r_pd = 0;
2590                                         rread = 1;
2591                                         r_write_silence--;
2592                                 }
2593                                 else {
2594                                         fread = read(forw_fd, &f_pd, 2); 
2595                                         rread = read(rev_fd, &r_pd, 2);
2596                                 }
2597                                 if ((rread == 0) && (fread == 0)) 
2598                                         break;
2599                                 *pd = (unsigned char)linear2ulaw( (f_pd + r_pd)/2 );
2600                                 rwritten = write(to_fd, pd, 1);
2601                                 if ((rwritten < 0) || (rread < 0) || (fread < 0)) {
2602                                         close(forw_fd);
2603                                         close(rev_fd);
2604                                         close(to_fd);
2605                                         destroy_progress_dlg(progbar);
2606                                         return FALSE;
2607                                 }
2608                         }
2609                 }
2610         }
2611         destroy_progress_dlg(progbar);
2612         close(forw_fd);
2613         close(rev_fd);
2614         close(to_fd);
2615         return TRUE;
2616 }
2617
2618
2619 /****************************************************************************/
2620 /* the user wants to save in a file */
2621 /* XXX support for different formats is currently commented out */
2622 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
2623 {
2624         gchar *g_dest;
2625         /*GtkWidget *wav, *au, *sw;*/
2626         GtkWidget *rev, *forw, *both;
2627         user_data_t *user_data;
2628         gint channels /*, format*/;
2629         
2630         g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
2631         
2632         /* Perhaps the user specified a directory instead of a file.
2633         Check whether they did. */
2634         if (test_for_directory(g_dest) == EISDIR) {
2635                 /* It's a directory - set the file selection box to display it. */
2636                 set_last_open_dir(g_dest);
2637                 g_free(g_dest);
2638                 file_selection_set_current_folder(fs, get_last_open_dir());
2639                 return;
2640         }
2641         
2642         /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb");
2643         au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb");
2644         sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/
2645         rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb");
2646         forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb");
2647         both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb");
2648         user_data = (user_data_t *)OBJECT_GET_DATA(ok_bt, "user_data");
2649         
2650         /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2651         * we don't support that codec. So we pop up a warning. Maybe it would be better to
2652         * disable the ok button or disable the buttons for direction if only one is not ok. The
2653         * problem is if we open the save voice dialog and then click the refresh button and maybe 
2654         * the state changes, so we can't save anymore. In this case we should be able to update
2655         * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2656         */
2657         
2658         /* we can not save in both dirctions */
2659         if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2660                 /* there are many combinations here, we just exit when first matches */
2661                 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) || 
2662                         (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2663                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2664                         "Can't save in a file: Unsupported codec!");
2665                 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) || 
2666                         (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2667                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2668                         "Can't save in a file: Wrong length of captured packets!");
2669                 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) || 
2670                         (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2671                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2672                         "Can't save in a file: RTP data with padding!");
2673                 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) || 
2674                         (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2675                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2676                         "Can't save in a file: Not all data in all packets was captured!");
2677                 else  
2678                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2679                         "Can't save in a file: File I/O problem!");
2680                 return;
2681         }
2682         /* we can not save forward direction */
2683         else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2684                 (GTK_TOGGLE_BUTTON (both)->active))) {  
2685                 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2686                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2687                         "Can't save forward direction in a file: Unsupported codec!");
2688                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2689                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2690                         "Can't save forward direction in a file: Wrong length of captured packets!");
2691                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2692                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2693                         "Can't save forward direction in a file: RTP data with padding!");
2694                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2695                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2696                         "Can't save forward direction in a file: Not all data in all packets was captured!");
2697                 else
2698                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2699                         "Can't save forward direction in a file: File I/O problem!");
2700                 return;
2701         }
2702         /* we can not save reversed direction */
2703         else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2704                 (GTK_TOGGLE_BUTTON (both)->active))) {  
2705                 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2706                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2707                         "Can't save reversed direction in a file: Unsupported codec!");
2708                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2709                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2710                         "Can't save reversed direction in a file: Wrong length of captured packets!");
2711                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2712                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2713                         "Can't save reversed direction in a file: RTP data with padding!");
2714                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2715                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2716                         "Can't save reversed direction in a file: Not all data in all packets was captured!");
2717                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2718                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2719                         "Can't save reversed direction in a file: No RTP data!");
2720                 else
2721                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2722                         "Can't save reversed direction in a file: File I/O problem!");
2723                 return;
2724         }
2725         
2726         /*if (GTK_TOGGLE_BUTTON (wav)->active)
2727         format = 1;
2728         else if (GTK_TOGGLE_BUTTON (au)->active)
2729         format = 2;
2730         else if (GTK_TOGGLE_BUTTON (sw)->active)
2731         format = 3;*/
2732         
2733         if (GTK_TOGGLE_BUTTON (rev)->active)
2734                 channels = 2;
2735         else if (GTK_TOGGLE_BUTTON (both)->active)
2736                 channels = 3;
2737         else 
2738                 channels = 1;
2739         
2740         if(!copy_file(g_dest, channels/*, format*/, user_data)) {
2741                 /* XXX - report the error type! */
2742                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2743                         "An error occured while saving voice in a file!");
2744                 return;
2745         }
2746         
2747         window_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
2748 }
2749
2750 /****************************************************************************/
2751 /* when the user wants to save the voice information in a file */
2752 /* XXX support for different formats is currently commented out */
2753 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2754 {
2755         GtkWidget *vertb;
2756         GtkWidget *table1;
2757         GtkWidget *label_format;
2758         GtkWidget *channels_label;
2759         /*GSList *format_group = NULL;*/
2760         GSList *channels_group = NULL;
2761         GtkWidget *forward_rb;
2762         GtkWidget *reversed_rb;
2763         GtkWidget *both_rb;
2764         /*GtkWidget *wav_rb; GtkWidget *au_rb; GtkWidget *sw_rb;*/
2765         GtkWidget *ok_bt;
2766         
2767         /* if we can't save in a file: wrong codec, cut packets or other errors */
2768         /* shold the error arise here or later when you click ok button ? 
2769         * if we do it here, then we must disable the refresh button, so we don't do it here */
2770         
2771         if (user_data->dlg.save_voice_as_w != NULL) {
2772                 /* There's already a Save voice info dialog box; reactivate it. */
2773                 reactivate_window(user_data->dlg.save_voice_as_w);
2774                 return;
2775         }
2776         
2777     /* XXX - use file_selection from dlg_utils instead! */
2778         user_data->dlg.save_voice_as_w = gtk_file_selection_new("Ethereal: Save Payload As ...");
2779         
2780         /* Container for each row of widgets */
2781         vertb = gtk_vbox_new(FALSE, 0);
2782         gtk_container_border_width(GTK_CONTAINER(vertb), 5);
2783         gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->action_area),
2784                 vertb, FALSE, FALSE, 0);
2785         gtk_widget_show (vertb);
2786         
2787         table1 = gtk_table_new (2, 4, FALSE);
2788         gtk_widget_show (table1);
2789         gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2790         gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2791         gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2792         
2793         label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2794         gtk_widget_show (label_format);
2795         gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2796                 (GtkAttachOptions) (GTK_FILL),
2797                 (GtkAttachOptions) (0), 0, 0);
2798         
2799         /* we support .au - ulaw*/ 
2800         /*      wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
2801         format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb));
2802         gtk_widget_show (wav_rb);
2803         gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2804         (GtkAttachOptions) (GTK_FILL),
2805         (GtkAttachOptions) (0), 0, 0);
2806         
2807           sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit  ");
2808           format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb));
2809           gtk_widget_show (sw_rb);
2810           gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2811           (GtkAttachOptions) (GTK_FILL),
2812           (GtkAttachOptions) (0), 0, 0);
2813           au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2814           format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
2815           gtk_widget_show (au_rb);
2816           gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2817           (GtkAttachOptions) (GTK_FILL),
2818           (GtkAttachOptions) (0), 0, 0);
2819         */ 
2820         
2821         channels_label = gtk_label_new ("Channels:");
2822         gtk_widget_show (channels_label);
2823         gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2824                 (GtkAttachOptions) (GTK_FILL),
2825                 (GtkAttachOptions) (0), 0, 0);
2826         gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2827         
2828         forward_rb = gtk_radio_button_new_with_label (channels_group, "forward  ");
2829         channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
2830         gtk_widget_show (forward_rb);
2831         gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2832                 (GtkAttachOptions) (GTK_FILL),
2833                 (GtkAttachOptions) (0), 0, 0);
2834         
2835         reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2836         channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
2837         gtk_widget_show (reversed_rb);
2838         gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2839                 (GtkAttachOptions) (GTK_FILL),
2840                 (GtkAttachOptions) (0), 0, 0);
2841         
2842         both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2843         channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
2844         gtk_widget_show (both_rb);
2845         gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2846                 (GtkAttachOptions) (GTK_FILL),
2847                 (GtkAttachOptions) (0), 0, 0);
2848         
2849         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2850         
2851         /* if one direction is nok we don't allow saving 
2852         XXX this is not ok since the user can click the refresh button and cause changes
2853         but we can not update this window. So we move all the decision on the time the ok
2854         button is clicked
2855         if (user_data->forward.saved == FALSE) {
2856         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2857         gtk_widget_set_sensitive(forward_rb, FALSE);
2858         gtk_widget_set_sensitive(both_rb, FALSE);
2859         }
2860         else if (user_data->reversed.saved == FALSE) {
2861         gtk_widget_set_sensitive(reversed_rb, FALSE);
2862         gtk_widget_set_sensitive(both_rb, FALSE);
2863         }
2864         */
2865         
2866         ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->ok_button;
2867         /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb);
2868         OBJECT_SET_DATA(ok_bt, "au_rb", au_rb);
2869         OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/
2870         OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
2871         OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
2872         OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
2873         OBJECT_SET_DATA(ok_bt, "user_data", user_data);
2874         SIGNAL_CONNECT(ok_bt, "clicked", save_voice_as_ok_cb,
2875                        user_data->dlg.save_voice_as_w);
2876
2877     window_set_cancel_button(user_data->dlg.save_voice_as_w, 
2878       GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button, window_cancel_button_cb);
2879
2880     SIGNAL_CONNECT(user_data->dlg.save_voice_as_w, "delete_event", 
2881                         window_delete_event_cb, NULL);
2882         SIGNAL_CONNECT(user_data->dlg.save_voice_as_w, "destroy",
2883                         save_voice_as_destroy_cb, user_data);
2884
2885         gtk_widget_show(user_data->dlg.save_voice_as_w);
2886     window_present(user_data->dlg.save_voice_as_w);
2887 }
2888
2889
2890 /****************************************************************************/
2891 /* when we are finished with redisection, we add the label for the statistic */
2892 static void draw_stat(user_data_t *user_data)
2893 {
2894         gchar label_max[200];
2895         guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
2896                 - user_data->forward.statinfo.start_seq_nr + 1;
2897         guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
2898                 - user_data->reversed.statinfo.start_seq_nr + 1;
2899         gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr;
2900         gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr;
2901         double f_perc, r_perc;
2902         if (f_expected){
2903                 f_perc = (double)(f_lost*100)/(double)f_expected;
2904         } else {
2905                 f_perc = 0;
2906         }
2907         if (r_expected){
2908                 r_perc = (double)(r_lost*100)/(double)r_expected;
2909         } else {
2910                 r_perc = 0;
2911         } 
2912                 
2913         g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
2914                 "Total RTP packets = %u   (expected %u)   Lost RTP packets = %d (%.2f%%)"
2915                 "   Sequence errors = %u",
2916                 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
2917                 user_data->forward.statinfo.total_nr,
2918                 f_expected, f_lost, f_perc, user_data->forward.statinfo.sequence);
2919
2920         gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2921
2922         g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
2923                 "Total RTP packets = %u   (expected %u)   Lost RTP packets = %d (%.2f%%)"
2924                 "   Sequence errors = %u",
2925                 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
2926                 user_data->reversed.statinfo.total_nr,
2927                 r_expected, r_lost, r_perc, user_data->reversed.statinfo.sequence);
2928
2929         gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2930
2931         return ;
2932 }
2933
2934
2935
2936 /****************************************************************************/
2937 /* append a line to clist */
2938 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
2939                          double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
2940                          gchar *timeStr, guint32 pkt_len, GdkColor *color)
2941 {
2942         guint added_row;
2943         gchar *data[9];
2944         gchar field[9][32];
2945         char *savelocale;
2946
2947         data[0]=&field[0][0];
2948         data[1]=&field[1][0];
2949         data[2]=&field[2][0];
2950         data[3]=&field[3][0];
2951         data[4]=&field[4][0];
2952         data[5]=&field[5][0];
2953         data[6]=&field[6][0];
2954         data[7]=&field[7][0];
2955         data[8]=&field[8][0];
2956
2957         /* save the current locale */
2958         savelocale = setlocale(LC_NUMERIC, NULL);
2959         /* switch to "C" locale to avoid problems with localized decimal separators
2960                 in g_snprintf("%f") functions */
2961         setlocale(LC_NUMERIC, "C");
2962         g_snprintf(field[0], 20, "%u", number);
2963         g_snprintf(field[1], 20, "%u", seq_num);
2964         g_snprintf(field[2], 20, "%.2f", delta);
2965         g_snprintf(field[3], 20, "%.2f", jitter);
2966         g_snprintf(field[4], 20, "%.2f", bandwidth);
2967         g_snprintf(field[5], 20, "%s", marker? "SET" : "");
2968         g_snprintf(field[6], 40, "%s", status);
2969         g_snprintf(field[7], 32, "%s", timeStr);
2970         g_snprintf(field[8], 20, "%u", pkt_len);
2971         /* restore previous locale setting */
2972         setlocale(LC_NUMERIC, savelocale);
2973
2974         added_row = gtk_clist_append(GTK_CLIST(clist), data);
2975         gtk_clist_set_row_data(GTK_CLIST(clist), added_row, GUINT_TO_POINTER(number));
2976         gtk_clist_set_background(GTK_CLIST(clist), added_row, color);
2977 }
2978
2979
2980 /****************************************************************************/
2981 /* callback for sorting columns of clist */
2982 static gint rtp_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
2983 {
2984         char *text1 = NULL;
2985         char *text2 = NULL;
2986         int i1, i2;
2987         double f1, f2;
2988
2989         const GtkCListRow *row1 = ptr1;
2990         const GtkCListRow *row2 = ptr2;
2991
2992         text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
2993         text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
2994
2995         switch(clist->sort_column){
2996         /* columns representing strings */
2997         case 5:
2998         case 6:
2999         case 7:
3000                 return strcmp (text1, text2);
3001         /* columns representing ints */
3002         case 0:
3003         case 1:
3004         case 8:
3005                 i1=atoi(text1);
3006                 i2=atoi(text2);
3007                 return i1-i2;
3008         /* columns representing floats */
3009         case 2:
3010         case 3:
3011         case 4:
3012                 f1=atof(text1);
3013                 f2=atof(text2);
3014                 if (fabs(f1-f2)<0.0000005)
3015                         return 0;
3016                 if (f1<f2)
3017                         return -1;
3018                 return 1;
3019         }
3020         g_assert_not_reached();
3021         return 0;
3022 }
3023
3024
3025 /****************************************************************************/
3026 static void
3027 click_column_cb(GtkCList *clist, gint column, gpointer data)
3028 {
3029         column_arrows *col_arrows = (column_arrows *) data;
3030         int i;
3031
3032         gtk_clist_freeze(clist);
3033
3034         for (i = 0; i < NUM_COLS; i++) {
3035                 gtk_widget_hide(col_arrows[i].ascend_pm);
3036                 gtk_widget_hide(col_arrows[i].descend_pm);
3037         }
3038
3039         if (column == clist->sort_column) {
3040                 if (clist->sort_type == GTK_SORT_ASCENDING) {
3041                         clist->sort_type = GTK_SORT_DESCENDING;
3042                         gtk_widget_show(col_arrows[column].descend_pm);
3043                 } else {
3044                         clist->sort_type = GTK_SORT_ASCENDING;
3045                         gtk_widget_show(col_arrows[column].ascend_pm);
3046                 }
3047         } else {
3048                 clist->sort_type = GTK_SORT_ASCENDING;
3049                 gtk_widget_show(col_arrows[column].ascend_pm);
3050                 gtk_clist_set_sort_column(clist, column);
3051         }
3052         gtk_clist_thaw(clist);
3053
3054         gtk_clist_sort(clist);
3055 }
3056
3057
3058 /****************************************************************************/
3059 /* Add the packet list */
3060 static
3061 GtkWidget* create_clist(user_data_t* user_data)
3062 {
3063         GtkWidget* clist_fwd;
3064
3065         /* clist for the information */
3066         clist_fwd = gtk_clist_new(NUM_COLS);
3067         gtk_widget_show(clist_fwd);
3068         SIGNAL_CONNECT(clist_fwd, "select_row", on_clist_select_row, user_data);
3069
3070         gtk_clist_column_titles_show(GTK_CLIST(clist_fwd));
3071         gtk_clist_set_compare_func(GTK_CLIST(clist_fwd), rtp_sort_column);
3072         gtk_clist_set_sort_column(GTK_CLIST(clist_fwd), 0);
3073         gtk_clist_set_sort_type(GTK_CLIST(clist_fwd), GTK_SORT_ASCENDING);
3074
3075         /* hide date and length column */
3076         gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 7, FALSE);
3077         gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 8, FALSE);
3078
3079         /* column widths and justification */
3080         gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 0, 60);
3081         gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 1, 75);
3082         gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 2, 75);
3083         gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 3, 75);
3084         gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 4, 50);
3085         gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 5, 75);
3086         gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 0, GTK_JUSTIFY_RIGHT);
3087         gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 1, GTK_JUSTIFY_RIGHT);
3088         gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 2, GTK_JUSTIFY_CENTER);
3089         gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 3, GTK_JUSTIFY_CENTER);
3090         gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 4, GTK_JUSTIFY_CENTER);
3091         gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 5, GTK_JUSTIFY_CENTER);
3092         gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 6, GTK_JUSTIFY_CENTER);
3093         return clist_fwd;
3094 }
3095
3096
3097 /****************************************************************************/
3098 /* Add the sort by column feature for a packet clist */
3099 static
3100 column_arrows* add_sort_by_column(GtkWidget* window, GtkWidget* clist,
3101                                                                   user_data_t* user_data _U_)
3102 {
3103         column_arrows *col_arrows;
3104         GdkBitmap *ascend_bm, *descend_bm;
3105         GdkPixmap *ascend_pm, *descend_pm;
3106         GtkStyle *win_style;
3107         GtkWidget *column_lb;
3108         int i;
3109
3110         col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS);
3111         win_style = gtk_widget_get_style(window);
3112         ascend_pm = gdk_pixmap_create_from_xpm_d(window->window,
3113                         &ascend_bm,
3114                         &win_style->bg[GTK_STATE_NORMAL],
3115                         (gchar **)clist_ascend_xpm);
3116         descend_pm = gdk_pixmap_create_from_xpm_d(window->window,
3117                         &descend_bm,
3118                         &win_style->bg[GTK_STATE_NORMAL],
3119                         (gchar **)clist_descend_xpm);
3120
3121         for (i=0; i<NUM_COLS; i++) {
3122                 col_arrows[i].table = gtk_table_new(2, 2, FALSE);
3123                 gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
3124                 column_lb = gtk_label_new(titles[i]);
3125                 gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
3126                 gtk_widget_show(column_lb);
3127
3128                 col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
3129                 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
3130                 col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
3131                 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
3132                 /* make packet-nr be the default sort order */
3133                 if (i == 0) {
3134                         gtk_widget_show(col_arrows[i].ascend_pm);
3135                 }
3136                 gtk_clist_set_column_widget(GTK_CLIST(clist), i, col_arrows[i].table);
3137                 gtk_widget_show(col_arrows[i].table);
3138         }
3139
3140         SIGNAL_CONNECT(clist, "click-column", click_column_cb, col_arrows);
3141
3142         return col_arrows;
3143 }
3144
3145 /****************************************************************************/
3146 /* Create the dialog box with all widgets */
3147 void create_rtp_dialog(user_data_t* user_data)
3148 {
3149         GtkWidget *window = NULL;
3150         GtkWidget *clist_fwd;
3151         GtkWidget *clist_rev;
3152         GtkWidget *label_stats_fwd;
3153         GtkWidget *label_stats_rev;
3154         GtkWidget *notebook;
3155
3156         GtkWidget *main_vb, *page, *page_r;
3157         GtkWidget *label;
3158         GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3159         GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3160 #ifdef USE_CONVERSATION_GRAPH
3161         GtkWidget *graph_bt;
3162 #endif
3163         GtkWidget *graph_bt;
3164         gchar label_forward[150];
3165         gchar label_reverse[150];
3166
3167         gchar str_ip_src[16];
3168         gchar str_ip_dst[16];
3169         column_arrows *col_arrows_fwd;
3170         column_arrows *col_arrows_rev;
3171         
3172         window = window_new(GTK_WINDOW_TOPLEVEL, "Ethereal: RTP Stream Analysis");
3173         gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3174
3175         /* Container for each row of widgets */
3176         main_vb = gtk_vbox_new(FALSE, 2);
3177         gtk_container_border_width(GTK_CONTAINER(main_vb), 2);
3178         gtk_container_add(GTK_CONTAINER(window), main_vb);
3179         gtk_widget_show(main_vb);
3180
3181         /* Notebooks... */
3182         strcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)));
3183         strcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)));
3184
3185         g_snprintf(label_forward, 149, 
3186                 "Analysing stream from  %s port %u  to  %s port %u   SSRC = %u", 
3187                 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3188
3189
3190         strcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)));
3191         strcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)));
3192
3193         g_snprintf(label_reverse, 149,
3194                 "Analysing stream from  %s port %u  to  %s port %u   SSRC = %u", 
3195                 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3196
3197         /* Start a notebook for flipping between sets of changes */
3198         notebook = gtk_notebook_new();
3199         gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3200         OBJECT_SET_DATA(window, "notebook", notebook);
3201
3202         user_data->dlg.notebook_signal_id = SIGNAL_CONNECT(notebook, "switch_page", on_notebook_switch_page,
3203                        user_data);
3204
3205         /* page for forward connection */
3206         page = gtk_vbox_new(FALSE, 8);
3207         gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3208
3209         /* direction label */
3210         label = gtk_label_new(label_forward);
3211         gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3212
3213         /* place for some statistics */
3214         label_stats_fwd = gtk_label_new("\n");
3215         gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3216
3217         /* scrolled window */
3218         scrolled_window = scrolled_window_new(NULL, NULL);
3219
3220         /* packet clist */
3221         clist_fwd = create_clist(user_data);
3222         gtk_widget_show(clist_fwd);
3223         gtk_container_add(GTK_CONTAINER(scrolled_window), clist_fwd);
3224         gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3225         gtk_widget_show(scrolled_window);
3226
3227         /* tab */
3228         label = gtk_label_new("  Forward Direction  ");
3229         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3230
3231         /* same page for reversed connection */
3232         page_r = gtk_vbox_new(FALSE, 8);
3233         gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3234         label = gtk_label_new(label_reverse);
3235         gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3236         label_stats_rev = gtk_label_new("\n");
3237         gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3238
3239         scrolled_window_r = scrolled_window_new(NULL, NULL);
3240
3241         clist_rev = create_clist(user_data);
3242         gtk_widget_show(clist_rev);
3243         gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_rev);
3244         gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3245         gtk_widget_show(scrolled_window_r);
3246
3247         label = gtk_label_new("  Reversed Direction  ");
3248         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3249
3250         /* page for help&about or future
3251         page_help = gtk_hbox_new(FALSE, 5);
3252         label = gtk_label_new("     Future    ");
3253         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3254         frame = gtk_frame_new("");
3255         text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3256         gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3257         gtk_container_add(GTK_CONTAINER(frame), text);
3258         gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3259         gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3260         */
3261
3262         /* show all notebooks */
3263         gtk_widget_show_all(notebook);
3264
3265         /* buttons */
3266         box4 = gtk_hbutton_box_new();
3267         gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3268         gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3269         gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3270         gtk_button_box_set_spacing(GTK_BUTTON_BOX (box4), 0);
3271         gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0);
3272         gtk_widget_show(box4);
3273
3274         voice_bt = gtk_button_new_with_label("Save payload...");
3275         gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3276         gtk_widget_show(voice_bt);
3277         SIGNAL_CONNECT(voice_bt, "clicked", on_save_bt_clicked, user_data);
3278
3279         csv_bt = gtk_button_new_with_label("Save as CSV...");
3280         gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3281         gtk_widget_show(csv_bt);
3282         SIGNAL_CONNECT(csv_bt, "clicked", save_csv_as_cb, user_data);
3283
3284         refresh_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_REFRESH);
3285         gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3286         gtk_widget_show(refresh_bt);
3287         SIGNAL_CONNECT(refresh_bt, "clicked", on_refresh_bt_clicked, user_data);
3288
3289         goto_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_JUMP_TO);
3290         gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3291         gtk_widget_show(goto_bt);
3292         SIGNAL_CONNECT(goto_bt, "clicked", on_goto_bt_clicked, user_data);
3293
3294         graph_bt = gtk_button_new_with_label("Graph");
3295         gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3296         gtk_widget_show(graph_bt);
3297         SIGNAL_CONNECT(graph_bt, "clicked", on_graph_bt_clicked, user_data);    
3298
3299
3300 #ifdef USE_CONVERSATION_GRAPH
3301         graph_bt = gtk_button_new_with_label("Graph");
3302         gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3303         gtk_widget_show(graph_bt);
3304         SIGNAL_CONNECT(graph_bt, "clicked", on_graph_bt_clicked, user_data);
3305 #endif
3306
3307         next_bt = gtk_button_new_with_label("Next non-Ok");
3308         gtk_container_add(GTK_CONTAINER(box4), next_bt);
3309         gtk_widget_show(next_bt);
3310         SIGNAL_CONNECT(next_bt, "clicked", on_next_bt_clicked, user_data);
3311
3312         close_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
3313         gtk_container_add(GTK_CONTAINER(box4), close_bt);
3314     GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3315         gtk_widget_show(close_bt);
3316     window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3317
3318     SIGNAL_CONNECT(window, "delete_event", window_delete_event_cb, NULL);
3319         SIGNAL_CONNECT(window, "destroy", on_destroy, user_data);
3320
3321     gtk_widget_show(window);
3322     window_present(window);
3323
3324         /* sort by column feature */
3325         col_arrows_fwd = add_sort_by_column(window, clist_fwd, user_data);
3326         col_arrows_rev = add_sort_by_column(window, clist_rev, user_data);
3327
3328         /* some widget references need to be saved for outside use */
3329         user_data->dlg.window = window;
3330         user_data->dlg.clist_fwd = GTK_CLIST(clist_fwd);
3331         user_data->dlg.clist_rev = GTK_CLIST(clist_rev);
3332         user_data->dlg.label_stats_fwd = label_stats_fwd;
3333         user_data->dlg.label_stats_rev = label_stats_rev;
3334         user_data->dlg.notebook = notebook;
3335         user_data->dlg.selected_clist = GTK_CLIST(clist_fwd);
3336         user_data->dlg.selected_row = 0;
3337         user_data->dlg.col_arrows_fwd = col_arrows_fwd;
3338         user_data->dlg.col_arrows_rev = col_arrows_rev;
3339 }
3340
3341
3342 /****************************************************************************/
3343 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3344                                                         gchar* proto_field, guint32* p_result)
3345 {
3346         field_info            *finfo;
3347         proto_node            *proto_sibling_node;
3348         header_field_info     *hfssrc;
3349         ipv4_addr             *ipv4;
3350
3351         finfo = PITEM_FINFO(ptree_node);
3352
3353         if (hfinformation==(finfo->hfinfo)) {
3354                 hfssrc = proto_registrar_get_byname(proto_field);
3355                 if (hfssrc == NULL)
3356                         return FALSE;
3357                 for(ptree_node=ptree_node->first_child; ptree_node!=NULL; 
3358                                         ptree_node=ptree_node->next) {
3359                         finfo=PITEM_FINFO(ptree_node);
3360                         if (hfssrc==finfo->hfinfo) {
3361                                 if (hfinformation->type==FT_IPv4) {
3362                                         ipv4 = fvalue_get(&finfo->value);
3363                                         *p_result = ipv4_get_net_order_addr(ipv4);
3364                                 }
3365                                 else {
3366                                         *p_result = fvalue_get_integer(&finfo->value);
3367                                 }
3368                                 return TRUE;
3369                         }
3370                 }
3371         }
3372
3373         proto_sibling_node = ptree_node->next;
3374
3375         if (proto_sibling_node) {
3376                 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3377         }
3378         else
3379         return FALSE;
3380 }
3381
3382 /****************************************************************************/
3383 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3384                                                                                          gchar* proto_name,
3385                                                                                          gchar* proto_field,
3386                                                                                          guint32* p_result)
3387 {
3388         proto_node      *ptree_node;
3389         header_field_info     *hfinformation;
3390
3391         hfinformation = proto_registrar_get_byname(proto_name);
3392         if (hfinformation == NULL)
3393                 return FALSE;
3394
3395         ptree_node = ((proto_node *)protocol_tree)->first_child;
3396         if (!ptree_node)
3397                 return FALSE;
3398
3399         return process_node(ptree_node, hfinformation, proto_field, p_result);
3400 }
3401
3402
3403 /* XXX just copied from gtk/rpc_stat.c */
3404 void protect_thread_critical_region(void);
3405 void unprotect_thread_critical_region(void);
3406
3407 /****************************************************************************/
3408 void rtp_analysis(
3409                 address *ip_src_fwd,
3410                 guint16 port_src_fwd,
3411                 address *ip_dst_fwd,
3412                 guint16 port_dst_fwd,
3413                 guint32 ssrc_fwd,
3414                 address *ip_src_rev,
3415                 guint16 port_src_rev,
3416                 address *ip_dst_rev,
3417                 guint16 port_dst_rev,
3418                 guint32 ssrc_rev
3419                 )
3420 {
3421         user_data_t *user_data;
3422         int fd;
3423         int i;
3424         static color_t col[MAX_GRAPHS] = {
3425                 {0,     0x0000, 0x0000, 0x0000},
3426                 {0,     0xffff, 0x0000, 0x0000},
3427                 {0,     0x0000, 0xffff, 0x0000},
3428                 {0,     0x0000, 0x0000, 0xffff}
3429         };
3430
3431         /* init */
3432         user_data = g_malloc(sizeof(user_data_t));
3433
3434         COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3435         user_data->port_src_fwd = port_src_fwd;
3436         COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3437         user_data->port_dst_fwd = port_dst_fwd;
3438         user_data->ssrc_fwd = ssrc_fwd;
3439         COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3440         user_data->port_src_rev = port_src_rev;
3441         COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3442         user_data->port_dst_rev = port_dst_rev;
3443         user_data->ssrc_rev = ssrc_rev;
3444
3445
3446         /* file names for storing sound data */
3447         /*XXX: check for errors*/
3448         fd = create_tempfile(user_data->f_tempname, sizeof(user_data->f_tempname),
3449                 "ether_rtp_f");
3450         close(fd);
3451         fd = create_tempfile(user_data->r_tempname, sizeof(user_data->r_tempname),
3452                 "ether_rtp_r");
3453         close(fd);
3454         user_data->forward.saveinfo.fp = NULL;
3455         user_data->reversed.saveinfo.fp = NULL;
3456         user_data->dlg.save_voice_as_w = NULL;
3457         user_data->dlg.save_csv_as_w = NULL;
3458         user_data->dlg.dialog_graph.window = NULL;
3459
3460 #ifdef USE_CONVERSATION_GRAPH
3461         user_data->dlg.graph_window = NULL;
3462         user_data->series_fwd.value_pairs = NULL;
3463         user_data->series_rev.value_pairs = NULL;
3464 #endif
3465
3466         /* init dialog_graph */
3467         user_data->dlg.dialog_graph.needs_redraw=TRUE;
3468         user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE];
3469         user_data->dlg.dialog_graph.draw_area=NULL;
3470         user_data->dlg.dialog_graph.pixmap=NULL;
3471         user_data->dlg.dialog_graph.scrollbar=NULL;
3472         user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3473         user_data->dlg.dialog_graph.pixmap_width=500;
3474         user_data->dlg.dialog_graph.pixmap_height=200;
3475         user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
3476         user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3477         user_data->dlg.dialog_graph.last_interval=0xffffffff;
3478         user_data->dlg.dialog_graph.max_interval=0;
3479         user_data->dlg.dialog_graph.num_items=0;
3480         user_data->dlg.dialog_graph.start_time = -1;
3481
3482         for(i=0;i<MAX_GRAPHS;i++){
3483                 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3484                 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3485                 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3486                 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3487                 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3488                 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3489                 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3490                 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3491         }
3492
3493         /* create the dialog box */
3494         create_rtp_dialog(user_data);
3495
3496         /* proceed as if the Refresh button would have been pressed */
3497         on_refresh_bt_clicked(NULL, user_data);
3498 }
3499
3500 /****************************************************************************/
3501 /* entry point from main menu */
3502 void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_) 
3503 {
3504         address ip_src_fwd;
3505         guint16 port_src_fwd;
3506         address ip_dst_fwd;
3507         guint16 port_dst_fwd;
3508         guint32 ssrc_fwd = 0;
3509         address ip_src_rev;
3510         guint16 port_src_rev;
3511         address ip_dst_rev;
3512         guint16 port_dst_rev;
3513         guint32 ssrc_rev = 0;
3514         unsigned int version_fwd;
3515
3516         gchar filter_text[256];
3517         dfilter_t *sfcode;
3518         capture_file *cf;
3519         epan_dissect_t *edt;
3520         gint err;
3521         gchar *err_info;
3522         gboolean frame_matched;
3523         frame_data *fdata;
3524         GList *strinfo_list;
3525         GList *filtered_list = NULL;
3526         rtp_stream_info_t *strinfo;
3527         guint nfound;
3528
3529         /* Try to compile the filter. */
3530         strcpy(filter_text,"rtp && (ip || ipv6)");
3531         if (!dfilter_compile(filter_text, &sfcode)) {
3532                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, dfilter_error_msg);
3533                 return;
3534         }
3535         /* we load the current file into cf variable */
3536         cf = &cfile;
3537         fdata = cf->current_frame;
3538         
3539         /* we are on the selected frame now */
3540         if (fdata == NULL)
3541                 return; /* if we exit here it's an error */
3542
3543         /* dissect the current frame */
3544         if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
3545             cf->pd, fdata->cap_len, &err, &err_info)) {
3546                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3547                         cf_read_error_message(err, err_info), cf->filename);
3548                 return;
3549         }
3550         edt = epan_dissect_new(TRUE, FALSE);
3551         epan_dissect_prime_dfilter(edt, sfcode);
3552         epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);
3553         frame_matched = dfilter_apply_edt(sfcode, edt);
3554         
3555         /* if it is not an rtp frame, show the rtpstream dialog */
3556         frame_matched = dfilter_apply_edt(sfcode, edt);
3557         if (frame_matched != 1) {
3558                 epan_dissect_free(edt);
3559                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3560                     "You didn't choose a RTP packet!");
3561                 return;
3562         }
3563
3564         /* ok, it is a RTP frame, so let's get the ip and port values */
3565         COPY_ADDRESS(&(ip_src_fwd), &(edt->pi.src))
3566         COPY_ADDRESS(&(ip_dst_fwd), &(edt->pi.dst))
3567         port_src_fwd = edt->pi.srcport;
3568         port_dst_fwd = edt->pi.destport;
3569
3570         /* assume the inverse ip/port combination for the reverse direction */
3571         COPY_ADDRESS(&(ip_src_rev), &(edt->pi.dst))
3572         COPY_ADDRESS(&(ip_dst_rev), &(edt->pi.src))
3573         port_src_rev = edt->pi.destport;
3574         port_dst_rev = edt->pi.srcport;
3575
3576         /* check if it is RTP Version 2 */
3577         if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3578                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3579                     "RTP Version != 2 isn't supported!");
3580                 return;
3581         }
3582         
3583         /* now we need the SSRC value of the current frame */
3584         if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3585                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3586                     "SSRC value couldn't be found!");
3587                 return;
3588         }
3589
3590         /* Scan for rtpstream */
3591         rtpstream_scan();
3592         /* search for reversed direction in the global rtp streams list */
3593         nfound = 0;
3594         strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3595         while (strinfo_list)
3596         {
3597                 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3598                 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3599                         && strinfo->src_port==port_src_fwd
3600                         && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3601                         && strinfo->dest_port==port_dst_fwd)
3602                 {
3603                         filtered_list = g_list_prepend(filtered_list, strinfo);
3604                 }
3605
3606                 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3607                         && strinfo->src_port==port_src_rev
3608                         && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3609                         && strinfo->dest_port==port_dst_rev)
3610                 {
3611                         ++nfound;
3612                         filtered_list = g_list_append(filtered_list, strinfo);
3613                         if (ssrc_rev==0)
3614                                 ssrc_rev = strinfo->ssrc;
3615                 }
3616
3617                 strinfo_list = g_list_next(strinfo_list);
3618         }
3619
3620         /* if more than one reverse streams found, we let the user choose the right one */
3621         if (nfound>1) {
3622                 rtpstream_dlg_show(filtered_list);
3623                 return;
3624         }
3625         else {
3626                 rtp_analysis(
3627                         &ip_src_fwd,
3628                         port_src_fwd,
3629                         &ip_dst_fwd,
3630                         port_dst_fwd,
3631                         ssrc_fwd,
3632                         &ip_src_rev,
3633                         port_src_rev,
3634                         &ip_dst_rev,
3635                         port_dst_rev,
3636                         ssrc_rev
3637                         );
3638         }
3639 }
3640
3641 /****************************************************************************/
3642 static void
3643 rtp_analysis_init(char *dummy _U_)
3644 {
3645         rtp_analysis_cb(NULL, NULL);
3646 }
3647
3648 /****************************************************************************/
3649 void
3650 register_tap_listener_rtp_analysis(void)
3651 {
3652         register_ethereal_tap("rtp", rtp_analysis_init);
3653
3654         register_tap_menu_item("RTP/Stream Analysis...", REGISTER_TAP_GROUP_NONE,
3655             rtp_analysis_cb, NULL, NULL, NULL);
3656 }