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