84a39252127238c1dd4a1d46192ef228ef39707c
[obnox/wireshark/wip.git] / gtk / io_stat.c
1 /* io_stat.c
2  * io_stat   2002 Ronnie Sahlberg
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #ifdef HAVE_SYS_TYPES_H
31 # include <sys/types.h>
32 #endif
33
34 #include <stdio.h>
35 #include <string.h>
36 #include <math.h>
37 #include <ctype.h>
38
39 #include <gtk/gtk.h>
40
41 #include <epan/epan_dissect.h>
42 #include <epan/packet_info.h>
43 #include <epan/stat_cmd_args.h>
44 #include <epan/tap.h>
45 #include <epan/strutil.h>
46
47 #include "../stat_menu.h"
48 #include "../alert_box.h"
49 #include "../simple_dialog.h"
50 #include "../color.h"
51
52 #include "gtk/gtkglobals.h"
53 #include "gtk/gui_utils.h"
54 #include "gtk/gui_stat_menu.h"
55 #include "gtk/stock_icons.h"
56 #include "gtk/dlg_utils.h"
57 #include "gtk/filter_dlg.h"
58 #include "gtk/help_dlg.h"
59 #include "gtk/pixmap_save.h"
60 #include "gtk/main.h"
61 #include "gtk/filter_autocomplete.h"
62
63
64 #define MAX_GRAPHS 5
65
66 #define MAX_YSCALE 28
67 #define LOGARITHMIC_YSCALE 0
68 #define AUTO_MAX_YSCALE 1
69 #define DEFAULT_YSCALE_INDEX 1
70 static guint32 yscale_max[MAX_YSCALE] = {LOGARITHMIC_YSCALE, AUTO_MAX_YSCALE, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000, 100000000, 200000000, 500000000, 1000000000, 2000000000};
71
72 #define MAX_PIXELS_PER_TICK 4
73 #define DEFAULT_PIXELS_PER_TICK_INDEX 2
74 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
75
76
77 #define DEFAULT_PLOT_STYLE      0
78 #define PLOT_STYLE_LINE         0
79 #define PLOT_STYLE_IMPULSE      1
80 #define PLOT_STYLE_FILLED_BAR   2
81 #define PLOT_STYLE_DOT          3
82 #define MAX_PLOT_STYLES         4
83 static const char *plot_style_name[MAX_PLOT_STYLES] = {
84         "Line",
85         "Impulse",
86         "FBar",
87         "Dot",
88 };
89
90 #define DEFAULT_COUNT_TYPE  0
91 #define COUNT_TYPE_FRAMES   0
92 #define COUNT_TYPE_BYTES    1
93 #define COUNT_TYPE_BITS     2
94 #define COUNT_TYPE_ADVANCED 3
95 #define MAX_COUNT_TYPES     4
96 static const char *count_type_names[MAX_COUNT_TYPES] = {"Packets/Tick", "Bytes/Tick", "Bits/Tick", "Advanced..."};
97
98 /* unit is in ms */
99 #define MAX_TICK_VALUES 7
100 #define DEFAULT_TICK_VALUE_INDEX 3
101 static const guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000, 60000, 600000 };
102
103 #define CALC_TYPE_SUM   0
104 #define CALC_TYPE_COUNT 1
105 #define CALC_TYPE_MAX   2
106 #define CALC_TYPE_MIN   3
107 #define CALC_TYPE_AVG   4
108 #define CALC_TYPE_LOAD  5
109 #define MAX_CALC_TYPES  6
110 #define DEFAULT_CALC_TYPE 0
111 static const char *calc_type_names[MAX_CALC_TYPES] = {"SUM(*)", "COUNT(*)", "MAX(*)", "MIN(*)", "AVG(*)", "LOAD(*)"};
112
113
114 typedef struct _io_stat_calc_type_t {
115         struct _io_stat_graph_t *gio;
116         int calc_type;
117 } io_stat_calc_type_t;
118
119 #define NUM_IO_ITEMS 100000
120 typedef struct _io_item_t {
121         guint32 frames; /* always calculated, will hold number of frames*/
122         guint32 bytes;  /* always calculated, will hold number of bytes*/
123         guint32 fields;
124         gint32 int_max;
125         gint32 int_min;
126         gint32 int_tot;
127         gfloat float_max;
128         gfloat float_min;
129         gfloat float_tot;
130         gdouble double_max;
131         gdouble double_min;
132         gdouble double_tot;
133         nstime_t time_max;
134         nstime_t time_min;
135         nstime_t time_tot;
136 } io_item_t;
137
138 typedef struct _io_stat_graph_t {
139         struct _io_stat_t *io;
140         io_item_t items[NUM_IO_ITEMS];
141         int plot_style;
142         gboolean display;
143         GtkWidget *display_button;
144         GtkWidget *filter_field;
145         GtkWidget *advanced_buttons;
146         int calc_type;
147         int hf_index;
148         GtkWidget *calc_field;
149         GdkColor color;
150         GdkGC *gc;
151         construct_args_t *args;
152         GtkWidget *filter_bt;
153 } io_stat_graph_t;
154
155
156 typedef struct _io_stat_t {
157         gboolean needs_redraw;
158         gint32 interval;      /* measurement interval in ms */
159         guint32 last_interval;
160         guint32 max_interval; /* XXX max_interval and num_items are redundant */
161         guint32 num_items;
162         guint32 left_x_border;
163         guint32 right_x_border;
164         gboolean view_as_time;
165         nstime_t start_time;
166
167         struct _io_stat_graph_t graphs[MAX_GRAPHS];
168         GtkWidget *window;
169         GtkWidget *draw_area;
170         GdkPixmap *pixmap;
171         GtkAdjustment *scrollbar_adjustment;
172         GtkWidget *scrollbar;
173         guint first_frame_num[NUM_IO_ITEMS];
174         guint last_frame_num;
175         int pixmap_width;
176         int pixmap_height;
177         int pixels_per_tick;
178         int max_y_units;
179         int count_type;
180 } io_stat_t;
181
182
183 static void init_io_stat_window(io_stat_t *io);
184 static void filter_callback(GtkWidget *widget _U_, gpointer user_data);
185
186 static void
187 io_stat_set_title(io_stat_t *io)
188 {
189         char            *title;
190
191         if(!io->window){
192                 return;
193         }
194         title = g_strdup_printf("Wireshark IO Graphs: %s", cf_get_display_name(&cfile));
195         gtk_window_set_title(GTK_WINDOW(io->window), title);
196         g_free(title);
197 }
198
199 static void
200 io_stat_reset(io_stat_t *io)
201 {
202         int i, j;
203
204         io->needs_redraw=TRUE;
205         for(i=0;i<MAX_GRAPHS;i++){
206                 for(j=0;j<NUM_IO_ITEMS;j++){
207                         io_item_t *ioi;
208                         ioi=&io->graphs[i].items[j];
209
210                         ioi->frames=0;
211                         ioi->bytes=0;
212                         ioi->fields=0;
213                         ioi->int_max=0;
214                         ioi->int_min=0;
215                         ioi->int_tot=0;
216                         ioi->float_max=0;
217                         ioi->float_min=0;
218                         ioi->float_tot=0;
219                         ioi->double_max=0;
220                         ioi->double_min=0;
221                         ioi->double_tot=0;
222                         nstime_set_zero(&ioi->time_max);
223                         nstime_set_zero(&ioi->time_min);
224                         nstime_set_zero(&ioi->time_tot);
225                 }
226         }
227         io->last_interval=0xffffffff;
228         io->max_interval=0;
229         io->num_items=0;
230         io->start_time.secs=0;
231         io->start_time.nsecs=0;
232         for(j=0;j<NUM_IO_ITEMS;j++) {
233                 io->first_frame_num[j]=0;
234         }
235         io->last_frame_num=0;
236
237         io_stat_set_title(io);
238 }
239
240 static void
241 tap_iostat_reset(void *g)
242 {
243         io_stat_graph_t *gio=g;
244
245         io_stat_reset(gio->io);
246 }
247
248 static gboolean
249 tap_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
250 {
251         io_stat_graph_t *git=g;
252         io_item_t *it;
253         nstime_t time_delta;
254         int idx;
255
256         /* we sometimes get called when git is disabled.
257            this is a bug since the tap listener should be removed first */
258         if(!git->display){
259                 return FALSE;
260         }
261
262         git->io->needs_redraw=TRUE;
263
264         /*
265          * Find which interval this is supposed to go in and store the
266          * interval index as idx
267          */
268         time_delta=pinfo->fd->rel_ts;
269         if(time_delta.nsecs<0){
270                 time_delta.secs--;
271                 time_delta.nsecs+=1000000000;
272         }
273         if(time_delta.secs<0){
274                 return FALSE;
275         }
276         idx=(int) ((time_delta.secs*1000+time_delta.nsecs/1000000)/git->io->interval);
277
278         /* some sanity checks */
279         if((idx<0)||(idx>=NUM_IO_ITEMS)){
280                 git->io->num_items = NUM_IO_ITEMS-1;
281                 return FALSE;
282         }
283
284         /* update num_items */
285         if((guint32)idx > git->io->num_items){
286                 git->io->num_items=idx;
287                 git->io->max_interval=(idx+1)*git->io->interval;
288         }
289
290         /* set start time */
291         if(git->io->start_time.secs == 0 && git->io->start_time.nsecs == 0) {
292                 nstime_diff (&git->io->start_time, &pinfo->fd->abs_ts, &pinfo->fd->rel_ts);
293         }
294
295         /* set first and last frame num in current interval */
296         if (git->io->first_frame_num[idx] == 0) {
297                 git->io->first_frame_num[idx]=pinfo->fd->num;
298         }
299         git->io->last_frame_num=pinfo->fd->num;
300
301         /*
302          * Find the appropriate io_item_t structure
303          */
304         it=&git->items[idx];
305
306
307         /*
308          * For ADVANCED mode we need to keep track of some more stuff
309          * than just frame and byte counts
310          */
311         if(git->io->count_type==COUNT_TYPE_ADVANCED){
312                 GPtrArray *gp;
313                 guint i;
314
315                 gp=proto_get_finfo_ptr_array(edt->tree, git->hf_index);
316                 if(!gp){
317                         return FALSE;
318                 }
319
320                 /* update the appropriate counters, make sure that if
321                  * fields==0 then this is the first seen value so
322                  * set any min/max values accordingly
323                  */
324                 for(i=0;i<gp->len;i++){
325                         int new_int;
326                         float new_float;
327                         double new_double;
328                         nstime_t *new_time;
329
330                         switch(proto_registrar_get_ftype(git->hf_index)){
331                         case FT_UINT8:
332                         case FT_UINT16:
333                         case FT_UINT24:
334                         case FT_UINT32:
335                                 new_int=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
336
337                                 if((new_int>it->int_max)||(it->fields==0)){
338                                         it->int_max=new_int;
339                                 }
340                                 if((new_int<it->int_min)||(it->fields==0)){
341                                         it->int_min=new_int;
342                                 }
343                                 it->int_tot+=new_int;
344                                 it->fields++;
345                                 break;
346                         case FT_INT8:
347                         case FT_INT16:
348                         case FT_INT24:
349                         case FT_INT32:
350                                 new_int=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
351                                 if((new_int>it->int_max)||(it->fields==0)){
352                                         it->int_max=new_int;
353                                 }
354                                 if((new_int<it->int_min)||(it->fields==0)){
355                                         it->int_min=new_int;
356                                 }
357                                 it->int_tot+=new_int;
358                                 it->fields++;
359                                 break;
360                         case FT_FLOAT:
361                                 new_float=(gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
362                                 if((new_float>it->float_max)||(it->fields==0)){
363                                         it->float_max=new_float;
364                                 }
365                                 if((new_float<it->float_min)||(it->fields==0)){
366                                         it->float_min=new_float;
367                                 }
368                                 it->float_tot+=new_float;
369                                 it->fields++;
370                                 break;
371                         case FT_DOUBLE:
372                                 new_double=fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
373                                 if((new_double>it->double_max)||(it->fields==0)){
374                                         it->double_max=new_double;
375                                 }
376                                 if((new_double<it->double_min)||(it->fields==0)){
377                                         it->double_min=new_double;
378                                 }
379                                 it->double_tot+=new_double;
380                                 it->fields++;
381                                 break;
382                         case FT_RELATIVE_TIME:
383                                 new_time=fvalue_get(&((field_info *)gp->pdata[0])->value);
384
385                                 switch(git->calc_type){
386                                         guint64 t, pt; /* time in us */
387                                         int j;
388                                 case CALC_TYPE_LOAD:
389                                         /* it is a LOAD calculation of a relative time field.
390                                          * add the time this call spanned to each
391                                          * interval it spanned according to its contribution
392                                          * to that interval.
393                                          */
394                                         t=new_time->secs;
395                                         t=t*1000000+new_time->nsecs/1000;
396                                         j=idx;
397                                         /* handle current interval */
398                                         pt=pinfo->fd->rel_ts.secs*1000000+pinfo->fd->rel_ts.nsecs/1000;
399                                         pt=pt%(git->io->interval*1000);
400                                         if(pt>t){
401                                                 pt=t;
402                                         }
403                                         while(t){
404                                                 git->items[j].time_tot.nsecs+=(int) (pt*1000);
405                                                 if(git->items[j].time_tot.nsecs>1000000000){
406                                                         git->items[j].time_tot.secs++;
407                                                         git->items[j].time_tot.nsecs-=1000000000;
408                                                 }
409
410                                                 if(j==0){
411                                                         break;
412                                                 }
413                                                 j--;
414                                                 t-=pt;
415                                                 if(t > (guint32) (git->io->interval*1000)){
416                                                         pt=git->io->interval*1000;
417                                                 } else {
418                                                         pt=t;
419                                                 }
420                                         }
421                                         break;
422                                 default:
423                                         if( (new_time->secs>it->time_max.secs)
424                                         ||( (new_time->secs==it->time_max.secs)
425                                           &&(new_time->nsecs>it->time_max.nsecs))
426                                         ||(it->fields==0)){
427                                                 it->time_max=*new_time;
428                                         }
429                                         if( (new_time->secs<it->time_min.secs)
430                                         ||( (new_time->secs==it->time_min.secs)
431                                           &&(new_time->nsecs<it->time_min.nsecs))
432                                         ||(it->fields==0)){
433                                                 it->time_min=*new_time;
434                                         }
435                                         nstime_add(&it->time_tot, new_time);
436                                         it->fields++;
437                                 }
438                         }
439                 }
440         }
441
442         it->frames++;
443         it->bytes+=pinfo->fd->pkt_len;
444
445         return TRUE;
446 }
447
448 static guint
449 get_frame_num(io_stat_t *io, guint32 idx, gboolean first)
450 {
451         guint i, frame_num=0;
452
453         if (idx>io->num_items) {
454                 return 0;
455         }
456
457         if (first) {
458                 frame_num=io->first_frame_num[idx];
459         }
460
461         if (frame_num==0) {
462                 /*
463                  * If first frame not found we select the last
464                  * frame in the previous interval
465                  *
466                  * If selecting the last frame we select the frame
467                  * before the first frame in the next interval
468                  */
469                 for(i=idx+1;i<=io->num_items;i++) {
470                         frame_num=io->first_frame_num[i];
471                         if (frame_num != 0) {
472                                 return frame_num-1;
473                         }
474                 }
475
476                 /*
477                  * If not found we select the last frame
478                  */
479                 frame_num=io->last_frame_num;
480         }
481
482         return frame_num;
483 }
484
485 static guint32
486 get_it_value(io_stat_t *io, int graph_id, int idx)
487 {
488         double value=0;
489         int adv_type;
490         io_item_t *it;
491
492         it=&io->graphs[graph_id].items[idx];
493
494         switch(io->count_type){
495         case COUNT_TYPE_FRAMES:
496                 return it->frames;
497         case COUNT_TYPE_BYTES:
498                 return it->bytes;
499         case COUNT_TYPE_BITS:
500                 return (it->bytes * 8);
501         }
502
503
504         adv_type=proto_registrar_get_ftype(io->graphs[graph_id].hf_index);
505         switch(adv_type){
506         case FT_NONE:
507                 switch(io->graphs[graph_id].calc_type){
508                 case CALC_TYPE_COUNT:
509                         value=it->frames;
510                         break;
511                 default:
512                         break;
513                 }
514                 break;
515         case FT_UINT8:
516         case FT_UINT16:
517         case FT_UINT24:
518         case FT_UINT32:
519         case FT_INT8:
520         case FT_INT16:
521         case FT_INT24:
522         case FT_INT32:
523                 switch(io->graphs[graph_id].calc_type){
524                 case CALC_TYPE_SUM:
525                         value=it->int_tot;
526                         break;
527                 case CALC_TYPE_COUNT:
528                         value=it->frames;
529                         break;
530                 case CALC_TYPE_MAX:
531                         value=it->int_max;
532                         break;
533                 case CALC_TYPE_MIN:
534                         value=it->int_min;
535                         break;
536                 case CALC_TYPE_AVG:
537                         if(it->fields){
538                                 value=it->int_tot/it->fields;
539                         } else {
540                                 value=0;
541                         }
542                         break;
543                 default:
544                         break;
545                 }
546                 break;
547         case FT_FLOAT:
548                 switch(io->graphs[graph_id].calc_type){
549                 case CALC_TYPE_SUM:
550                         value=it->float_tot;
551                         break;
552                 case CALC_TYPE_COUNT:
553                         value=it->frames;
554                         break;
555                 case CALC_TYPE_MAX:
556                         value=it->float_max;
557                         break;
558                 case CALC_TYPE_MIN:
559                         value=it->float_min;
560                         break;
561                 case CALC_TYPE_AVG:
562                         if(it->fields){
563                                 value=it->float_tot/it->fields;
564                         } else {
565                                 value=0;
566                         }
567                         break;
568                 default:
569                         break;
570                 }
571                 break;
572         case FT_DOUBLE:
573                 switch(io->graphs[graph_id].calc_type){
574                 case CALC_TYPE_SUM:
575                         value=it->double_tot;
576                         break;
577                 case CALC_TYPE_COUNT:
578                         value=it->frames;
579                         break;
580                 case CALC_TYPE_MAX:
581                         value=it->double_max;
582                         break;
583                 case CALC_TYPE_MIN:
584                         value=it->double_min;
585                         break;
586                 case CALC_TYPE_AVG:
587                         if(it->fields){
588                                 value=it->double_tot/it->fields;
589                         } else {
590                                 value=0;
591                         }
592                         break;
593                 default:
594                         break;
595                 }
596                 break;
597         case FT_RELATIVE_TIME:
598                 switch(io->graphs[graph_id].calc_type){
599                 case CALC_TYPE_COUNT:
600                         value=it->frames;
601                         break;
602                 case CALC_TYPE_MAX:
603                         value=(guint32) (it->time_max.secs*1000000+it->time_max.nsecs/1000);
604                         break;
605                 case CALC_TYPE_MIN:
606                         value=(guint32) (it->time_min.secs*1000000+it->time_min.nsecs/1000);
607                         break;
608                 case CALC_TYPE_SUM:
609                         value=(guint32) (it->time_tot.secs*1000000+it->time_tot.nsecs/1000);
610                         break;
611                 case CALC_TYPE_AVG:
612                         if(it->fields){
613                                 guint64 t; /* time in us */
614
615                                 t=it->time_tot.secs;
616                                 t=t*1000000+it->time_tot.nsecs/1000;
617                                 value=(guint32) (t/it->fields);
618                         } else {
619                                 value=0;
620                         }
621                         break;
622                 case CALC_TYPE_LOAD:
623                         value=(guint32) ((it->time_tot.secs*1000000+it->time_tot.nsecs/1000)/io->interval);
624                         break;
625                 default:
626                         break;
627                 }
628                 break;
629         default:
630                 break;
631         }
632         return (guint32)value; /* FIXME: loss of precision, visible on the graph for small values */
633 }
634
635 static void
636 print_time_scale_string(char *buf, int buf_len, guint32 t, guint32 t_max, gboolean log_flag)
637 {
638         if(t_max>=10000000 || (log_flag && t_max>=1000000)){
639                 g_snprintf(buf, buf_len, "%ds",t/1000000);
640         } else if(t_max>=1000000){
641                 g_snprintf(buf, buf_len, "%d.%1ds",t/1000000,(t%1000000)/100000);
642         } else if(t_max>=10000 || (log_flag && t_max>=1000)){
643                 g_snprintf(buf, buf_len, "%dms",t/1000);
644         } else if(t_max>=1000){
645                 g_snprintf(buf, buf_len, "%d.%1dms",t/1000,(t%1000)/100);
646         } else {
647                 g_snprintf(buf, buf_len, "%dus",t);
648         }
649 }
650
651 static void
652 print_interval_string(char *buf, int buf_len, guint32 interval, io_stat_t *io,
653                       gboolean ext)
654 {
655         if (io->view_as_time) {
656                 struct tm *tmp;
657                 time_t sec_val = interval/1000 + io->start_time.secs;
658                 gint32 nsec_val = interval%1000 + io->start_time.nsecs/1000000;
659
660                 if(nsec_val >= 1000) {
661                         sec_val++;
662                         nsec_val -= 1000;
663                 }
664                 tmp = localtime (&sec_val);
665                 if(io->interval>=1000){
666                         g_snprintf(buf, buf_len, "%02d:%02d:%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
667                 } else if(io->interval>=100){
668                         g_snprintf(buf, buf_len, "%02d:%02d:%02d.%1d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/100);
669                 } else if(io->interval>=10){
670                         g_snprintf(buf, buf_len, "%02d:%02d:%02d.%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/10);
671                 } else {
672                         g_snprintf(buf, buf_len, "%02d:%02d:%02d.%03d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val);
673                 }
674         } else {
675                 if (!ext) {
676                         g_snprintf(buf, buf_len, "%d.%03d", interval/1000,interval%1000);
677                 } else if(io->interval>=60000){
678                         g_snprintf(buf, buf_len, "%dm", interval/60000);
679                 } else if(io->interval>=1000){
680                         g_snprintf(buf, buf_len, "%ds", interval/1000);
681                 } else if(io->interval>=100){
682                         g_snprintf(buf, buf_len, "%d.%1ds", interval/1000,(interval/100)%10);
683                 } else if(io->interval>=10){
684                         g_snprintf(buf, buf_len, "%d.%02ds", interval/1000,(interval/10)%100);
685                 } else {
686                         g_snprintf(buf, buf_len, "%d.%03ds", interval/1000,interval%1000);
687                 }
688         }
689 }
690
691 static void
692 io_stat_draw(io_stat_t *io)
693 {
694         int i, tics, ystart, ys;
695         guint32 last_interval, first_interval, interval_delta;
696         gint32 current_interval;
697         guint32 top_y_border;
698         guint32 bottom_y_border;
699         PangoLayout  *layout;
700         int label_width, label_height;
701         guint32 draw_width, draw_height;
702         char label_string[45];
703
704         /* new variables */
705         guint32 num_time_intervals;
706         guint32 max_value;              /* max value of seen data */
707         guint32 max_y;                  /* max value of the Y scale */
708         gboolean draw_y_as_time;
709
710         if(!io->needs_redraw){
711                 return;
712         }
713         io->needs_redraw=FALSE;
714
715
716         /*
717          * Find the length of the intervals we have data for
718          * so we know how large arrays we need to malloc()
719          */
720         num_time_intervals=io->num_items+1;
721
722         /* XXX move this check to _packet() */
723         if(num_time_intervals>NUM_IO_ITEMS){
724                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IO-Stat error. There are too many entries, bailing out");
725                 return;
726         }
727
728
729         /*
730          * find the max value so we can autoscale the y axis
731          */
732         max_value=0;
733         for(i=0;i<MAX_GRAPHS;i++){
734                 int idx;
735
736                 if(!io->graphs[i].display){
737                         continue;
738                 }
739                 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
740                         guint32 val;
741
742                         val=get_it_value(io, i, idx);
743
744                         /* keep track of the max value we have encountered */
745                         if(val>max_value){
746                                 max_value=val;
747                         }
748                 }
749         }
750
751
752
753         /*
754          * Clear out old plot
755          */
756         gdk_draw_rectangle(io->pixmap,
757                            io->draw_area->style->white_gc,
758                            TRUE,
759                            0, 0,
760                            io->draw_area->allocation.width,
761                            io->draw_area->allocation.height);
762
763         /*
764          * Calculate the y scale we should use
765          */
766         if(io->max_y_units==AUTO_MAX_YSCALE){
767                 max_y=yscale_max[MAX_YSCALE-1];
768                 for(i=MAX_YSCALE-1;i>1;i--){
769                         if(max_value<yscale_max[i]){
770                                 max_y=yscale_max[i];
771                         }
772                 }
773         } else if(io->max_y_units==LOGARITHMIC_YSCALE){
774                 max_y=1000000000;
775                 for(i=1000000000;i>1;i/=10){
776                         if(max_value<(guint32)i){
777                                 max_y=i;
778                         }
779                 }
780         } else {
781                 /* the user had specified an explicit y scale to use */
782                 max_y=io->max_y_units;
783         }
784
785
786         /*
787          * If we use ADVANCED and all the graphs are plotting
788          * either MIN/MAX/AVG of an FT_RELATIVE_TIME field
789          * then we will do some some special processing for the
790          * labels for the Y axis below:
791          *   we will append the time unit " s" " ms" or " us"
792          *   and we will present the unit in decimal
793          */
794         draw_y_as_time=FALSE;
795         if(io->count_type==COUNT_TYPE_ADVANCED){
796                 draw_y_as_time=TRUE;
797                 for(i=0;i<MAX_GRAPHS;i++){
798                         int adv_type;
799
800                         if(!io->graphs[i].display){
801                                 continue;
802                         }
803                         adv_type=proto_registrar_get_ftype(io->graphs[i].hf_index);
804                         switch(adv_type){
805                         case FT_RELATIVE_TIME:
806                                 switch(io->graphs[i].calc_type){
807                                 case CALC_TYPE_SUM:
808                                 case CALC_TYPE_MAX:
809                                 case CALC_TYPE_MIN:
810                                 case CALC_TYPE_AVG:
811                                         break;
812                                 default:
813                                         draw_y_as_time=FALSE;
814                                 }
815                                 break;
816                         default:
817                                 draw_y_as_time=FALSE;
818                         }
819                 }
820         }
821
822
823
824         /*
825          * Calculate size of borders surrounding the plot
826          * The border on the right side needs to be adjusted depending
827          * on the width of the text labels. For simplicity we assume that the
828          * top y scale label will be the widest one
829          */
830         if(draw_y_as_time){
831                 if(io->max_y_units==LOGARITHMIC_YSCALE){
832                         print_time_scale_string(label_string, 15, 100000, 100000, TRUE); /* 100 ms */
833                 } else {
834                         print_time_scale_string(label_string, 15, max_y, max_y, FALSE);
835                 }
836         } else {
837                 g_snprintf(label_string, 15, "%d", max_y);
838         }
839
840         layout = gtk_widget_create_pango_layout(io->draw_area, label_string);
841         pango_layout_get_pixel_size(layout, &label_width, &label_height);
842
843         io->left_x_border=10;
844         io->right_x_border=label_width+20;
845         top_y_border=10;
846         bottom_y_border=label_height+20;
847
848
849         /*
850          * Calculate the size of the drawing area for the actual plot
851          */
852         draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
853         draw_height=io->pixmap_height-top_y_border-bottom_y_border;
854
855
856         /*
857          * Add a warning if too many entries
858          */
859         if (num_time_intervals == NUM_IO_ITEMS) {
860                 g_snprintf (label_string, 45, "Warning: Graph limited to %d entries", NUM_IO_ITEMS);
861                 pango_layout_set_text(layout, label_string, -1);
862                 gdk_draw_layout(io->pixmap,
863                                 io->draw_area->style->black_gc, 5,
864                                 io->pixmap_height-bottom_y_border-draw_height-label_height/2,
865                                 layout);
866         }
867
868         /*
869          * Draw the y axis and labels
870          * (we always draw the y scale with 11 ticks along the axis)
871          */
872         gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
873                 io->pixmap_width-io->right_x_border+1,
874                 top_y_border,
875                 io->pixmap_width-io->right_x_border+1,
876                 io->pixmap_height-bottom_y_border);
877
878         if(io->max_y_units==LOGARITHMIC_YSCALE){
879                 tics=(int)log10((double)max_y);
880                 ystart=draw_height/10;
881                 ys=-1;
882         } else {
883                 tics=10;
884                 ystart=ys=0;
885         }
886
887         for(i=ys;i<=tics;i++){
888                 int xwidth, lwidth, ypos;
889
890                 xwidth=5;
891                 if(io->max_y_units==LOGARITHMIC_YSCALE){
892                         if(i==ys) {
893                                 /* position for the 0 value */
894                                 ypos=io->pixmap_height-bottom_y_border;
895                         } else if(i==tics) {
896                                 /* position for the top value, do not draw logarithmic tics above graph */
897                                 ypos=io->pixmap_height-bottom_y_border-draw_height;
898                         } else {
899                                 int j;
900                                 /* draw the logarithmic tics */
901                                 for(j=2;j<10;j++) {
902                                         ypos=(int)(io->pixmap_height-bottom_y_border-(draw_height-ystart)*(i+log10((double)j))/tics-ystart);
903                                         /* draw the tick */
904                                         gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
905                                                       io->pixmap_width-io->right_x_border+1, ypos,
906                                                       io->pixmap_width-io->right_x_border+1+xwidth, ypos);
907                                 }
908                                 ypos=io->pixmap_height-bottom_y_border-(draw_height-ystart)*i/tics-ystart;
909                         }
910                         /* all "main" logarithmic lines are slightly longer */
911                         xwidth=10;
912                 } else {
913                         if(!(i%5)){
914                                 /* first, middle and last tick are slightly longer */
915                                 xwidth=10;
916                         }
917                         ypos=io->pixmap_height-bottom_y_border-draw_height*i/10;
918                 }
919                 /* draw the tick */
920                 gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
921                               io->pixmap_width-io->right_x_border+1, ypos,
922                               io->pixmap_width-io->right_x_border+1+xwidth, ypos);
923                 /* draw the labels */
924                 if(xwidth==10) {
925                         guint32 value;
926                         if(io->max_y_units==LOGARITHMIC_YSCALE){
927                                 value=(guint32)(max_y/pow(10,tics-i));
928                                 if(draw_y_as_time){
929                                         print_time_scale_string(label_string, 15, value, value, TRUE);
930                                 } else {
931                                         g_snprintf(label_string, 15, "%d", value);
932                                 }
933                         } else {
934                                 value=(max_y/10)*i;
935                                 if(draw_y_as_time){
936                                         print_time_scale_string(label_string, 15, value, max_y, FALSE);
937                                 } else {
938                                         g_snprintf(label_string, 15, "%d", value);
939                                 }
940                         }
941
942                         pango_layout_set_text(layout, label_string, -1);
943                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
944                         gdk_draw_layout(io->pixmap,
945                                         io->draw_area->style->black_gc,
946                                         io->pixmap_width-io->right_x_border+15+label_width-lwidth,
947                                         ypos-label_height/2,
948                                         layout);
949                 }
950         }
951
952
953         /*
954          * if we have not specified the last_interval via the gui,
955          * then just pick the current end of the capture so that is scrolls
956          * nicely when doing live captures
957          */
958         if(io->last_interval==0xffffffff){
959                 last_interval=io->max_interval;
960         } else {
961                 last_interval=io->last_interval;
962         }
963
964
965
966
967 /*XXX*/
968         /* plot the x-scale */
969         gdk_draw_line(io->pixmap, io->draw_area->style->black_gc, io->left_x_border, io->pixmap_height-bottom_y_border+1, io->pixmap_width-io->right_x_border+1, io->pixmap_height-bottom_y_border+1);
970
971         if((last_interval/io->interval)>=draw_width/io->pixels_per_tick){
972                 first_interval=(last_interval/io->interval)-draw_width/io->pixels_per_tick+1;
973                 first_interval*=io->interval;
974         } else {
975                 first_interval=0;
976         }
977
978         interval_delta=(100/io->pixels_per_tick)*io->interval;
979         for(current_interval=last_interval;current_interval>=(gint32)first_interval;current_interval=current_interval-io->interval){
980                 int x, xlen;
981
982                 /* if pixels_per_tick is 1 or 2, only draw every 10 ticks */
983                 /* if pixels_per_tick is 5, only draw every 5 ticks */
984                 if(((io->pixels_per_tick<5) && (current_interval%(10*io->interval))) ||
985                    ((io->pixels_per_tick==5) && (current_interval%(5*io->interval)))){
986                         continue;
987                 }
988
989                 if(!(current_interval%interval_delta)){
990                         xlen=10;
991                 } else if(!(current_interval%(interval_delta/2))){
992                         xlen=8;
993                 } else {
994                         xlen=5;
995                 }
996
997                 x=draw_width+io->left_x_border-((last_interval-current_interval)/io->interval)*io->pixels_per_tick;
998                 gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
999                         x-1-io->pixels_per_tick/2,
1000                         io->pixmap_height-bottom_y_border+1,
1001                         x-1-io->pixels_per_tick/2,
1002                         io->pixmap_height-bottom_y_border+xlen+1);
1003
1004                 if(xlen==10){
1005                         int lwidth, x_pos;
1006                         print_interval_string (label_string, 15, current_interval, io, TRUE);
1007                         pango_layout_set_text(layout, label_string, -1);
1008                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1009
1010                         if ((x-1-io->pixels_per_tick/2-lwidth/2) < 5) {
1011                                 x_pos=5;
1012                         } else if ((x-1-io->pixels_per_tick/2+lwidth/2) > (io->pixmap_width-5)) {
1013                                 x_pos=io->pixmap_width-lwidth-5;
1014                         } else {
1015                                 x_pos=x-1-io->pixels_per_tick/2-lwidth/2;
1016                         }
1017
1018                         gdk_draw_layout(io->pixmap,
1019                                         io->draw_area->style->black_gc,
1020                                         x_pos,
1021                                         io->pixmap_height-bottom_y_border+15,
1022                                         layout);
1023                 }
1024
1025         }
1026         g_object_unref(G_OBJECT(layout));
1027
1028
1029         /*
1030          * Loop over all graphs and draw them
1031          */
1032         for(i=MAX_GRAPHS-1;i>=0;i--){
1033                 guint64 val;
1034                 guint32 interval, x_pos, y_pos, prev_x_pos, prev_y_pos;
1035
1036                 if(!io->graphs[i].display){
1037                         continue;
1038                 }
1039
1040                 /* initialize prev x/y to the value of the first interval */
1041                 prev_x_pos=draw_width-1-io->pixels_per_tick*((last_interval-first_interval)/io->interval)+io->left_x_border;
1042                 val=get_it_value(io, i, first_interval/io->interval);
1043                 if(val>max_y){
1044                         prev_y_pos=0;
1045                 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1046                         if (val==0) {
1047                                 prev_y_pos=(guint32)(draw_height-1+top_y_border);
1048                         } else {
1049                                 prev_y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
1050                         }
1051                 } else {
1052                         prev_y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1053                 }
1054
1055                 for(interval=first_interval;interval<last_interval;interval+=io->interval){
1056                         x_pos=draw_width-1-io->pixels_per_tick*((last_interval-interval)/io->interval)+io->left_x_border;
1057
1058                         val=get_it_value(io, i, interval/io->interval);
1059                         if(val>max_y){
1060                                 y_pos=0;
1061                         } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1062                                 if (val==0) {
1063                                         y_pos=(guint32)(draw_height-1+top_y_border);
1064                                 } else {
1065                                         y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
1066                                 }
1067                         } else {
1068                                 y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1069                         }
1070
1071                         switch(io->graphs[i].plot_style){
1072                         case PLOT_STYLE_LINE:
1073                                 /* dont need to draw anything if the segment
1074                                  * is entirely above the top of the graph
1075                                  */
1076                                 if( (prev_y_pos!=0) || (y_pos!=0) ){
1077                                         gdk_draw_line(io->pixmap, io->graphs[i].gc,
1078                                                 prev_x_pos, prev_y_pos,
1079                                                 x_pos, y_pos);
1080                                 }
1081                                 break;
1082                         case PLOT_STYLE_IMPULSE:
1083                                 if(val){
1084                                         gdk_draw_line(io->pixmap, io->graphs[i].gc,
1085                                                 x_pos, draw_height-1+top_y_border,
1086                                                 x_pos, y_pos);
1087                                 }
1088                                 break;
1089                         case PLOT_STYLE_FILLED_BAR:
1090                                 if(val){
1091                                         gdk_draw_rectangle(io->pixmap,
1092                                                 io->graphs[i].gc, TRUE,
1093                                                 x_pos-io->pixels_per_tick/2,
1094                                                 y_pos,
1095                                                 io->pixels_per_tick,
1096                                                 draw_height-1+top_y_border-y_pos);
1097                                 }
1098                                 break;
1099                         case PLOT_STYLE_DOT:
1100                                 if(val){
1101                                         gdk_draw_rectangle(io->pixmap,
1102                                                 io->graphs[i].gc, TRUE,
1103                                                 x_pos-io->pixels_per_tick/2,
1104                                                 y_pos-io->pixels_per_tick/2,
1105                                                 io->pixels_per_tick,
1106                                                 io->pixels_per_tick);
1107                                 }
1108                                 break;
1109                         }
1110
1111                         prev_y_pos=y_pos;
1112                         prev_x_pos=x_pos;
1113                 }
1114         }
1115
1116
1117
1118         gdk_draw_pixmap(io->draw_area->window,
1119                         io->draw_area->style->fg_gc[GTK_WIDGET_STATE(io->draw_area)],
1120                         io->pixmap,
1121                         0, 0,
1122                         0, 0,
1123                         io->pixmap_width, io->pixmap_height);
1124
1125
1126         /* update the scrollbar */
1127         if (io->max_interval == 0) {
1128                 io->scrollbar_adjustment->upper=(gfloat) io->interval;
1129                 io->scrollbar_adjustment->step_increment=(gfloat) (io->interval/10);
1130                 io->scrollbar_adjustment->page_increment=(gfloat) io->interval;
1131         } else {
1132                 io->scrollbar_adjustment->upper=(gfloat) io->max_interval;
1133                 io->scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1134                 io->scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1135         }
1136         io->scrollbar_adjustment->page_size=io->scrollbar_adjustment->page_increment;
1137         io->scrollbar_adjustment->value=(gfloat)first_interval;
1138         gtk_adjustment_changed(io->scrollbar_adjustment);
1139         gtk_adjustment_value_changed(io->scrollbar_adjustment);
1140
1141 }
1142
1143 static void
1144 io_stat_redraw(io_stat_t *io)
1145 {
1146         io->needs_redraw=TRUE;
1147         io_stat_draw(io);
1148 }
1149
1150 static void
1151 tap_iostat_draw(void *g)
1152 {
1153         io_stat_graph_t *git=g;
1154
1155         io_stat_draw(git->io);
1156 }
1157
1158 /* ok we get called with both the filter and the field.
1159    make sure the field is part of the filter.
1160    (make sure and make sure  just append it)
1161    the field MUST be part of the filter or else we wont
1162    be able to pick up the field values after the edt tree has been
1163    pruned
1164 */
1165 static GString *
1166 enable_graph(io_stat_graph_t *gio, const char *filter, const char *field)
1167 {
1168         char real_filter[262];
1169
1170         gio->display=TRUE;
1171
1172         real_filter[0]=0;
1173         if(filter){
1174                 /* skip all whitespaces */
1175                 while(*filter){
1176                         if(*filter==' '){
1177                                 filter++;
1178                                 continue;
1179                         }
1180                         if(*filter=='\t'){
1181                                 filter++;
1182                                 continue;
1183                         }
1184                         break;
1185                 }
1186                 if(*filter){
1187                         g_snprintf(real_filter, 257, "(%s)", filter);
1188                         real_filter[257]=0;
1189                 }
1190         }
1191         if(field){
1192                 /* skip all whitespaces */
1193                 while(*field){
1194                         if(*field==' '){
1195                                 field++;
1196                                 continue;
1197                         }
1198                         if(*field=='\t'){
1199                                 field++;
1200                                 continue;
1201                         }
1202                         break;
1203                 }
1204                 if(*field){
1205                         if(real_filter[0]!=0){
1206                                 g_strlcat(real_filter, " && ", 262);
1207                         }
1208                         g_strlcat(real_filter, field, 262);
1209                 }
1210         }
1211         return register_tap_listener("frame", gio, real_filter[0]?real_filter:NULL,
1212                                      TL_REQUIRES_PROTO_TREE,
1213                                      tap_iostat_reset, tap_iostat_packet, tap_iostat_draw);
1214 }
1215
1216 static void
1217 disable_graph(io_stat_graph_t *gio)
1218 {
1219         if (gio->display) {
1220                 gio->display=FALSE;
1221                 protect_thread_critical_region();
1222                 remove_tap_listener(gio);
1223                 unprotect_thread_critical_region();
1224                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button),
1225                     FALSE);
1226         }
1227 }
1228
1229 static void
1230 iostat_init(const char *optarg _U_, void* userdata _U_)
1231 {
1232         io_stat_t *io;
1233         int i=0;
1234         static color_t col[MAX_GRAPHS] = {
1235                 {0,     0x0000, 0x0000, 0x0000},
1236                 {0,     0xffff, 0x0000, 0x0000},
1237                 {0,     0x0000, 0xffff, 0x0000},
1238                 {0,     0x0000, 0x0000, 0xffff},
1239                 {0,     0xffff, 0x5000, 0xffff}
1240         };
1241         GString *error_string;
1242
1243         io=g_malloc(sizeof(io_stat_t));
1244         io->needs_redraw=TRUE;
1245         io->interval=tick_interval_values[DEFAULT_TICK_VALUE_INDEX];
1246         io->window=NULL;
1247         io->draw_area=NULL;
1248         io->pixmap=NULL;
1249         io->scrollbar=NULL;
1250         io->scrollbar_adjustment=NULL;
1251         io->pixmap_width=500;
1252         io->pixmap_height=200;
1253         io->pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
1254         io->max_y_units=AUTO_MAX_YSCALE;
1255         io->count_type=0;
1256         io->last_interval=0xffffffff;
1257         io->max_interval=0;
1258         io->num_items=0;
1259         io->left_x_border=0;
1260         io->right_x_border=500;
1261         io->view_as_time=FALSE;
1262         io->start_time.secs=0;
1263         io->start_time.nsecs=0;
1264
1265         for(i=0;i<MAX_GRAPHS;i++){
1266                 io->graphs[i].gc=NULL;
1267                 io->graphs[i].color.pixel=col[i].pixel;
1268                 io->graphs[i].color.red=col[i].red;
1269                 io->graphs[i].color.green=col[i].green;
1270                 io->graphs[i].color.blue=col[i].blue;
1271                 io->graphs[i].display=0;
1272                 io->graphs[i].display_button=NULL;
1273                 io->graphs[i].filter_field=NULL;
1274                 io->graphs[i].advanced_buttons=NULL;
1275                 io->graphs[i].io=io;
1276
1277                 io->graphs[i].args=g_malloc(sizeof(construct_args_t));
1278                 io->graphs[i].args->title = NULL;
1279                 io->graphs[i].args->wants_apply_button=TRUE;
1280                 io->graphs[i].args->activate_on_ok=TRUE;
1281                 io->graphs[i].args->modal_and_transient=FALSE;
1282
1283                 io->graphs[i].filter_bt=NULL;
1284         }
1285         io_stat_reset(io);
1286
1287         error_string=enable_graph(&io->graphs[0], NULL, NULL);
1288         g_assert((error_string == NULL) && "Can't attach io_stat tap !");
1289 #if 0
1290         if(error_string){
1291
1292                 fprintf(stderr, "wireshark: Can't attach io_stat tap: %s\n",
1293                     error_string->str);
1294                 g_string_free(error_string, TRUE);
1295                 io->graphs[0].display=0;
1296                 io->graphs[0].display_button=NULL;
1297                 io->graphs[0].filter_field=NULL;
1298                 io->graphs[0].advanced_buttons=NULL;
1299                 exit(10);
1300         }
1301 #endif
1302         /* build the GUI */
1303         init_io_stat_window(io);
1304
1305         cf_retap_packets(&cfile);
1306         gdk_window_raise(io->window->window);
1307         io_stat_redraw(io);
1308 }
1309
1310 static void
1311 draw_area_destroy_cb(GtkWidget *widget _U_, gpointer user_data)
1312 {
1313         io_stat_t *io = user_data;
1314         int i;
1315
1316         for(i=0;i<MAX_GRAPHS;i++){
1317                 if(io->graphs[i].display){
1318                         protect_thread_critical_region();
1319                         remove_tap_listener(&io->graphs[i]);
1320                         unprotect_thread_critical_region();
1321
1322                         g_free( (gpointer) (io->graphs[i].args->title) );
1323                         io->graphs[i].args->title=NULL;
1324
1325                         g_free(io->graphs[i].args);
1326                         io->graphs[i].args=NULL;
1327                 }
1328         }
1329         g_free(io);
1330
1331         return;
1332 }
1333
1334 static gboolean
1335 pixmap_clicked_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data)
1336 {
1337         io_stat_t *io = user_data;
1338         guint32 draw_width, interval, last_interval;
1339         guint frame_num;
1340
1341         draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
1342
1343         if ((event->x <= (draw_width+io->left_x_border+1-(draw_width/io->pixels_per_tick)*io->pixels_per_tick)) ||
1344             (event->x >= (draw_width+io->left_x_border-io->pixels_per_tick/2))) {
1345               /* Outside draw area */
1346               return FALSE;
1347         }
1348
1349         if ((event->button==1 || event->button==3) && io->pixmap!=NULL) {
1350                 /*
1351                  * Button 1 selects the first package in the interval.
1352                  * Button 3 selects the last package in the interval.
1353                  */
1354                 if (io->last_interval==0xffffffff) {
1355                         last_interval=io->max_interval;
1356                 } else {
1357                         last_interval=io->last_interval;
1358                 }
1359
1360                 interval=(guint32)((last_interval/io->interval)-(draw_width+io->left_x_border-event->x-io->pixels_per_tick/2-1)/io->pixels_per_tick);
1361                 frame_num=get_frame_num (io, interval, event->button==1?TRUE:FALSE);
1362                 if (frame_num != 0) {
1363                         cf_goto_frame(&cfile, frame_num);
1364                 }
1365         }
1366
1367         return TRUE;
1368 }
1369
1370 /* create a new backing pixmap of the appropriate size */
1371 static gboolean
1372 draw_area_configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer user_data)
1373 {
1374         io_stat_t *io = user_data;
1375         int i;
1376 #if GTK_CHECK_VERSION(2,6,0)
1377         GtkWidget *save_bt;
1378 #endif
1379
1380         if(io->pixmap){
1381                 gdk_pixmap_unref(io->pixmap);
1382                 io->pixmap=NULL;
1383         }
1384
1385         io->pixmap=gdk_pixmap_new(widget->window,
1386                         widget->allocation.width,
1387                         widget->allocation.height,
1388                         -1);
1389         io->pixmap_width=widget->allocation.width;
1390         io->pixmap_height=widget->allocation.height;
1391
1392 #if GTK_CHECK_VERSION(2,6,0)
1393         save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt");
1394         g_object_set_data(G_OBJECT(save_bt), "pixmap", io->pixmap);
1395         gtk_widget_set_sensitive(save_bt, TRUE);
1396 #endif
1397
1398         gdk_draw_rectangle(io->pixmap,
1399                         widget->style->white_gc,
1400                         TRUE,
1401                         0, 0,
1402                         widget->allocation.width,
1403                         widget->allocation.height);
1404
1405         /* set up the colors and the GC structs for this pixmap */
1406         for(i=0;i<MAX_GRAPHS;i++){
1407                 io->graphs[i].gc=gdk_gc_new(io->pixmap);
1408                 gdk_gc_set_rgb_fg_color(io->graphs[i].gc, &io->graphs[i].color);
1409         }
1410
1411         io_stat_redraw(io);
1412         return TRUE;
1413 }
1414
1415 static void
1416 scrollbar_changed(GtkWidget *widget _U_, gpointer user_data)
1417 {
1418         io_stat_t *io = user_data;
1419         guint32 mi;
1420
1421         mi=(guint32) (io->scrollbar_adjustment->value+io->scrollbar_adjustment->page_size);
1422         if(io->last_interval==mi){
1423                 return;
1424         }
1425         if( (io->last_interval==0xffffffff)
1426         &&  (mi==io->max_interval) ){
1427                 return;
1428         }
1429
1430         io->last_interval=(mi/io->interval)*io->interval;
1431         io_stat_redraw(io);
1432
1433         return;
1434 }
1435
1436 /* redraw the screen from the backing pixmap */
1437 static gboolean
1438 draw_area_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
1439 {
1440         io_stat_t *io = user_data;
1441
1442         gdk_draw_pixmap(widget->window,
1443                         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1444                         io->pixmap,
1445                         event->area.x, event->area.y,
1446                         event->area.x, event->area.y,
1447                         event->area.width, event->area.height);
1448
1449         return FALSE;
1450 }
1451
1452 static void
1453 create_draw_area(io_stat_t *io, GtkWidget *box)
1454 {
1455         io->draw_area=gtk_drawing_area_new();
1456         g_signal_connect(io->draw_area, "destroy", G_CALLBACK(draw_area_destroy_cb), io);
1457
1458         gtk_widget_set_size_request(io->draw_area, io->pixmap_width, io->pixmap_height);
1459
1460         /* signals needed to handle backing pixmap */
1461         g_signal_connect(io->draw_area, "expose-event", G_CALLBACK(draw_area_expose_event), io);
1462         g_signal_connect(io->draw_area, "configure-event", G_CALLBACK(draw_area_configure_event), io);
1463         gtk_widget_add_events (io->draw_area, GDK_BUTTON_PRESS_MASK);
1464         g_signal_connect(io->draw_area, "button-press-event", G_CALLBACK(pixmap_clicked_event), io);
1465
1466         gtk_widget_show(io->draw_area);
1467         gtk_box_pack_start(GTK_BOX(box), io->draw_area, TRUE, TRUE, 0);
1468
1469         /* create the associated scrollbar */
1470         io->scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1471         io->scrollbar=gtk_hscrollbar_new(io->scrollbar_adjustment);
1472         gtk_widget_show(io->scrollbar);
1473         gtk_box_pack_start(GTK_BOX(box), io->scrollbar, FALSE, FALSE, 0);
1474         g_signal_connect(io->scrollbar_adjustment, "value-changed", G_CALLBACK(scrollbar_changed), io);
1475 }
1476
1477 static void
1478 tick_interval_select(GtkWidget *item, gpointer user_data)
1479 {
1480         io_stat_t *io = user_data;
1481         int i;
1482
1483         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1484
1485         io->interval=tick_interval_values[i];
1486         cf_retap_packets(&cfile);
1487         gdk_window_raise(io->window->window);
1488         io_stat_redraw(io);
1489 }
1490
1491 static void
1492 pixels_per_tick_select(GtkWidget *item, gpointer user_data)
1493 {
1494         io_stat_t *io = user_data;
1495         int i;
1496
1497         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1498         io->pixels_per_tick=pixels_per_tick[i];
1499         io_stat_redraw(io);
1500 }
1501
1502 static void
1503 plot_style_select(GtkWidget *item, gpointer user_data)
1504 {
1505         io_stat_graph_t *ppt = user_data;
1506         int val;
1507
1508         val=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1509
1510         ppt->plot_style=val;
1511
1512         io_stat_redraw(ppt->io);
1513 }
1514
1515 static GtkWidget *
1516 create_pixels_per_tick_menu_items(io_stat_t *io)
1517 {
1518         char str[5];
1519         GtkWidget *combo_box;
1520         int i;
1521
1522         combo_box = gtk_combo_box_new_text ();
1523
1524         for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1525                 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
1526                 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1527         }
1528         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PIXELS_PER_TICK_INDEX);
1529         g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), io);
1530
1531         return combo_box;
1532 }
1533
1534 static void
1535 yscale_select(GtkWidget *item, gpointer user_data)
1536 {
1537         io_stat_t *io = user_data;
1538         int i;
1539
1540         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1541
1542         io->max_y_units = yscale_max[i];
1543         io_stat_redraw(io);
1544 }
1545
1546 static GtkWidget *
1547 create_tick_interval_menu_items(io_stat_t *io)
1548 {
1549         GtkWidget *combo_box;
1550         char str[15];
1551         int i;
1552
1553         combo_box = gtk_combo_box_new_text ();
1554
1555         for(i=0;i<MAX_TICK_VALUES;i++){
1556                 if(tick_interval_values[i]>=60000){
1557                         g_snprintf(str, sizeof(str), "%u min", tick_interval_values[i]/60000);
1558                 } else if(tick_interval_values[i]>=1000){
1559                         g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1560                 } else if(tick_interval_values[i]>=100){
1561                         g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1562                 } else if(tick_interval_values[i]>=10){
1563                         g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1564                 } else {
1565                         g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1566                 }
1567                 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1568         }
1569         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_TICK_VALUE_INDEX);
1570         g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), io);
1571
1572         return combo_box;
1573 }
1574
1575 static GtkWidget *
1576 create_yscale_max_menu_items(io_stat_t *io)
1577 {
1578         char str[15];
1579         GtkWidget *combo_box;
1580         int i;
1581
1582         combo_box = gtk_combo_box_new_text ();
1583
1584         for(i=0;i<MAX_YSCALE;i++){
1585                 if(yscale_max[i]==LOGARITHMIC_YSCALE){
1586                         g_strlcpy(str, "Logarithmic", 15);
1587                 } else if(yscale_max[i]==AUTO_MAX_YSCALE){
1588                         g_strlcpy(str, "Auto", 15);
1589                 } else {
1590                         g_snprintf(str, 15, "%u", yscale_max[i]);
1591                 }
1592                 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1593         }
1594         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_YSCALE_INDEX);
1595         g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), io);
1596         return combo_box;
1597 }
1598
1599 static void
1600 count_type_select(GtkWidget *item, gpointer user_data)
1601 {
1602         io_stat_t *io = user_data;
1603         static gboolean advanced_visible=FALSE;
1604         int i;
1605
1606         io->count_type = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1607
1608         if(io->count_type==COUNT_TYPE_ADVANCED){
1609                 for(i=0;i<MAX_GRAPHS;i++){
1610                         disable_graph(&io->graphs[i]);
1611                         gtk_widget_show(io->graphs[i].advanced_buttons);
1612                         /* redraw the entire window so the unhidden widgets show up, hopefully */
1613                         gtk_widget_queue_draw_area(io->window,
1614                                                    0,
1615                                                    0,
1616                                                    io->window->allocation.width,
1617                                                    io->window->allocation.height);
1618                 }
1619                 advanced_visible=TRUE;
1620                 io_stat_redraw(io);
1621         } else if (advanced_visible) {
1622                 for(i=0;i<MAX_GRAPHS;i++){
1623                         gtk_widget_hide(io->graphs[i].advanced_buttons);
1624                         filter_callback(item, &io->graphs[i]);
1625                 }
1626                 advanced_visible=FALSE;
1627         } else {
1628                 io_stat_redraw(io);
1629         }
1630 }
1631
1632 static GtkWidget *
1633 create_frames_or_bytes_menu_items(io_stat_t *io)
1634 {
1635         GtkWidget *combo_box;
1636         int i;
1637
1638         combo_box = gtk_combo_box_new_text ();
1639
1640         for(i=0;i<MAX_COUNT_TYPES;i++){
1641                 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), count_type_names[i]);
1642         }
1643         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_COUNT_TYPE);
1644         g_signal_connect(combo_box, "changed", G_CALLBACK(count_type_select), io);
1645         return combo_box;
1646 }
1647
1648 static void
1649 create_ctrl_menu(io_stat_t *io, GtkWidget *box, const char *name, GtkWidget * (*func)(io_stat_t *io))
1650 {
1651         GtkWidget *hbox;
1652         GtkWidget *label;
1653         GtkWidget *combo_box;
1654
1655         hbox=gtk_hbox_new(FALSE, 0);
1656         gtk_container_add(GTK_CONTAINER(box), hbox);
1657         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1658         gtk_widget_show(hbox);
1659
1660         label=gtk_label_new(name);
1661         gtk_widget_show(label);
1662         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1663
1664         combo_box = (*func)(io);
1665         gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1666         gtk_widget_show(combo_box);
1667 }
1668
1669 static void
1670 view_as_time_toggle_dest(GtkWidget *widget _U_, gpointer user_data)
1671 {
1672         io_stat_t *io = user_data;
1673
1674         io->view_as_time = io->view_as_time ? FALSE : TRUE;
1675
1676         io_stat_redraw(io);
1677 }
1678
1679 static void
1680 create_ctrl_area(io_stat_t *io, GtkWidget *box)
1681 {
1682         GtkWidget *frame_vbox;
1683         GtkWidget *frame;
1684         GtkWidget *vbox;
1685         GtkWidget *view_cb;
1686
1687         frame_vbox=gtk_vbox_new(FALSE, 0);
1688         gtk_box_pack_start(GTK_BOX(box), frame_vbox, FALSE, FALSE, 0);
1689         gtk_widget_show(frame_vbox);
1690
1691         frame = gtk_frame_new("X Axis");
1692         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1693         gtk_widget_show(frame);
1694
1695         vbox=gtk_vbox_new(FALSE, 0);
1696         gtk_container_add(GTK_CONTAINER(frame), vbox);
1697         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1698         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1699         gtk_widget_show(vbox);
1700
1701         create_ctrl_menu(io, vbox, "Tick interval:", create_tick_interval_menu_items);
1702         create_ctrl_menu(io, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1703
1704         view_cb = gtk_check_button_new_with_mnemonic("_View as time of day");
1705         gtk_container_add(GTK_CONTAINER(vbox), view_cb);
1706         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(view_cb), io->view_as_time);
1707         g_signal_connect(view_cb, "toggled", G_CALLBACK(view_as_time_toggle_dest), io);
1708         gtk_widget_show(view_cb);
1709
1710         frame = gtk_frame_new("Y Axis");
1711         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1712         gtk_widget_show(frame);
1713
1714         vbox=gtk_vbox_new(FALSE, 0);
1715         gtk_container_add(GTK_CONTAINER(frame), vbox);
1716         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1717         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1718         gtk_widget_show(vbox);
1719
1720         create_ctrl_menu(io, vbox, "Unit:", create_frames_or_bytes_menu_items);
1721         create_ctrl_menu(io, vbox, "Scale:", create_yscale_max_menu_items);
1722
1723         return;
1724 }
1725
1726 static void
1727 filter_callback(GtkWidget *widget _U_, gpointer user_data)
1728 {
1729         io_stat_graph_t *gio = user_data;
1730         const char *filter;
1731         const char *field=NULL;
1732         header_field_info *hfi;
1733         dfilter_t *dfilter;
1734
1735         /* this graph is not active, just update display and redraw */
1736         if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))){
1737                 disable_graph(gio);
1738                 io_stat_redraw(gio->io);
1739                 return;
1740         }
1741
1742         /* first check if the field string is valid */
1743         if(gio->io->count_type==COUNT_TYPE_ADVANCED){
1744                 field=gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
1745
1746                 /* warn and bail out if there was no field specified */
1747                 if(field==NULL || field[0]==0){
1748                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You didn't specify a field name.");
1749                         disable_graph(gio);
1750                         io_stat_redraw(gio->io);
1751                         return;
1752                 }
1753                 /* warn and bail out if the field could not be found */
1754                 hfi=proto_registrar_get_byname(field);
1755                 if(hfi==NULL){
1756                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "There is no field named '%s'.", field);
1757                         disable_graph(gio);
1758                         io_stat_redraw(gio->io);
1759                         return;
1760                 }
1761                 gio->hf_index=hfi->id;
1762                 /* check that the type is compatible */
1763                 switch(hfi->type){
1764                 case FT_UINT8:
1765                 case FT_UINT16:
1766                 case FT_UINT24:
1767                 case FT_UINT32:
1768                 case FT_INT8:
1769                 case FT_INT16:
1770                 case FT_INT24:
1771                 case FT_INT32:
1772                 case FT_FLOAT:
1773                 case FT_DOUBLE:
1774                         /* these values support all calculations except LOAD */
1775                         switch(gio->calc_type){
1776                         case CALC_TYPE_LOAD:
1777                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1778                                     "LOAD(*) is only supported for relative-time fields.");
1779                                 disable_graph(gio);
1780                                 io_stat_redraw(gio->io);
1781                                 return;
1782                         }
1783                         /* these types support all calculations */
1784                         break;
1785                 case FT_RELATIVE_TIME:
1786                         /* this type only supports COUNT, MAX, MIN, AVG */
1787                         switch(gio->calc_type){
1788                         case CALC_TYPE_SUM:
1789                         case CALC_TYPE_COUNT:
1790                         case CALC_TYPE_MAX:
1791                         case CALC_TYPE_MIN:
1792                         case CALC_TYPE_AVG:
1793                         case CALC_TYPE_LOAD:
1794                                 break;
1795                         default:
1796                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1797                                     "%s is a relative-time field, so %s calculations are not supported on it.",
1798                                     field,
1799                                     calc_type_names[gio->calc_type]);
1800                                 disable_graph(gio);
1801                                 io_stat_redraw(gio->io);
1802                                 return;
1803                         }
1804                         break;
1805                 case FT_UINT64:
1806                 case FT_INT64:
1807                         /*
1808                          * XXX - support this if gint64/guint64 are
1809                          * available?
1810                          */
1811                         if(gio->calc_type!=CALC_TYPE_COUNT){
1812                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1813                                     "%s is a 64-bit integer, so %s calculations are not supported on it.",
1814                                     field,
1815                                     calc_type_names[gio->calc_type]);
1816                                 disable_graph(gio);
1817                                 io_stat_redraw(gio->io);
1818                                 return;
1819                         }
1820                         break;
1821                 default:
1822                         if(gio->calc_type!=CALC_TYPE_COUNT){
1823                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1824                                     "%s doesn't have integral or float values, so %s calculations are not supported on it.",
1825                                     field,
1826                                     calc_type_names[gio->calc_type]);
1827                                 disable_graph(gio);
1828                                 io_stat_redraw(gio->io);
1829                                 return;
1830                         }
1831                         break;
1832                 }
1833         }
1834
1835         /* first check if the filter string is valid. */
1836         filter=gtk_entry_get_text(GTK_ENTRY(gio->filter_field));
1837         if(!dfilter_compile(filter, &dfilter)) {
1838                 bad_dfilter_alert_box(filter);
1839                 disable_graph(gio);
1840                 io_stat_redraw(gio->io);
1841                 return;
1842         }
1843         if (dfilter != NULL)
1844                 dfilter_free(dfilter);
1845
1846         /* ok, we have a valid filter and the graph is active.
1847            first just try to delete any previous settings and then apply
1848            the new ones.
1849         */
1850         protect_thread_critical_region();
1851         remove_tap_listener(gio);
1852         unprotect_thread_critical_region();
1853
1854         io_stat_reset(gio->io);
1855         enable_graph(gio, filter, field);
1856         cf_retap_packets(&cfile);
1857         gdk_window_raise(gio->io->window->window);
1858         io_stat_redraw(gio->io);
1859
1860         return;
1861 }
1862
1863 static void
1864 calc_type_select(GtkWidget *item, gpointer user_data)
1865 {
1866         io_stat_graph_t *gio = user_data;
1867
1868         gio->calc_type=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1869
1870         /* disable the graph */
1871         disable_graph(gio);
1872         io_stat_redraw(gio->io);
1873 }
1874
1875 static GtkWidget *
1876 create_calc_types_menu_items(io_stat_graph_t *gio)
1877 {
1878         GtkWidget *combo_box;
1879         int i;
1880
1881         combo_box = gtk_combo_box_new_text ();
1882
1883         for(i=0;i<MAX_CALC_TYPES;i++){
1884                 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), calc_type_names[i]);
1885         }
1886         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_CALC_TYPE);
1887         g_signal_connect(combo_box, "changed", G_CALLBACK(calc_type_select), gio);
1888         return combo_box;
1889 }
1890
1891 static void
1892 create_advanced_menu(io_stat_graph_t *gio, GtkWidget *box, const char *name,  GtkWidget *(*func)(io_stat_graph_t *io))
1893 {
1894         GtkWidget *hbox;
1895         GtkWidget *label;
1896         GtkWidget *combo_box;
1897
1898         hbox=gtk_hbox_new(FALSE, 0);
1899         gtk_container_add(GTK_CONTAINER(box), hbox);
1900         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1901         gtk_widget_show(hbox);
1902
1903         label=gtk_label_new(name);
1904         gtk_widget_show(label);
1905         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1906
1907         combo_box = (*func)(gio);
1908         gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1909         gtk_widget_show(combo_box);
1910 }
1911
1912 static void
1913 create_advanced_field(io_stat_graph_t *gio, GtkWidget *box)
1914 {
1915
1916         gio->calc_field=gtk_entry_new();
1917         gtk_entry_set_max_length(GTK_ENTRY(gio->calc_field),50);
1918         gtk_box_pack_start(GTK_BOX(box), gio->calc_field, TRUE, TRUE, 0);
1919         gtk_widget_show(gio->calc_field);
1920         g_signal_connect(gio->calc_field, "activate", G_CALLBACK(filter_callback), gio);
1921         g_object_set_data (G_OBJECT(gio->calc_field), E_FILT_FIELD_NAME_ONLY_KEY, "");
1922         g_signal_connect(gio->calc_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
1923         g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
1924         g_signal_connect(gio->calc_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
1925         g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
1926         colorize_filter_te_as_empty(gio->calc_field);
1927 }
1928
1929 static void
1930 create_advanced_box(io_stat_graph_t *gio, GtkWidget *box)
1931 {
1932         GtkWidget *hbox;
1933
1934         hbox=gtk_hbox_new(FALSE, 0);
1935         gio->advanced_buttons=hbox;
1936         gtk_container_add(GTK_CONTAINER(box), hbox);
1937         gtk_box_set_child_packing(GTK_BOX(box), hbox, TRUE, TRUE, 0, GTK_PACK_START);
1938         gtk_widget_hide(hbox);
1939
1940         gio->calc_type=CALC_TYPE_SUM;
1941         create_advanced_menu(gio, hbox, "Calc:", create_calc_types_menu_items);
1942         create_advanced_field(gio, hbox);
1943 }
1944
1945 static void
1946 filter_button_clicked(GtkWidget *w, gpointer user_data)
1947 {
1948         io_stat_graph_t *gio = user_data;
1949
1950         display_filter_construct_cb(w, gio->args);
1951         return;
1952 }
1953
1954 static void
1955 create_filter_box(io_stat_graph_t *gio, GtkWidget *box, int num)
1956 {
1957         GtkWidget *combo_box;
1958         GtkWidget *hbox;
1959         GtkWidget *label;
1960         char str[256];
1961         int i;
1962
1963         hbox=gtk_hbox_new(FALSE, 3);
1964         gtk_container_add(GTK_CONTAINER(box), hbox);
1965         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1966         gtk_widget_show(hbox);
1967
1968         g_snprintf(str, 256, "Graph %d", num);
1969         gio->display_button=gtk_toggle_button_new_with_label(str);
1970         gtk_box_pack_start(GTK_BOX(hbox), gio->display_button, FALSE, FALSE, 0);
1971         gtk_widget_show(gio->display_button);
1972         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
1973         g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);
1974
1975         label=gtk_label_new("Color");
1976         gtk_widget_show(label);
1977         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1978
1979         gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gio->color);
1980         gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &gio->color);
1981         gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &gio->color);
1982         gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &gio->color);
1983         gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &gio->color);
1984 /*      g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);*/
1985
1986
1987         /* filter prefs dialog */
1988         gio->filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
1989
1990         g_snprintf(str, 256, "Wireshark: Display Filter  IO-Stat (Filter:%d)", num);
1991         g_free( (gpointer) (gio->args->title) );
1992         gio->args->title=g_strdup(str);
1993
1994         g_signal_connect(gio->filter_bt, "clicked", G_CALLBACK(filter_button_clicked), gio);
1995         g_signal_connect(gio->filter_bt, "destroy", G_CALLBACK(filter_button_destroy_cb), NULL);
1996
1997         gtk_box_pack_start(GTK_BOX(hbox), gio->filter_bt, FALSE, TRUE, 0);
1998         gtk_widget_show(gio->filter_bt);
1999
2000         gio->filter_field=gtk_entry_new();
2001         gtk_entry_set_max_length(GTK_ENTRY(gio->filter_field),256);
2002         /* filter prefs dialog */
2003         g_object_set_data(G_OBJECT(gio->filter_bt), E_FILT_TE_PTR_KEY, gio->filter_field);
2004         /* filter prefs dialog */
2005
2006         gtk_box_pack_start(GTK_BOX(hbox), gio->filter_field, TRUE, TRUE, 0);
2007         gtk_widget_show(gio->filter_field);
2008         g_signal_connect(gio->filter_field, "activate", G_CALLBACK(filter_callback), gio);
2009         g_signal_connect(gio->filter_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
2010         g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
2011         g_signal_connect(gio->filter_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
2012         g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
2013         colorize_filter_te_as_empty(gio->filter_field);
2014
2015         create_advanced_box(gio, hbox);
2016
2017         /*
2018          * create PlotStyle menu
2019          */
2020         g_snprintf(str, 256, " Style:");
2021         label=gtk_label_new(str);
2022         gtk_widget_show(label);
2023         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2024
2025         combo_box = gtk_combo_box_new_text ();
2026         for(i=0;i<MAX_PLOT_STYLES;i++){
2027                 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), plot_style_name[i]);
2028         }
2029         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PLOT_STYLE);
2030         g_signal_connect(combo_box, "changed", G_CALLBACK(plot_style_select), &gio->io->graphs[num-1]);
2031
2032         gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
2033         gtk_widget_show(combo_box);
2034
2035         return;
2036 }
2037
2038 static void
2039 create_filter_area(io_stat_t *io, GtkWidget *box)
2040 {
2041         GtkWidget *frame;
2042         GtkWidget *vbox;
2043         int i;
2044
2045         frame=gtk_frame_new("Graphs");
2046         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
2047         gtk_widget_show(frame);
2048
2049         vbox=gtk_vbox_new(FALSE, 1);
2050         gtk_container_add(GTK_CONTAINER(frame), vbox);
2051         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
2052         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
2053         gtk_widget_show(vbox);
2054
2055         for(i=0;i<MAX_GRAPHS;i++){
2056                 create_filter_box(&io->graphs[i], vbox, i+1);
2057         }
2058
2059         return;
2060 }
2061
2062 static void
2063 copy_as_csv_cb(GtkWindow *copy_bt _U_, gpointer user_data)
2064 {
2065         guint32         i, interval, val;
2066         char            string[15];
2067         GtkClipboard    *cb;
2068         GString         *CSV_str=g_string_new("");
2069         io_stat_t       *io = user_data;
2070
2071         g_string_append(CSV_str, "\"Interval start\"");
2072         for(i=0;i<MAX_GRAPHS;i++) {
2073                 if (io->graphs[i].display) {
2074                         g_string_append_printf(CSV_str, ",\"Graph %d\"", i+1);
2075                 }
2076         }
2077         g_string_append(CSV_str,"\n");
2078
2079         for(interval=0; interval<io->max_interval; interval+=io->interval) {
2080                 print_interval_string (string, 15, interval, io, FALSE);
2081                 g_string_append_printf(CSV_str, "\"%s\"", string);
2082                 for(i=0;i<MAX_GRAPHS;i++) {
2083                         if (io->graphs[i].display) {
2084                                 val=get_it_value(io, i, interval/io->interval);
2085                                 g_string_append_printf(CSV_str, ",\"%d\"", val);
2086                         }
2087                 }
2088                 g_string_append(CSV_str,"\n");
2089         }
2090
2091         /* Now that we have the CSV data, copy it into the default clipboard */
2092         cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);    /* Get the default clipboard */
2093         gtk_clipboard_set_text(cb, CSV_str->str, -1);       /* Copy the CSV data into the clipboard */
2094         g_string_free(CSV_str, TRUE);                       /* Free the memory */
2095 }
2096
2097 static void
2098 init_io_stat_window(io_stat_t *io)
2099 {
2100         GtkWidget *vbox;
2101         GtkWidget *hbox;
2102         GtkWidget *bbox;
2103         GtkWidget *close_bt, *help_bt;
2104         GtkTooltips *tooltips = gtk_tooltips_new();
2105         GtkWidget *copy_bt;
2106 #if GTK_CHECK_VERSION(2,6,0)
2107         GtkWidget *save_bt;
2108 #endif
2109
2110         /* create the main window, transient_for top_level */
2111         io->window = dlg_window_new("I/O Graphs");
2112         gtk_window_set_destroy_with_parent (GTK_WINDOW(io->window), TRUE);
2113
2114         vbox=gtk_vbox_new(FALSE, 0);
2115         gtk_container_add(GTK_CONTAINER(io->window), vbox);
2116         gtk_widget_show(vbox);
2117
2118         create_draw_area(io, vbox);
2119
2120         hbox=gtk_hbox_new(FALSE, 3);
2121         gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2122         gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
2123         gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2124         gtk_widget_show(hbox);
2125
2126         create_filter_area(io, hbox);
2127         create_ctrl_area(io, hbox);
2128
2129         io_stat_set_title(io);
2130
2131 #if GTK_CHECK_VERSION(2,6,0)
2132         bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE,
2133                                   GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
2134 #else
2135         bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_COPY,
2136                                   GTK_STOCK_HELP, NULL);
2137 #endif
2138         gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
2139         gtk_widget_show(bbox);
2140
2141         close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
2142         window_set_cancel_button(io->window, close_bt, window_cancel_button_cb);
2143         gtk_tooltips_set_tip(tooltips, close_bt, "Close this dialog", NULL);
2144
2145 #if GTK_CHECK_VERSION(2,6,0)
2146         save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
2147         gtk_widget_set_sensitive(save_bt, FALSE);
2148         gtk_tooltips_set_tip(tooltips, save_bt, "Save the displayed graph to a file", NULL);
2149         g_signal_connect(save_bt, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
2150         g_object_set_data(G_OBJECT(io->window), "save_bt", save_bt);
2151 #endif
2152
2153         copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
2154         gtk_tooltips_set_tip(tooltips, copy_bt,
2155                              "Copy values from selected graphs to the clipboard in CSV (Comma Separated Values) format", NULL);
2156         g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), io);
2157
2158         help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
2159         g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_IO_GRAPH_DIALOG);
2160         gtk_tooltips_set_tip (tooltips, help_bt, "Show topic specific help", NULL);
2161
2162         g_signal_connect(io->window, "delete-event", G_CALLBACK(window_delete_event_cb), NULL);
2163
2164         gtk_widget_show(io->window);
2165         window_present(io->window);
2166 }
2167
2168 #ifdef MAIN_MENU_USE_UIMANAGER
2169 void
2170 gui_iostat_cb(GtkAction *action _U_, gpointer user_data _U_)
2171 {
2172         iostat_init(NULL,NULL);
2173 }
2174 #else
2175 static void
2176 gui_iostat_cb(GtkWidget *w _U_, gpointer d _U_)
2177 {
2178         iostat_init(NULL,NULL);
2179 }
2180 #endif
2181
2182 void
2183 register_tap_listener_gtk_iostat(void)
2184 {
2185         register_stat_cmd_arg("io,stat", iostat_init,NULL);
2186
2187 #ifdef MAIN_MENU_USE_UIMANAGER
2188 #else
2189         register_stat_menu_item_stock("_IO Graphs",
2190                                       REGISTER_STAT_GROUP_GENERIC, WIRESHARK_STOCK_GRAPHS,
2191                                       gui_iostat_cb, NULL, NULL, NULL);
2192 #endif
2193 }