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