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