fix doxygen generation
[obnox/wireshark/wip.git] / gtk / io_stat.c
index 649ee043212eda269894dc71f189f98c866d2503..2518492a285a17a2bcf74bf400bd2c6791c2ac7b 100644 (file)
@@ -1,7 +1,7 @@
 /* io_stat.c
  * io_stat   2002 Ronnie Sahlberg
  *
- * $Id: io_stat.c,v 1.21 2003/04/23 08:20:05 guy Exp $
+ * $Id$
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
@@ -27,8 +27,6 @@
 # include "config.h"
 #endif
 
-#include <stdio.h>
-
 #ifdef HAVE_SYS_TYPES_H
 # include <sys/types.h>
 #endif
 #include <ctype.h>
 
 #include <gtk/gtk.h>
+
+#include <epan/epan_dissect.h>
+#include <epan/packet_info.h>
+
 #include "gtkglobals.h"
-#include "menu.h"
-#include "epan/epan_dissect.h"
-#include "epan/packet_info.h"
-#include "../tap.h"
+#include "ui_util.h"
+#include "tap_menu.h"
+#include <epan/tap.h>
 #include "../register.h"
+#include "alert_box.h"
 #include "simple_dialog.h"
 #include "../globals.h"
 #include "../color.h"
 #include "compat_macros.h"
-
-/* filter prefs dialog */
-#include "filter_prefs.h"
-/* filter prefs dialog */
+#include "dlg_utils.h"
+#include "filter_dlg.h"
 
 void protect_thread_critical_region(void);
 void unprotect_thread_critical_region(void);
@@ -66,50 +66,64 @@ static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 10, 20, 50, 100, 200,
 #define DEFAULT_PIXELS_PER_TICK 2
 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
 
-#define MAX_COUNT_TYPES 3
+
+#define DEFAULT_PLOT_STYLE     0
+#define PLOT_STYLE_LINE                0
+#define PLOT_STYLE_IMPULSE     1
+#define PLOT_STYLE_FILLED_BAR  2
+#define MAX_PLOT_STYLES                3
+static char *plot_style_name[MAX_PLOT_STYLES] = {
+       "Line",
+       "Impulse",
+       "FBar",
+};
+
+
+#define COUNT_TYPE_FRAMES   0
+#define COUNT_TYPE_BYTES    1
 #define COUNT_TYPE_ADVANCED 2
-static char *max_count_types[MAX_COUNT_TYPES] = {"frames/tick", "bytes/tick", "advanced..."};
+#define MAX_COUNT_TYPES 3
+static char *count_type_names[MAX_COUNT_TYPES] = {"Packets/Tick", "Bytes/Tick", "Advanced..."};
 
 /* unit is in ms */
-#define MAX_TICK_VALUES 4
-#define DEFAULT_TICK_VALUE 2
-static guint max_tick_values[MAX_TICK_VALUES] = { 10, 100, 1000, 10000 };
+#define MAX_TICK_VALUES 5
+#define DEFAULT_TICK_VALUE 3
+static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
 
-#define MAX_CALC_TYPES 5
 #define CALC_TYPE_SUM  0
 #define CALC_TYPE_COUNT        1
 #define CALC_TYPE_MAX  2
 #define CALC_TYPE_MIN  3
 #define CALC_TYPE_AVG  4
-static char *max_calc_types[MAX_CALC_TYPES] = {"SUM(*)", "COUNT(*)", "MAX(*)", "MIN(*)", "AVG(*)"};
+#define CALC_TYPE_LOAD 5
+#define MAX_CALC_TYPES 6
+static char *calc_type_names[MAX_CALC_TYPES] = {"SUM(*)", "COUNT(*)", "MAX(*)", "MIN(*)", "AVG(*)", "LOAD(*)"};
 
 
-typedef struct _io_stat_item_t {
-       struct _io_stat_item_t *next;
-       struct _io_stat_item_t *prev;
-       guint32 time;   /* this is number of ms since start of capture */
-       guint32 frames; /* always calculated, will hold number of packets/frames*/
-       guint32 bytes;
+typedef struct _io_stat_calc_type_t {
+       struct _io_stat_graph_t *gio;
+       int calc_type;
+} io_stat_calc_type_t;
+
+#define NUM_IO_ITEMS 100000
+typedef struct _io_item_t {
+       guint32 frames; /* always calculated, will hold number of frames*/
+       guint32 bytes;  /* always calculated, will hold number of bytes*/
        gint32 int_max;
        gint32 int_min;
        gint32 int_tot;
        nstime_t time_max;
        nstime_t time_min;
        nstime_t time_tot;
-} io_stat_item_t;
-
-typedef struct _io_stat_calc_type_t {
-       struct _io_stat_graph_t *gio;
-       int calc_type;
-} io_stat_calc_type_t;
+} io_item_t;
 
 typedef struct _io_stat_graph_t {
        struct _io_stat_t *io;
-       struct _io_stat_item_t *counts;
-       int display;
+       io_item_t items[NUM_IO_ITEMS];
+       int plot_style;
+       gboolean display;
        GtkWidget *display_button;
-       GtkWidget *color_button;
-       GtkWidget *filter_button;
+       GtkWidget *filter_field;
        GtkWidget *advanced_buttons;
        int calc_type;
        io_stat_calc_type_t calc_types[MAX_CALC_TYPES];
@@ -121,38 +135,15 @@ typedef struct _io_stat_graph_t {
        GtkWidget *filter_bt;
 } io_stat_graph_t;
 
-typedef struct _io_stat_yscale_t {
-       struct _io_stat_t *io;
-       int yscale;
-} io_stat_yscale_t;
-
-typedef struct _io_stat_pixels_per_tick_t {
-       struct _io_stat_t *io;
-       int pixels_per_tick;
-} io_stat_pixels_per_tick_t;
-
-typedef struct _io_stat_count_type_t {
-       struct _io_stat_t *io;
-       int count_type;
-} io_stat_count_type_t;
-
-typedef struct _io_stat_tick_interval_t {
-       struct _io_stat_t *io;
-       int interval;
-} io_stat_tick_interval_t;
-
 
 typedef struct _io_stat_t {
-       int needs_redraw;
+       gboolean needs_redraw;
        gint32 interval;    /* measurement interval in ms */
        guint32 last_interval; 
-       guint32 max_interval;
+       guint32 max_interval; /* XXX max_interval and num_items are redundant */
+       guint32 num_items;
 
        struct _io_stat_graph_t graphs[MAX_GRAPHS];
-       struct _io_stat_yscale_t yscale[MAX_YSCALE];
-       struct _io_stat_pixels_per_tick_t pixelspertick[MAX_PIXELS_PER_TICK];
-       struct _io_stat_count_type_t counttype[MAX_COUNT_TYPES];
-       struct _io_stat_tick_interval_t tick_val[MAX_TICK_VALUES];
        GtkWidget *window;
        GtkWidget *draw_area;
        GdkPixmap *pixmap;
@@ -174,63 +165,65 @@ GdkColormap *colormap;
 
 static void init_io_stat_window(io_stat_t *io);
 
-/* Hash table to keep track of widget to io_stat_t mappings.
-   Did it this way since i could not find a clean way to associate private 
-   data with a widget using the API */
-static GHashTable *io_stat_widget_table=NULL;
-static guint
-io_stat_widget_hash(gconstpointer k)
+static void
+io_stat_set_title(io_stat_t *io)
 {
-       guint32 frame = (guint32)k;
+       char            *title;
 
-       return frame;
+       if(!io->window){
+               return;
+       }
+       title = g_strdup_printf("Ethereal IO Graphs: %s", cf_get_display_name(&cfile));
+       gtk_window_set_title(GTK_WINDOW(io->window), title);
+       g_free(title);
 }
-static gint
-io_stat_widget_equal(gconstpointer k1, gconstpointer k2)
-{
-       guint32 frame1 = (guint32)k1;
-       guint32 frame2 = (guint32)k2;
 
-       return frame1==frame2;
-}
+static void
+io_stat_reset(io_stat_t *io)
+{
+       int i, j;
 
+       io->needs_redraw=TRUE;
+       for(i=0;i<MAX_GRAPHS;i++){
+               for(j=0;j<NUM_IO_ITEMS;j++){
+                       io_item_t *ioi;
+                       ioi=&io->graphs[i].items[j];
+
+                       ioi->frames=0;
+                       ioi->bytes=0;
+                       ioi->int_max=0;
+                       ioi->int_min=0;
+                       ioi->int_tot=0;
+                       ioi->time_max.secs=0;
+                       ioi->time_max.nsecs=0;
+                       ioi->time_min.secs=0;
+                       ioi->time_min.nsecs=0;
+                       ioi->time_tot.secs=0;
+                       ioi->time_tot.nsecs=0;
+               }
+       }
+       io->last_interval=0xffffffff;
+       io->max_interval=0;
+       io->num_items=0;
 
+       io_stat_set_title(io);
+}
 
 static void
 gtk_iostat_reset(void *g)
 {
        io_stat_graph_t *gio=g;
-       io_stat_item_t *it;
-
-       gio->io->needs_redraw=1;
-
-       while(gio->counts->next){
-               it=gio->counts->next;
-               gio->counts->next=it->next;
-               g_free(it);
-       }
 
-       gio->counts->prev=gio->counts;
-       gio->counts->next=NULL;
-       gio->counts->time=0;
-       gio->counts->frames=0;
-       gio->counts->bytes=0;
-       gio->counts->int_max=0;
-       gio->counts->int_min=0;
-       gio->counts->int_tot=0;
-
-       gio->io->last_interval=0xffffffff;
-       gio->io->max_interval=0;
+       io_stat_reset(gio->io);
 }
 
-
 static int
 gtk_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, void *dummy _U_)
 {
        io_stat_graph_t *git=g;
-       io_stat_item_t *it;
+       io_item_t *it;
        nstime_t time_delta;
-       guint32 adjusted_time;
+       int idx;
 
        /* we sometimes get called when git is disabled.
           this is a bug since the tap listener should be removed first */
@@ -238,13 +231,12 @@ gtk_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, void *dummy
                return 0;
        }
 
-       git->io->needs_redraw=1;
-
-       /* the prev item before the main one is always the last interval we saw packets for */
-       it=git->counts->prev;
+       git->io->needs_redraw=TRUE;
 
-       /* XXX for the time being, just ignore all frames that are in the past.
-          should be fixed in the future but hopefully it is uncommon */
+       /* 
+        * Find which interval this is supposed to to in and store the
+        * interval index as idx
+        */
        time_delta.secs=pinfo->fd->rel_secs;
        time_delta.nsecs=pinfo->fd->rel_usecs*1000;
        if(time_delta.nsecs<0){
@@ -252,47 +244,31 @@ gtk_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, void *dummy
                time_delta.nsecs+=1000000000;
        }
        if(time_delta.secs<0){
-               return TRUE;
+               return FALSE;
        }
-       /* time since start of capture in ms */
-       adjusted_time=time_delta.secs*1000+time_delta.nsecs/1000000;
+       idx=(time_delta.secs*1000+time_delta.nsecs/1000000)/git->io->interval;
 
-       /* timestamps jumped backwards, just ignore the packet.
-          if this is common someone can fix this later */
-       if(adjusted_time<it->time){
-               return TRUE;
+       /* some sanity checks */
+       if((idx<0)||(idx>=NUM_IO_ITEMS)){
+               return FALSE;
        }
 
-
-       /* we have moved into a new interval, we need to create a new struct */
-       if(adjusted_time>=(it->time+git->io->interval)){
-               it->next=g_malloc(sizeof(io_stat_item_t));
-               it->next->prev=it;
-               it->next->next=NULL;
-               it=it->next;
-               git->counts->prev=it;
-
-               /* set time of new counter struct of adjusted_time rounded
-                  to multiple of intervals */
-               it->time=(adjusted_time/git->io->interval)*git->io->interval;
-               it->frames=0;
-               it->bytes=0;
-               it->int_max=0;          
-               it->int_min=0;          
-               it->int_tot=0;
-               it->time_max.secs=0;
-               it->time_max.nsecs=0;
-               it->time_min.secs=0;
-               it->time_min.nsecs=0;
-               it->time_tot.secs=0;
-               it->time_tot.nsecs=0;
-               if(it->time>git->io->max_interval){
-                       git->io->max_interval=it->time;
-               }
+       /* update num_items */
+       if((guint32)idx > git->io->num_items){
+               git->io->num_items=idx;
+               git->io->max_interval=idx*git->io->interval;
        }
 
-       /* it will now give us the current structure to use to store the data in */
+       /*
+        * Find the appropriate io_item_t structure 
+        */
+       it=&git->items[idx];
 
+
+       /*
+        * For ADVANCED mode we need to keep track of some more stuff
+        * than just frame and byte counts
+        */
        if(git->io->count_type==COUNT_TYPE_ADVANCED){
                GPtrArray *gp;
                guint i;
@@ -302,34 +278,10 @@ gtk_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, void *dummy
                        return FALSE;
                }
 
-               if(it->frames==0){
-                       int new_int;
-                       nstime_t *new_time;
-
-                       switch(proto_registrar_get_ftype(git->hf_index)){
-                       case FT_UINT8:
-                       case FT_UINT16:
-                       case FT_UINT24:
-                       case FT_UINT32:
-                       case FT_INT8:
-                       case FT_INT16:
-                       case FT_INT24:
-                       case FT_INT32:
-                               new_int=fvalue_get_integer(((field_info *)gp->pdata[0])->value);
-                               it->int_max=new_int;
-                               it->int_min=new_int;
-                               it->int_tot=0;
-                               break;
-                       case FT_RELATIVE_TIME:
-                               new_time=fvalue_get(((field_info *)gp->pdata[0])->value);
-                               it->time_max.secs=new_time->secs;
-                               it->time_max.nsecs=new_time->nsecs;
-                               it->time_min.secs=new_time->secs;
-                               it->time_min.nsecs=new_time->nsecs;
-                               it->time_tot.secs=0;
-                               it->time_tot.nsecs=0;
-                       }
-               }
+               /* update the appropriate counters, make sure that if 
+                * frames==0 then this is the first seen value so
+                * set any min/max values accordingly 
+                */
                for(i=0;i<gp->len;i++){
                        int new_int;
                        nstime_t *new_time;
@@ -343,36 +295,81 @@ gtk_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, void *dummy
                        case FT_INT16:
                        case FT_INT24:
                        case FT_INT32:
-                               new_int=fvalue_get_integer(((field_info *)gp->pdata[i])->value);
+                               new_int=fvalue_get_integer(&((field_info *)gp->pdata[i])->value);
 
-                               if(new_int>it->int_max){
+                               if((new_int>it->int_max)||(it->frames==0)){
                                        it->int_max=new_int;
                                }
-                               if(new_int<it->int_min){
+                               if((new_int<it->int_min)||(it->frames==0)){
                                        it->int_min=new_int;
                                }
                                it->int_tot+=new_int;
                                break;
                        case FT_RELATIVE_TIME:
-                               new_time=fvalue_get(((field_info *)gp->pdata[0])->value);
+                               new_time=fvalue_get(&((field_info *)gp->pdata[0])->value);
 
-                               if( (new_time->secs>it->time_max.secs)
-                               ||( (new_time->secs==it->time_max.secs)
-                                 &&(new_time->nsecs>it->time_max.nsecs))){
-                                       it->time_max.secs=new_time->secs;
-                                       it->time_max.nsecs=new_time->nsecs;
-                               }
-                               if( (new_time->secs<it->time_min.secs)
-                               ||( (new_time->secs==it->time_min.secs)
-                                 &&(new_time->nsecs<it->time_min.nsecs))){
-                                       it->time_min.secs=new_time->secs;
-                                       it->time_min.nsecs=new_time->nsecs;
-                               }
-                               it->time_tot.secs+=new_time->secs;
-                               it->time_tot.nsecs+=new_time->nsecs;
-                               if(it->time_tot.nsecs>=1000000000){
-                                       it->time_tot.nsecs-=1000000000;
-                                       it->time_tot.secs++;
+                               switch(git->calc_type){
+#ifdef G_HAVE_UINT64
+                                       guint64 t, pt; /* time in us */
+#else
+                                       guint32 t, pt;
+#endif
+                                       int i;
+                               case CALC_TYPE_LOAD:
+                                       /* it is a LOAD calculation of a relative time field. 
+                                        * add the time this call spanned to each
+                                        * interval it spanned according to its contribution 
+                                        * to that interval.
+                                        */
+                                       t=new_time->secs;
+                                       t=t*1000000+new_time->nsecs/1000;
+                                       i=idx;
+                                       /* handle current interval */
+                                       pt=pinfo->fd->rel_secs*1000000+pinfo->fd->rel_usecs;
+                                       pt=pt%(git->io->interval*1000);
+                                       if(pt>t){
+                                               pt=t;
+                                       }
+                                       while(t){
+                                               git->items[i].time_tot.nsecs+=pt*1000;
+                                               if(git->items[i].time_tot.nsecs>1000000000){
+                                                       git->items[i].time_tot.secs++;
+                                                       git->items[i].time_tot.nsecs-=1000000000;
+                                               }
+
+                                               if(i==0){
+                                                       break;
+                                               }
+                                               i--;
+                                               t-=pt;
+                                               if(t > (guint32) (git->io->interval*1000)){
+                                                       pt=git->io->interval*1000;
+                                               } else {
+                                                       pt=t;
+                                               }
+                                       }
+                                       break;
+                               default:
+                                       if( (new_time->secs>it->time_max.secs)
+                                       ||( (new_time->secs==it->time_max.secs)
+                                         &&(new_time->nsecs>it->time_max.nsecs))
+                                       ||(it->frames==0)){
+                                               it->time_max.secs=new_time->secs;
+                                               it->time_max.nsecs=new_time->nsecs;
+                                       }
+                                       if( (new_time->secs<it->time_min.secs)
+                                       ||( (new_time->secs==it->time_min.secs)
+                                         &&(new_time->nsecs<it->time_min.nsecs))
+                                       ||(it->frames==0)){
+                                               it->time_min.secs=new_time->secs;
+                                               it->time_min.nsecs=new_time->nsecs;
+                                       }
+                                       it->time_tot.secs+=new_time->secs;
+                                       it->time_tot.nsecs+=new_time->nsecs;
+                                       if(it->time_tot.nsecs>=1000000000){
+                                               it->time_tot.nsecs-=1000000000;
+                                               it->time_tot.secs++;
+                                       }
                                }
 
                        }
@@ -387,80 +384,93 @@ gtk_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, void *dummy
 
 
 static guint32
-get_it_value(io_stat_t *io, io_stat_item_t *it, int adv_type, int calc_type)
+get_it_value(io_stat_t *io, int graph_id, int idx)
 {
        guint32 value=0;
+       int adv_type;
+       io_item_t *it;
+
+       it=&io->graphs[graph_id].items[idx];
 
        switch(io->count_type){
-       case 0:
-               value=it->frames;
-               break;
-       case 1:
-               value=it->bytes;
+       case COUNT_TYPE_FRAMES:
+               return it->frames;
+       case COUNT_TYPE_BYTES:
+               return it->bytes;
+       }
+
+
+       adv_type=proto_registrar_get_ftype(io->graphs[graph_id].hf_index);
+       switch(adv_type){
+       case FT_NONE:
+               switch(io->graphs[graph_id].calc_type){
+               case CALC_TYPE_COUNT:
+                       value=it->frames;
+                       break;
+               default:
+                       break;
+               }
                break;
-       case COUNT_TYPE_ADVANCED:
-               switch(adv_type){
-               case FT_UINT8:
-               case FT_UINT16:
-               case FT_UINT24:
-               case FT_UINT32:
-               case FT_INT8:
-               case FT_INT16:
-               case FT_INT24:
-               case FT_INT32:
-                       switch(calc_type){
-                       case CALC_TYPE_SUM:
-                               value=it->int_tot;
-                               break;
-                       case CALC_TYPE_COUNT:
-                               value=it->frames;
-                               break;
-                       case CALC_TYPE_MAX:
-                               value=it->int_max;
-                               break;
-                       case CALC_TYPE_MIN:
-                               value=it->int_min;
-                               break;
-                       case CALC_TYPE_AVG:
-                               if(it->frames){
-                                       value=it->int_tot/it->frames;
-                               } else {
-                                       value=0;
-                               }
-                               break;
-                       default:
-                               break;
+       case FT_UINT8:
+       case FT_UINT16:
+       case FT_UINT24:
+       case FT_UINT32:
+       case FT_INT8:
+       case FT_INT16:
+       case FT_INT24:
+       case FT_INT32:
+               switch(io->graphs[graph_id].calc_type){
+               case CALC_TYPE_SUM:
+                       value=it->int_tot;
+                       break;
+               case CALC_TYPE_COUNT:
+                       value=it->frames;
+                       break;
+               case CALC_TYPE_MAX:
+                       value=it->int_max;
+                       break;
+               case CALC_TYPE_MIN:
+                       value=it->int_min;
+                       break;
+               case CALC_TYPE_AVG:
+                       if(it->frames){
+                               value=it->int_tot/it->frames;
+                       } else {
+                               value=0;
                        }
                        break;
-               case FT_RELATIVE_TIME:
-                       switch(calc_type){
-                       case CALC_TYPE_COUNT:
-                               value=it->frames;
-                               break;
-                       case CALC_TYPE_MAX:
-                               value=it->time_max.secs*1000000+it->time_max.nsecs/1000;
-                               break;
-                       case CALC_TYPE_MIN:
-                               value=it->time_min.secs*1000000+it->time_min.nsecs/1000;
-                               break;
-                       case CALC_TYPE_AVG:
-                               if(it->frames){
+               default:
+                       break;
+               }
+               break;
+       case FT_RELATIVE_TIME:
+               switch(io->graphs[graph_id].calc_type){
+               case CALC_TYPE_COUNT:
+                       value=it->frames;
+                       break;
+               case CALC_TYPE_MAX:
+                       value=it->time_max.secs*1000000+it->time_max.nsecs/1000;
+                       break;
+               case CALC_TYPE_MIN:
+                       value=it->time_min.secs*1000000+it->time_min.nsecs/1000;
+                       break;
+               case CALC_TYPE_AVG:
+                       if(it->frames){
 #ifdef G_HAVE_UINT64
-                                       guint64 tmp;
+                               guint64 t; /* time in us */
 #else
-                                       guint32 tmp;
+                               guint32 t;
 #endif
-                                       tmp=it->time_tot.secs;
-                                       tmp=tmp*1000000+it->time_tot.nsecs/1000;
-                                       value=tmp/it->frames;
-                               } else {
-                                       value=0;
-                               }
-                               break;
-                       default:
-                               break;
+                               t=it->time_tot.secs;
+                               t=t*1000000+it->time_tot.nsecs/1000;
+                               value=t/it->frames;
+                       } else {
+                               value=0;
                        }
                        break;
+               case CALC_TYPE_LOAD:
+                       value=(it->time_tot.secs*1000000+it->time_tot.nsecs/1000)/io->interval;
+                       break;
                default:
                        break;
                }
@@ -471,17 +481,29 @@ get_it_value(io_stat_t *io, io_stat_item_t *it, int adv_type, int calc_type)
        return value;
 }
 
+
 static void
-gtk_iostat_draw(void *g)
+print_time_scale_string(char *buf, int buf_len, guint32 t)
+{
+       if(t>=10000000){
+               g_snprintf(buf, buf_len, "%ds",t/1000000);
+       } else if(t>=1000000){
+               g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
+       } else if(t>=10000){
+               g_snprintf(buf, buf_len, "%dms",t/1000);
+       } else if(t>=1000){
+               g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
+       } else {
+               g_snprintf(buf, buf_len, "%dus",t);
+       }
+}
+
+static void
+io_stat_draw(io_stat_t *io)
 {
-       io_stat_graph_t *git=g;
-       io_stat_t *io;
-       io_stat_item_t *it;
        int i;
-       guint32 value;
        guint32 last_interval, first_interval, interval_delta, delta_multiplier;
        gint32 current_interval;
-       guint32 max_y;
        guint32 left_x_border;
        guint32 right_x_border;
        guint32 top_y_border;
@@ -495,45 +517,78 @@ gtk_iostat_draw(void *g)
        guint32 draw_width, draw_height;
        char label_string[15];
 
-       io=git->io;
+       /* new variables */
+       guint32 num_time_intervals;
+       guint32 max_value;              /* max value of seen data */
+       guint32 max_y;                  /* max value of the Y scale */
+       gboolean draw_y_as_time;
+
 #if GTK_MAJOR_VERSION <2
        font = io->draw_area->style->font;
 #endif
 
-       if(!git->io->needs_redraw){
+       if(!io->needs_redraw){
                return;
        }
-       git->io->needs_redraw=0;
+       io->needs_redraw=FALSE;
 
-       /* if we havent specified the last_interval via the gui,
-          then just pick the most recent one */
-       if(io->last_interval==0xffffffff){
-               last_interval=io->max_interval;
-       } else {
-               last_interval=io->last_interval;
+
+       /* 
+        * Find the length of the intervals we have data for
+        * so we know how large arrays we need to malloc()
+        */
+       num_time_intervals=io->num_items;
+       /* if there isnt anything to do, just return */
+       if(num_time_intervals==0){
+               return;
+       }
+       num_time_intervals+=1;
+       /* XXX move this check to _packet() */
+       if(num_time_intervals>NUM_IO_ITEMS){
+               simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IO-Stat error. There are too many entries, bailing out");
+               return;
        }
-       
 
-       if(io->max_y_units==AUTO_MAX_YSCALE){
-               guint32 max_value=0;
-               for(i=0;i<MAX_GRAPHS;i++){
-                       int adv_type=0;
 
-                       if( (!io->graphs[i].display) || (!io->graphs[i].counts) ){
-                               continue;
-                       }
+       /* 
+        * find the max value so we can autoscale the y axis
+        */
+       max_value=0;
+       for(i=0;i<MAX_GRAPHS;i++){
+               int idx;
 
-                       if(io->count_type==COUNT_TYPE_ADVANCED){
-                               adv_type=proto_registrar_get_ftype(io->graphs[i].hf_index);
-                       }
+               if(!io->graphs[i].display){
+                       continue;
+               }
+               for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
+                       guint32 val;
 
-                       for(it=io->graphs[i].counts;it;it=it->next){
-                               value=get_it_value(io, it, adv_type, io->graphs[i].calc_type);
-                               if(value>=max_value){
-                                       max_value=value;
-                               }
+                       val=get_it_value(io, i, idx);
+
+                       /* keep track of the max value we have encountered */
+                       if(val>max_value){
+                               max_value=val;
                        }
                }
+       }
+
+
+
+       /* 
+        * Clear out old plot
+        */
+        gdk_draw_rectangle(io->pixmap,
+                           io->draw_area->style->white_gc,
+                           TRUE,
+                           0, 0,
+                           io->draw_area->allocation.width,
+                           io->draw_area->allocation.height);
+
+
+       /*
+        * Calculate the y scale we should use
+        */
+       if(io->max_y_units==AUTO_MAX_YSCALE){
                max_y=yscale_max[MAX_YSCALE-1];
                for(i=MAX_YSCALE-1;i>0;i--){
                        if(max_value<yscale_max[i]){
@@ -541,12 +596,59 @@ gtk_iostat_draw(void *g)
                        }
                }
        } else {
+               /* the user had specified an explicit y scale to use */
                max_y=io->max_y_units;
        }
 
 
-       /* just assume that max_y will be the longest string */
-       sprintf(label_string,"%d", max_y);
+       /*
+        * If we use ADVANCED and all the graphs are plotting
+        * either MIN/MAX/AVG of an FT_RELATIVE_TIME field
+        * then we will do some some special processing for the
+        * labels for the Y axis below:
+        *   we will append the time unit " s" " ms" or " us"
+        *   and we will present the unit in decimal
+        */
+       draw_y_as_time=FALSE;
+       if(io->count_type==COUNT_TYPE_ADVANCED){
+               draw_y_as_time=TRUE;
+               for(i=0;i<MAX_GRAPHS;i++){
+                       int adv_type;
+
+                       if(!io->graphs[i].display){
+                               continue;
+                       }
+                       adv_type=proto_registrar_get_ftype(io->graphs[i].hf_index);
+                       switch(adv_type){
+                       case FT_RELATIVE_TIME:
+                               switch(io->graphs[i].calc_type){
+                               case CALC_TYPE_MAX:
+                               case CALC_TYPE_MIN:
+                               case CALC_TYPE_AVG:
+                                       break;
+                               default:
+                                       draw_y_as_time=FALSE;
+                               }
+                               break;
+                       default:
+                               draw_y_as_time=FALSE;
+                       }
+               }
+       }
+
+
+
+       /* 
+        * Calculate size of borders surrounding the plot 
+        * The border on the right side needs to be adjusted depending
+        * on the width of the text labels. For simplicity we assume that the
+        * top y scale label will be the widest one
+        */
+       if(draw_y_as_time){
+               print_time_scale_string(label_string, 15, max_y);
+       } else {
+               g_snprintf(label_string, 15, "%d", max_y);
+       }
 #if GTK_MAJOR_VERSION < 2
         label_width=gdk_string_width(font, label_string);
         label_height=gdk_string_height(font, label_string);
@@ -554,25 +656,23 @@ gtk_iostat_draw(void *g)
         layout = gtk_widget_create_pango_layout(io->draw_area, label_string);
         pango_layout_get_pixel_size(layout, &label_width, &label_height);
 #endif
-
        left_x_border=10;
        right_x_border=label_width+20;
        top_y_border=10;
        bottom_y_border=label_height+20;
 
+
+       /*
+        * Calculate the size of the drawing area for the actual plot
+        */
        draw_width=io->pixmap_width-right_x_border-left_x_border;
        draw_height=io->pixmap_height-top_y_border-bottom_y_border;
 
 
-       /* clear out old plot */
-        gdk_draw_rectangle(io->pixmap,
-                           io->draw_area->style->white_gc,
-                           TRUE,
-                           0, 0,
-                           io->draw_area->allocation.width,
-                           io->draw_area->allocation.height);
-
-       /* plot the y-scale */
+       /* 
+        * Draw the y axis and labels
+        * (we always draw the y scale with 11 ticks along the axis)
+        */
        gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
                io->pixmap_width-right_x_border+1, 
                top_y_border,
@@ -583,33 +683,107 @@ gtk_iostat_draw(void *g)
 
                xwidth=5;
                if(!(i%5)){
+                       /* first, middle and last tick are slightly longer */
                        xwidth=10;
                }
+               /* draw the tick */
                gdk_draw_line(io->pixmap, io->draw_area->style->black_gc, 
                        io->pixmap_width-right_x_border+1, 
                        io->pixmap_height-bottom_y_border-draw_height*i/10, 
                        io->pixmap_width-right_x_border+1+xwidth, 
                        io->pixmap_height-bottom_y_border-draw_height*i/10);
-               sprintf(label_string,"%d", max_y*i/10);
+               /* draw the labels */
+               if(i==0){
+                       if(draw_y_as_time){
+                               print_time_scale_string(label_string, 15, (max_y*i/10));
+                       } else {
+                               g_snprintf(label_string, 15, "%d", max_y*i/10);
+                       }
 #if GTK_MAJOR_VERSION < 2
-                lwidth=gdk_string_width(font, label_string);
-                gdk_draw_string(io->pixmap,
-                                font,
-                                io->draw_area->style->black_gc,
-                                io->pixmap_width-right_x_border+15+label_width-lwidth,
-                                io->pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
-                                label_string);
+                       lwidth=gdk_string_width(font, label_string);
+                       gdk_draw_string(io->pixmap,
+                                       font,
+                                       io->draw_area->style->black_gc,
+                                       io->pixmap_width-right_x_border+15+label_width-lwidth,
+                                       io->pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
+                                       label_string);
 #else
-                pango_layout_set_text(layout, label_string, -1);
-                pango_layout_get_pixel_size(layout, &lwidth, NULL);
-               gdk_draw_layout(io->pixmap,
-                                io->draw_area->style->black_gc,
-                                io->pixmap_width-right_x_border+15+label_width-lwidth,
-                                io->pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
-                                layout);
+                       pango_layout_set_text(layout, label_string, -1);
+                       pango_layout_get_pixel_size(layout, &lwidth, NULL);
+                       gdk_draw_layout(io->pixmap,
+                                       io->draw_area->style->black_gc,
+                                       io->pixmap_width-right_x_border+15+label_width-lwidth,
+                                       io->pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
+                                       layout);
 #endif
+               }
+               if(i==5){
+                       if(draw_y_as_time){
+                               print_time_scale_string(label_string, 15, (max_y*i/10));
+                       } else {
+                               g_snprintf(label_string, 15, "%d", max_y*i/10);
+                       }
+#if GTK_MAJOR_VERSION < 2
+                       lwidth=gdk_string_width(font, label_string);
+                       gdk_draw_string(io->pixmap,
+                                       font,
+                                       io->draw_area->style->black_gc,
+                                       io->pixmap_width-right_x_border+15+label_width-lwidth,
+                                       io->pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
+                                       label_string);
+#else
+                       pango_layout_set_text(layout, label_string, -1);
+                       pango_layout_get_pixel_size(layout, &lwidth, NULL);
+                       gdk_draw_layout(io->pixmap,
+                                       io->draw_area->style->black_gc,
+                                       io->pixmap_width-right_x_border+15+label_width-lwidth,
+                                       io->pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
+                                       layout);
+#endif
+               }
+               if(i==10){
+                       if(draw_y_as_time){
+                               print_time_scale_string(label_string, 15, (max_y*i/10));
+                       } else {
+                               g_snprintf(label_string, 15, "%d", max_y*i/10);
+                       }
+#if GTK_MAJOR_VERSION < 2
+                       lwidth=gdk_string_width(font, label_string);
+                       gdk_draw_string(io->pixmap,
+                                       font,
+                                       io->draw_area->style->black_gc,
+                                       io->pixmap_width-right_x_border+15+label_width-lwidth,
+                                       io->pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
+                                       label_string);
+#else
+                       pango_layout_set_text(layout, label_string, -1);
+                       pango_layout_get_pixel_size(layout, &lwidth, NULL);
+                       gdk_draw_layout(io->pixmap,
+                                       io->draw_area->style->black_gc,
+                                       io->pixmap_width-right_x_border+15+label_width-lwidth,
+                                       io->pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
+                                       layout);
+#endif
+               }
+       }
+
+
+
+       /* 
+        * if we have not specified the last_interval via the gui,
+        * then just pick the current end of the capture so that is scrolls
+        * nicely when doing live captures
+        */
+       if(io->last_interval==0xffffffff){
+               last_interval=io->max_interval;
+       } else {
+               last_interval=io->last_interval;
        }
+       
+
+
 
+/*XXX*/
        /* plot the x-scale */
        gdk_draw_line(io->pixmap, io->draw_area->style->black_gc, left_x_border, io->pixmap_height-bottom_y_border+1, io->pixmap_width-right_x_border+1, io->pixmap_height-bottom_y_border+1);
 
@@ -620,7 +794,6 @@ gtk_iostat_draw(void *g)
                first_interval=0;
        }
 
-
        interval_delta=1;
        delta_multiplier=5;
        while(interval_delta<((last_interval-first_interval)/10)){
@@ -632,7 +805,6 @@ gtk_iostat_draw(void *g)
                }
        }
 
-
        for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-io->interval){
                int x, xlen;
 
@@ -657,13 +829,13 @@ gtk_iostat_draw(void *g)
                if(xlen==10){
                        int lwidth;
                        if(io->interval>=1000){
-                               sprintf(label_string,"%d", current_interval/1000);
+                               g_snprintf(label_string, 15, "%ds", current_interval/1000);
                        } else if(io->interval>=100){
-                               sprintf(label_string,"%d.%1d", current_interval/1000,(current_interval/100)%10);
+                               g_snprintf(label_string, 15, "%d.%1ds", current_interval/1000,(current_interval/100)%10);
                        } else if(io->interval>=10){
-                               sprintf(label_string,"%d.%2d", current_interval/1000,(current_interval/10)%100);
+                               g_snprintf(label_string, 15, "%d.%2ds", current_interval/1000,(current_interval/10)%100);
                        } else {
-                               sprintf(label_string,"%d.%3d", current_interval/1000,current_interval%1000);
+                               g_snprintf(label_string, 15, "%d.%3ds", current_interval/1000,current_interval%1000);
                        }
 #if GTK_MAJOR_VERSION < 2
                         lwidth=gdk_string_width(font, label_string);
@@ -690,106 +862,76 @@ gtk_iostat_draw(void *g)
 #endif
 
 
-       /* loop over all items */
+
+       /* 
+        * Loop over all graphs and draw them 
+        */
        for(i=MAX_GRAPHS-1;i>=0;i--){
-               int adv_type=0;
-               int first_drawed=1;
+               guint32 interval;
+               guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
 
-               if( (!io->graphs[i].display) || (!io->graphs[i].counts) ){
+               if(!io->graphs[i].display){
                        continue;
                }
 
-               if(io->count_type==COUNT_TYPE_ADVANCED){
-                       adv_type=proto_registrar_get_ftype(io->graphs[i].hf_index);
-               }
-
-               /* loop over all intervals for the item */
-               for(it=io->graphs[i].counts;it;it=it->next){
-                       guint32 startx,starty, nexty;
-                       guint32 val=0;
-                       guint32 next_val=0;
-
-                       /* skip it if is outside the graph */
-                       if(it->time<first_interval){
-                               continue;
-                       }
-                       if(it->time>last_interval){
-                               continue;
-                       }
+               /* initialize prev x/y to the low left corner of the graph */
+               prev_x_pos=draw_width-1-io->pixels_per_tick*((last_interval-first_interval)/io->interval+1)+left_x_border;
+               prev_y_pos=draw_height-1+top_y_border;
 
-                       val=get_it_value(io, it, adv_type, io->graphs[i].calc_type);
-                       if(it->next){
-                               next_val=get_it_value(io, it->next, adv_type, io->graphs[i].calc_type);
-                       }
+               for(interval=first_interval+io->interval;interval<=last_interval;interval+=io->interval){
+                       guint32 val;
 
+                       x_pos=draw_width-1-io->pixels_per_tick*((last_interval-interval)/io->interval+1)+left_x_border;
 
-                       startx=draw_width-1-io->pixels_per_tick*((last_interval-it->time)/io->interval+1);
+                       val=get_it_value(io, i, interval/io->interval);
                        if(val>max_y){
-                               starty=0;
-                       } else {
-                               starty=draw_height-1-(val*draw_height)/max_y;
-                       }
-                       if(next_val>max_y){
-                               nexty=0;
+                               y_pos=0;
                        } else {
-                               nexty=draw_height-1-(next_val*draw_height)/max_y;
+                               y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
                        }
 
-
-                       /* just skip intervals that ar all or partially outside
-                          the draw_area */
-                       if(startx <=0){
-                               continue;
-                       }
-
-
-                       /* the first seen data for this graph might be 
-                        * somewhere in the middle of the capture,  make sure 
-                        * we start drawing a proper line at zero from the 
-                        * left edge of the graph up until the measurement 
-                        * point.
+                       /* dont need to draw anything if the segment
+                        * is entirely above the top of the graph 
                         */
-                       if(first_drawed){
-                               first_drawed=0;
-                               gdk_draw_line(io->pixmap, io->graphs[i].gc, 10, draw_height-1+10, startx+10, draw_height-1+10);
-                               gdk_draw_line(io->pixmap, io->graphs[i].gc, startx+10, draw_height-1+10, startx+10, starty+10);
-                       }
-
-                       gdk_draw_line(io->pixmap, io->graphs[i].gc, startx+10, starty+10, startx+io->pixels_per_tick-1+10, starty+10);
-
-                       /* if there is no next measured interval but he have 
-                          not reached last_interval yet, just draw 0 for the
-                          rest of the graph */
-                       if( (!it->next) && (it->time!=last_interval) ){
-                               gdk_draw_line(io->pixmap, io->graphs[i].gc, startx+io->pixels_per_tick-1+10, starty+10, startx+io->pixels_per_tick-1+10, draw_height-1+10);
-                               gdk_draw_line(io->pixmap, io->graphs[i].gc, startx+io->pixels_per_tick-1+10, draw_height-1+10, draw_width-1+10, draw_height-1+10);
-                               continue;
-                       }
-
-                       if(!it->next){
+                       if( (prev_y_pos==0) && (y_pos==0) ){
+                               prev_y_pos=y_pos;
+                               prev_x_pos=x_pos;
                                continue;
                        }
 
-                       /* if there is data in next interval, connect to it */
-                       if((it->time+io->interval)==it->next->time){
-                               gdk_draw_line(io->pixmap, io->graphs[i].gc, startx+io->pixels_per_tick-1+10, starty+10, startx+io->pixels_per_tick-1+10, nexty+10);
-                               continue;
+                       switch(io->graphs[i].plot_style){
+                       case PLOT_STYLE_LINE:
+                               gdk_draw_line(io->pixmap, io->graphs[i].gc, 
+                                       prev_x_pos, prev_y_pos, 
+                                       x_pos, y_pos);
+                               break;
+                       case PLOT_STYLE_IMPULSE:
+                               if(val){
+                                       gdk_draw_line(io->pixmap, io->graphs[i].gc, 
+                                               x_pos, draw_height-1+top_y_border,
+                                               x_pos, y_pos);
+                               }
+                               break;
+                       case PLOT_STYLE_FILLED_BAR:
+                               if(val){
+                                       gdk_draw_rectangle(io->pixmap,
+                                               io->graphs[i].gc, TRUE,
+                                               x_pos-io->pixels_per_tick/2,
+                                               draw_height-1-(val*draw_height)/max_y+top_y_border,
+                                               io->pixels_per_tick,
+                                               (val*draw_height)/max_y);
+                                               
+                               }
+                               break;
                        }
 
-                       /* there is a gap until the next interval, we need to
-                          draw a bit more to connect to it. */
-                       if((it->time+io->interval)<it->next->time){
-                               int nextx;
-                               nextx=draw_width-1-io->pixels_per_tick*((last_interval-it->next->time)/io->interval+1);
-                               gdk_draw_line(io->pixmap, io->graphs[i].gc, startx+io->pixels_per_tick-1+10, starty+10, startx+io->pixels_per_tick-1+10, draw_height-1+10);
-                               gdk_draw_line(io->pixmap, io->graphs[i].gc, startx+io->pixels_per_tick-1+10, draw_height-1+10, nextx-1+10, draw_height-1+10);
-                               gdk_draw_line(io->pixmap, io->graphs[i].gc, nextx-1+10, draw_height-1+10, nextx-1+10, nexty+10);
-                               continue;
-                       }
-                       
+                       prev_y_pos=y_pos;
+                       prev_x_pos=x_pos;
                }
        }
 
+
+
        gdk_draw_pixmap(io->draw_area->window,
                        io->draw_area->style->fg_gc[GTK_WIDGET_STATE(io->draw_area)],
                        io->pixmap,
@@ -799,19 +941,106 @@ gtk_iostat_draw(void *g)
 
 
        /* update the scrollbar */
-       io->scrollbar_adjustment->upper=io->max_interval;
-       io->scrollbar_adjustment->step_increment=(last_interval-first_interval)/10;
-       io->scrollbar_adjustment->page_increment=(last_interval-first_interval);
+       io->scrollbar_adjustment->upper=(gfloat) io->max_interval;
+       io->scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
+       io->scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
        if((last_interval-first_interval)*100 < io->max_interval){
-               io->scrollbar_adjustment->page_size=io->max_interval/100;
+               io->scrollbar_adjustment->page_size=(gfloat) (io->max_interval/100);
        } else {
-               io->scrollbar_adjustment->page_size=(last_interval-first_interval);
+               io->scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
        }
        io->scrollbar_adjustment->value=last_interval-io->scrollbar_adjustment->page_size;
        gtk_adjustment_changed(io->scrollbar_adjustment);
        gtk_adjustment_value_changed(io->scrollbar_adjustment);
+
 }
 
+static void
+io_stat_redraw(io_stat_t *io)
+{
+       io->needs_redraw=TRUE;
+       io_stat_draw(io);
+}
+
+static void
+gtk_iostat_draw(void *g)
+{
+       io_stat_graph_t *git=g;
+
+       io_stat_draw(git->io);
+}
+
+
+/* ok we get called with both the filter and the field. 
+   make sure the field is part of the filter.
+   (make sure and make sure  just append it)
+   the field MUST be part of the filter or else we wont
+   be able to pick up the field values after the edt tree has been
+   pruned 
+*/
+static GString *
+enable_graph(io_stat_graph_t *gio, char *filter, char *field)
+{
+       char real_filter[260];
+
+       gio->display=TRUE;
+       
+       real_filter[0]=0;
+       if(filter){
+               /* skip all whitespaces */
+               while(*filter){
+                       if(*filter==' '){
+                               filter++;
+                               continue;
+                       }
+                       if(*filter=='\t'){
+                               filter++;
+                               continue;
+                       }
+                       break;
+               }
+               if(*filter){
+                       strncpy(real_filter, filter, 255);
+                       real_filter[255]=0;
+               }
+       }
+       if(field){
+               /* skip all whitespaces */
+               while(*field){
+                       if(*field==' '){
+                               field++;
+                               continue;
+                       }
+                       if(*field=='\t'){
+                               field++;
+                               continue;
+                       }
+                       break;
+               }
+               if(*field){
+                       if(real_filter[0]!=0){
+                               strcat(real_filter, " && ");
+                       }
+                       strncat(real_filter, field, 259-strlen(real_filter));
+                       real_filter[259]=0;
+               }
+       }
+       return register_tap_listener("frame", gio, real_filter[0]?real_filter:NULL,
+           gtk_iostat_reset, gtk_iostat_packet, gtk_iostat_draw);
+}
+
+static void
+disable_graph(io_stat_graph_t *gio)
+{
+       if (gio->display) {
+               gio->display=FALSE;
+               protect_thread_critical_region();
+               remove_tap_listener(gio);
+               unprotect_thread_critical_region();
+               gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button),
+                   FALSE);
+       }
+}
 
 static void
 gtk_iostat_init(char *optarg _U_)
@@ -828,7 +1057,7 @@ gtk_iostat_init(char *optarg _U_)
        GString *error_string;
 
        io=g_malloc(sizeof(io_stat_t));
-       io->needs_redraw=1;
+       io->needs_redraw=TRUE;
        io->interval=1000;
        io->window=NULL;
        io->draw_area=NULL;
@@ -837,31 +1066,22 @@ gtk_iostat_init(char *optarg _U_)
        io->scrollbar_adjustment=NULL;
        io->pixmap_width=500;
        io->pixmap_height=200;
-       io->pixels_per_tick=5;
+    io->pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
        io->max_y_units=AUTO_MAX_YSCALE;
        io->count_type=0;
        io->last_interval=0xffffffff;
        io->max_interval=0;
+       io->num_items=0;
 
        for(i=0;i<MAX_GRAPHS;i++){
-               io->graphs[i].counts=g_malloc(sizeof(io_stat_item_t));
-               io->graphs[i].counts->prev=io->graphs[i].counts;
-               io->graphs[i].counts->next=NULL;
-               io->graphs[i].counts->time=0;
-               io->graphs[i].counts->frames=0;
-               io->graphs[i].counts->bytes=0;
-               io->graphs[i].counts->int_max=0;
-               io->graphs[i].counts->int_min=0;
-               io->graphs[i].counts->int_tot=0;
                io->graphs[i].gc=NULL;
                io->graphs[i].color.pixel=col[i].pixel;
                io->graphs[i].color.red=col[i].red;
                io->graphs[i].color.green=col[i].green;
                io->graphs[i].color.blue=col[i].blue;
-               io->graphs[i].display=i?0:1;
+               io->graphs[i].display=0;
                io->graphs[i].display_button=NULL;
-               io->graphs[i].color_button=NULL;
-               io->graphs[i].filter_button=NULL;
+               io->graphs[i].filter_field=NULL;
                io->graphs[i].advanced_buttons=NULL;
                io->graphs[i].io=io;
 
@@ -872,17 +1092,16 @@ gtk_iostat_init(char *optarg _U_)
 
                io->graphs[i].filter_bt=NULL;
        }
+       io_stat_reset(io);
 
-       error_string=register_tap_listener("frame", &io->graphs[0], NULL, gtk_iostat_reset, gtk_iostat_packet, gtk_iostat_draw);
+       error_string=enable_graph(&io->graphs[0], NULL, NULL);
        if(error_string){
                fprintf(stderr, "ethereal: Can't attach io_stat tap: %s\n",
                    error_string->str);
                g_string_free(error_string, TRUE);
-               g_free(io->graphs[0].counts);
-               io->graphs[0].counts=NULL;
                io->graphs[0].display=0;
                io->graphs[0].display_button=NULL;
-               io->graphs[0].filter_button=NULL;
+               io->graphs[0].filter_field=NULL;
                io->graphs[0].advanced_buttons=NULL;
                exit(10);
        }                       
@@ -890,54 +1109,30 @@ gtk_iostat_init(char *optarg _U_)
        /* build the GUI */
        init_io_stat_window(io);
 
-       redissect_packets(&cfile);
-       io->needs_redraw=1;
-       gtk_iostat_draw(&io->graphs[0]);
+       retap_packets(&cfile);
+       io_stat_redraw(io);
 }
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
 static gint
 quit(GtkWidget *widget, GdkEventExpose *event _U_)
 {
        int i;
        io_stat_t *io;
 
-       io=g_hash_table_lookup(io_stat_widget_table, (void*)widget);
-       if(io){
-               g_hash_table_remove(io_stat_widget_table, (void*)widget);
-       }
+       io=(io_stat_t *)OBJECT_GET_DATA(widget, "io_stat_t");
 
        for(i=0;i<MAX_GRAPHS;i++){
-               io_stat_item_t *it;
-               protect_thread_critical_region();
-               remove_tap_listener(&io->graphs[i]);
-               while((it=io->graphs[i].counts)){
-                       io->graphs[i].counts=io->graphs[i].counts->next;
-                       g_free(it);
-               }
-               unprotect_thread_critical_region();
+               if(io->graphs[i].display){
+                       protect_thread_critical_region();
+                       remove_tap_listener(&io->graphs[i]);
+                       unprotect_thread_critical_region();
 
-               free(io->graphs[i].args->title);
-               io->graphs[i].args->title=NULL;
+                       free(io->graphs[i].args->title);
+                       io->graphs[i].args->title=NULL;
 
-               g_free(io->graphs[i].args);
-               io->graphs[i].args=NULL;
+                       g_free(io->graphs[i].args);
+                       io->graphs[i].args=NULL;
+               }
        }
        g_free(io);
 
@@ -951,7 +1146,7 @@ configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
        int i;
        io_stat_t *io;
 
-       io=g_hash_table_lookup(io_stat_widget_table, (void*)widget);
+       io=(io_stat_t *)OBJECT_GET_DATA(widget, "io_stat_t");
        if(!io){
                exit(10);
        }
@@ -991,8 +1186,7 @@ configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
                
        }
 
-       io->needs_redraw=1;
-       gtk_iostat_draw(&io->graphs[0]);
+       io_stat_redraw(io);
        return TRUE;
 }
 
@@ -1002,7 +1196,7 @@ scrollbar_changed(GtkWidget *widget _U_, gpointer data)
        io_stat_t *io=(io_stat_t *)data;
        guint32 mi;
 
-       mi=io->scrollbar_adjustment->value+io->scrollbar_adjustment->page_size;
+       mi=(guint32) (io->scrollbar_adjustment->value+io->scrollbar_adjustment->page_size);
        if(io->last_interval==mi){
                return TRUE;
        }
@@ -1012,8 +1206,7 @@ scrollbar_changed(GtkWidget *widget _U_, gpointer data)
        }
 
        io->last_interval=(mi/io->interval)*io->interval;
-       io->needs_redraw=1;
-       gtk_iostat_draw(&io->graphs[0]);
+       io_stat_redraw(io);
 
        return TRUE;
 }
@@ -1023,7 +1216,8 @@ static gint
 expose_event(GtkWidget *widget, GdkEventExpose *event)
 {
        io_stat_t *io;
-       io=g_hash_table_lookup(io_stat_widget_table, (void*)widget);
+
+       io=(io_stat_t *)OBJECT_GET_DATA(widget, "io_stat_t");
        if(!io){
                exit(10);
        }
@@ -1045,7 +1239,7 @@ create_draw_area(io_stat_t *io, GtkWidget *box)
 {
        io->draw_area=gtk_drawing_area_new();
        SIGNAL_CONNECT(io->draw_area, "destroy", quit, io);
-       g_hash_table_insert(io_stat_widget_table, (void*)io->draw_area, (void*)io);
+       OBJECT_SET_DATA(io->draw_area, "io_stat_t", io);
 
        WIDGET_SET_SIZE(io->draw_area, io->pixmap_width, io->pixmap_height);
 
@@ -1066,24 +1260,43 @@ create_draw_area(io_stat_t *io, GtkWidget *box)
 
 
 static void
-tick_interval_select(GtkWidget *item _U_, gpointer key)
+tick_interval_select(GtkWidget *item, gpointer key)
 {
-       io_stat_tick_interval_t *tiv =(io_stat_tick_interval_t *)key;
+       int val;
+       io_stat_t *io;
+
+       io=(io_stat_t *)key;
+       val=(int)OBJECT_GET_DATA(item, "tick_interval");
 
-       tiv->io->interval=tiv->interval;
-       redissect_packets(&cfile);
-       tiv->io->needs_redraw=1;
-       gtk_iostat_draw(&tiv->io->graphs[0]);
+       io->interval=val;
+       retap_packets(&cfile);
+       io_stat_redraw(io);
 }
 
 static void
-pixels_per_tick_select(GtkWidget *item _U_, gpointer key)
+pixels_per_tick_select(GtkWidget *item, gpointer key)
 {
-       io_stat_pixels_per_tick_t *ppt=(io_stat_pixels_per_tick_t *)key;
+       int val;
+       io_stat_t *io;
+
+       io=(io_stat_t *)key;
+       val=(int)OBJECT_GET_DATA(item, "pixels_per_tick");
+       io->pixels_per_tick=val;
+       io_stat_redraw(io);
+}
+
+static void
+plot_style_select(GtkWidget *item, gpointer key)
+{
+       int val;
+       io_stat_graph_t *ppt;
+
+       ppt=(io_stat_graph_t *)key;
+       val=(int)OBJECT_GET_DATA(item, "plot_style");
 
-       ppt->io->pixels_per_tick=ppt->pixels_per_tick;
-       ppt->io->needs_redraw=1;
-       gtk_iostat_draw(&ppt->io->graphs[0]);
+       ppt->plot_style=val;
+
+       io_stat_redraw(ppt->io);
 }
 
 static void 
@@ -1094,11 +1307,12 @@ create_pixels_per_tick_menu_items(io_stat_t *io, GtkWidget *menu)
        int i;
 
        for(i=0;i<MAX_PIXELS_PER_TICK;i++){
-               sprintf(str,"%d", pixels_per_tick[i]);
+               g_snprintf(str, 5, "%d", pixels_per_tick[i]);
                menu_item=gtk_menu_item_new_with_label(str);
-               io->pixelspertick[i].io=io;
-               io->pixelspertick[i].pixels_per_tick=pixels_per_tick[i];
-               SIGNAL_CONNECT(menu_item, "activate", pixels_per_tick_select, &io->pixelspertick[i]);
+
+               OBJECT_SET_DATA(menu_item, "pixels_per_tick",
+                                pixels_per_tick[i]);
+               SIGNAL_CONNECT(menu_item, "activate", pixels_per_tick_select, io);
                gtk_widget_show(menu_item);
                gtk_menu_append(GTK_MENU(menu), menu_item);
        }
@@ -1108,13 +1322,16 @@ create_pixels_per_tick_menu_items(io_stat_t *io, GtkWidget *menu)
 
 
 static void
-yscale_select(GtkWidget *item _U_, gpointer key)
+yscale_select(GtkWidget *item, gpointer key)
 {
-       io_stat_yscale_t *ys=(io_stat_yscale_t *)key;
+       int val;
+       io_stat_t *io;
+
+       io=(io_stat_t *)key;
+       val=(int)OBJECT_GET_DATA(item, "yscale_max");
 
-       ys->io->max_y_units=ys->yscale;
-       ys->io->needs_redraw=1;
-       gtk_iostat_draw(&ys->io->graphs[0]);
+       io->max_y_units=val;
+       io_stat_redraw(io);
 }
 
 static void 
@@ -1125,20 +1342,20 @@ create_tick_interval_menu_items(io_stat_t *io, GtkWidget *menu)
        int i;
 
        for(i=0;i<MAX_TICK_VALUES;i++){
-               if(max_tick_values[i]>=1000){
-                       sprintf(str,"%d sec", max_tick_values[i]/1000);
-               } else if(max_tick_values[i]>=100){
-                       sprintf(str,"0.%1d sec", (max_tick_values[i]/100)%10);
-               } else if(max_tick_values[i]>=10){
-                       sprintf(str,"0.%02d sec", (max_tick_values[i]/10)%10);
+               if(tick_interval_values[i]>=1000){
+                       g_snprintf(str, 15, "%d sec", tick_interval_values[i]/1000);
+               } else if(tick_interval_values[i]>=100){
+                       g_snprintf(str, 15, "0.%1d sec", (tick_interval_values[i]/100)%10);
+               } else if(tick_interval_values[i]>=10){
+                       g_snprintf(str, 15, "0.%02d sec", (tick_interval_values[i]/10)%10);
                } else {
-                       sprintf(str,"0.%03d sec", (max_tick_values[i])%10);
+                       g_snprintf(str, 15, "0.%03d sec", (tick_interval_values[i])%10);
                }
 
                menu_item=gtk_menu_item_new_with_label(str);
-               io->tick_val[i].io=io;
-               io->tick_val[i].interval=max_tick_values[i];
-               SIGNAL_CONNECT(menu_item, "activate", tick_interval_select, &io->tick_val[i]);
+               OBJECT_SET_DATA(menu_item, "tick_interval",
+                                tick_interval_values[i]);
+               SIGNAL_CONNECT(menu_item, "activate", tick_interval_select, (gpointer)io);
                gtk_widget_show(menu_item);
                gtk_menu_append(GTK_MENU(menu), menu_item);
        }
@@ -1157,12 +1374,11 @@ create_yscale_max_menu_items(io_stat_t *io, GtkWidget *menu)
                if(yscale_max[i]==AUTO_MAX_YSCALE){
                        strcpy(str,"Auto");
                } else {
-                       sprintf(str,"%d", yscale_max[i]);
+                       g_snprintf(str, 15, "%d", yscale_max[i]);
                }
                menu_item=gtk_menu_item_new_with_label(str);
-               io->yscale[i].io=io;
-               io->yscale[i].yscale=yscale_max[i];
-               SIGNAL_CONNECT(menu_item, "activate", yscale_select, &io->yscale[i]);
+               OBJECT_SET_DATA(menu_item, "yscale_max", yscale_max[i]);
+               SIGNAL_CONNECT(menu_item, "activate", yscale_select, io);
                gtk_widget_show(menu_item);
                gtk_menu_append(GTK_MENU(menu), menu_item);
        }
@@ -1170,36 +1386,38 @@ create_yscale_max_menu_items(io_stat_t *io, GtkWidget *menu)
 }
 
 static void
-count_type_select(GtkWidget *item _U_, gpointer key)
+count_type_select(GtkWidget *item, gpointer key)
 {
-       io_stat_count_type_t *ct=(io_stat_count_type_t *)key;
+       int val;
+       io_stat_t *io;
+
+       io=(io_stat_t *)key;
+       val=(int)OBJECT_GET_DATA(item, "count_type");
 
-       ct->io->count_type=ct->count_type;
+       io->count_type=val;
 
-       if(ct->io->count_type==COUNT_TYPE_ADVANCED){
+       if(io->count_type==COUNT_TYPE_ADVANCED){
                int i;
                for(i=0;i<MAX_GRAPHS;i++){
-                       ct->io->graphs[i].display=0;
-                       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ct->io->graphs[i].display_button), ct->io->graphs[i].display);
-                       gtk_widget_show(ct->io->graphs[i].advanced_buttons);
-/* redraw the entire window so teh unhidden widgets show up, hopefully */
+                       disable_graph(&io->graphs[i]);
+                       gtk_widget_show(io->graphs[i].advanced_buttons);
+/* redraw the entire window so the unhidden widgets show up, hopefully */
 {GdkRectangle update_rect;
 update_rect.x=0;
 update_rect.y=0;
-update_rect.width=ct->io->window->allocation.width;
-update_rect.height=ct->io->window->allocation.height;
-gtk_widget_draw(ct->io->window, &update_rect);
+update_rect.width=io->window->allocation.width;
+update_rect.height=io->window->allocation.height;
+gtk_widget_draw(io->window, &update_rect);
 }
                }
        } else {
                int i;
                for(i=0;i<MAX_GRAPHS;i++){
-                       gtk_widget_hide(ct->io->graphs[i].advanced_buttons);
+                       gtk_widget_hide(io->graphs[i].advanced_buttons);
                }
        }
 
-       ct->io->needs_redraw=1;
-       gtk_iostat_draw(&ct->io->graphs[0]);
+       io_stat_redraw(io);
 }
 
 static void 
@@ -1209,10 +1427,9 @@ create_frames_or_bytes_menu_items(io_stat_t *io, GtkWidget *menu)
        int i;
 
        for(i=0;i<MAX_COUNT_TYPES;i++){
-               menu_item=gtk_menu_item_new_with_label(max_count_types[i]);
-               io->counttype[i].io=io;
-               io->counttype[i].count_type=i;
-               SIGNAL_CONNECT(menu_item, "activate", count_type_select, &io->counttype[i]);
+               menu_item=gtk_menu_item_new_with_label(count_type_names[i]);
+               OBJECT_SET_DATA(menu_item, "count_type", i);
+               SIGNAL_CONNECT(menu_item, "activate", count_type_select, io);
                gtk_widget_show(menu_item);
                gtk_menu_append(GTK_MENU(menu), menu_item);
        }
@@ -1247,17 +1464,39 @@ create_ctrl_menu(io_stat_t *io, GtkWidget *box, char *name, void (*func)(io_stat
 static void
 create_ctrl_area(io_stat_t *io, GtkWidget *box)
 {
+    GtkWidget *frame_vbox;
+    GtkWidget *frame;
        GtkWidget *vbox;
 
+       frame_vbox=gtk_vbox_new(FALSE, 0);
+       gtk_container_add(GTK_CONTAINER(box), frame_vbox);
+       gtk_widget_show(frame_vbox);
+
+    frame = gtk_frame_new("X Axis");
+       gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
+       gtk_widget_show(frame);
+
+       vbox=gtk_vbox_new(FALSE, 0);
+       gtk_container_add(GTK_CONTAINER(frame), vbox);
+    gtk_container_border_width(GTK_CONTAINER(vbox), 3);
+       gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
+       gtk_widget_show(vbox);
+
+       create_ctrl_menu(io, vbox, "Tick interval:", create_tick_interval_menu_items);
+       create_ctrl_menu(io, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
+
+    frame = gtk_frame_new("Y Axis");
+       gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
+       gtk_widget_show(frame);
+
        vbox=gtk_vbox_new(FALSE, 0);
-       gtk_container_add(GTK_CONTAINER(box), vbox);
+       gtk_container_add(GTK_CONTAINER(frame), vbox);
+    gtk_container_border_width(GTK_CONTAINER(vbox), 3);
        gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
        gtk_widget_show(vbox);
 
-       create_ctrl_menu(io, vbox, "Unit:", create_frames_or_bytes_menu_items);
-       create_ctrl_menu(io, vbox, "Tick Interval:", create_tick_interval_menu_items);
-       create_ctrl_menu(io, vbox, "Pixels Per Tick:", create_pixels_per_tick_menu_items);
-       create_ctrl_menu(io, vbox, "Y-scale:", create_yscale_max_menu_items);
+    create_ctrl_menu(io, vbox, "Unit:", create_frames_or_bytes_menu_items);
+       create_ctrl_menu(io, vbox, "Scale:", create_yscale_max_menu_items);
 
        return;
 }
@@ -1267,30 +1506,34 @@ static gint
 filter_callback(GtkWidget *widget _U_, io_stat_graph_t *gio)
 {
        char *filter;
-       int i;
+       char *field;
        header_field_info *hfi;
+       dfilter_t *dfilter;
+
+       field=(char *)gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
+
+       /* this graph is not active, just update display and redraw */
+       if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))){
+               disable_graph(gio);
+               io_stat_redraw(gio->io);
+               return 0;
+       }
 
        /* first check if the field string is valid */
        if(gio->io->count_type==COUNT_TYPE_ADVANCED){
-               char *field;
-               field=(char *)gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
                /* warn and bail out if there was no field specified */
                if(field==NULL || field[0]==0){
-                       simple_dialog(ESD_TYPE_WARN, NULL, "'%s' is not a valid field name", field);
-                       gio->display=0;
-                       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
-                       gio->io->needs_redraw=1;
-                       gtk_iostat_draw(gio);
+                       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You did not specify a field name.");
+                       disable_graph(gio);
+                       io_stat_redraw(gio->io);
                        return 0;
                }
                /* warn and bail out if the field could not be found */
                hfi=proto_registrar_get_byname(field);
                if(hfi==NULL){
-                       simple_dialog(ESD_TYPE_WARN, NULL, "'%s' is not a valid field name", field);
-                       gio->display=0;
-                       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
-                       gio->io->needs_redraw=1;
-                       gtk_iostat_draw(gio);
+                       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "'%s' is not a valid field name.", field);
+                       disable_graph(gio);
+                       io_stat_redraw(gio->io);
                        return 0;
                }
                gio->hf_index=hfi->id;
@@ -1304,69 +1547,80 @@ filter_callback(GtkWidget *widget _U_, io_stat_graph_t *gio)
                case FT_INT16:
                case FT_INT24:
                case FT_INT32:
+                       /* these values support all calculations except LOAD */
+                       switch(gio->calc_type){
+                       case CALC_TYPE_LOAD:
+                               simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                                   "LOAD(*) is only supported for relative-time fields.");
+                               disable_graph(gio);
+                               io_stat_redraw(gio->io);
+                               return 0;
+                       }
                        /* these types support all calculations */
                        break;
                case FT_RELATIVE_TIME:
-                       /* this type only support COUNT, MAX, MIN, AVG */
+                       /* this type only supports COUNT, MAX, MIN, AVG */
                        switch(gio->calc_type){
                        case CALC_TYPE_COUNT:
                        case CALC_TYPE_MAX:
                        case CALC_TYPE_MIN:
                        case CALC_TYPE_AVG:
+                       case CALC_TYPE_LOAD:
                                break;
                        default:
-                               simple_dialog(ESD_TYPE_WARN, NULL, "%s is not supported for %s's type", max_calc_types[gio->calc_type], field);
-                               gio->display=0;
-                               gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
-                               gio->io->needs_redraw=1;
-                               gtk_iostat_draw(gio);
+                               simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                                   "%s is a relative-time field, so %s calculations are not supported on it.",
+                                   field,
+                                   calc_type_names[gio->calc_type]);
+                               disable_graph(gio);
+                               io_stat_redraw(gio->io);
+                               return 0;
+                       }
+                       break;
+               case FT_UINT64:
+               case FT_INT64:
+                       /*
+                        * XXX - support this if gint64/guint64 are
+                        * available?
+                        */
+                       if(gio->calc_type!=CALC_TYPE_COUNT){
+                               simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                                   "%s is a 64-bit integer, so %s calculations are not supported on it.",
+                                   field,
+                                   calc_type_names[gio->calc_type]);
+                               disable_graph(gio);
+                               io_stat_redraw(gio->io);
                                return 0;
                        }
                        break;
                default:
+                       /*
+                        * XXX - support all operations on floating-point
+                        * numbers?
+                        */
                        if(gio->calc_type!=CALC_TYPE_COUNT){
-                               simple_dialog(ESD_TYPE_WARN, NULL, "Stat calculations is not supported for %s's type", field);
-                               gio->display=0;
-                               gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
-                               gio->io->needs_redraw=1;
-                               gtk_iostat_draw(gio);
+                               simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                                   "%s doesn't have integral values, so %s calculations are not supported on it.",
+                                   field,
+                                   calc_type_names[gio->calc_type]);
+                               disable_graph(gio);
+                               io_stat_redraw(gio->io);
                                return 0;
                        }
+                       break;
                }
-
-
-       }
-
-       /* first check if the filter string is valid.  Do this by just trying
-          to register, deregister a dummy listener. */
-       filter=(char *)gtk_entry_get_text(GTK_ENTRY(gio->filter_button));
-       if(register_tap_listener("frame", &filter, filter, NULL, NULL, NULL)){
-               simple_dialog(ESD_TYPE_WARN, NULL, "%s is not a valid filter string", filter);
-               if(gio->display){
-                       gio->display=0;
-                       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
-               }
-               protect_thread_critical_region();
-               remove_tap_listener(gio);
-               unprotect_thread_critical_region();
-
-               gio->io->needs_redraw=1;
-               gtk_iostat_draw(gio);
-
-               return 0;
        }
-       /* just remove the dummy again */
-       protect_thread_critical_region();
-       remove_tap_listener(&filter);
-       unprotect_thread_critical_region();
 
-       /* this graph is not active, just update display and redraw */
-       if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))){
-               gio->display=0;
-               gio->io->needs_redraw=1;
-               gtk_iostat_draw(gio);
+       /* first check if the filter string is valid. */
+       filter=(char *)gtk_entry_get_text(GTK_ENTRY(gio->filter_field));
+       if(!dfilter_compile(filter, &dfilter)) {
+               bad_dfilter_alert_box(filter);
+               disable_graph(gio);
+               io_stat_redraw(gio->io);
                return 0;
        }
+       if (dfilter != NULL)
+               dfilter_free(dfilter);
 
        /* ok, we have a valid filter and the graph is active.
           first just try to delete any previous settings and then apply
@@ -1376,16 +1630,10 @@ filter_callback(GtkWidget *widget _U_, io_stat_graph_t *gio)
        remove_tap_listener(gio);
        unprotect_thread_critical_region();
        
-       gio->display=1;
-
-       /* only register the draw routine for the first gio. */
-       for(i=0;i<MAX_GRAPHS;i++){
-               gtk_iostat_reset(&gio->io->graphs[i]);
-       }
-       register_tap_listener("frame", gio, filter, gtk_iostat_reset, gtk_iostat_packet, gtk_iostat_draw);
-       redissect_packets(&cfile);
-       gio->io->needs_redraw=1;
-       gtk_iostat_draw(gio);
+       io_stat_reset(gio->io);
+       enable_graph(gio, filter, field);
+       retap_packets(&cfile);
+       io_stat_redraw(gio->io);
 
        return 0;
 }
@@ -1399,11 +1647,8 @@ calc_type_select(GtkWidget *item _U_, gpointer key)
        ct->gio->calc_type=ct->calc_type;
 
        /* disable the graph */
-       ct->gio->display=0;
-       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ct->gio->display_button), ct->gio->display);
-
-       ct->gio->io->needs_redraw=1;
-       gtk_iostat_draw(&ct->gio->io->graphs[0]);
+       disable_graph(ct->gio);
+       io_stat_redraw(ct->gio->io);
 }
 
 
@@ -1416,7 +1661,7 @@ create_calc_types_menu_items(io_stat_graph_t *gio, GtkWidget *menu)
        for(i=0;i<MAX_CALC_TYPES;i++){
                gio->calc_types[i].gio=gio;
                gio->calc_types[i].calc_type=i;
-               menu_item=gtk_menu_item_new_with_label(max_calc_types[i]);
+               menu_item=gtk_menu_item_new_with_label(calc_type_names[i]);
                SIGNAL_CONNECT(menu_item, "activate", calc_type_select, &gio->calc_types[i]);
                gtk_widget_show(menu_item);
                gtk_menu_append(GTK_MENU(menu), menu_item);
@@ -1450,18 +1695,6 @@ create_advanced_menu(io_stat_graph_t *gio, GtkWidget *box, char *name, void (*fu
        gtk_widget_show(option_menu);
 }
 
-
-static gint
-field_callback(GtkWidget *widget _U_, io_stat_graph_t *gio)
-{
-       gio->display=0;
-       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
-       gio->io->needs_redraw=1;
-       gtk_iostat_draw(gio);
-       return 0;
-}
-
-
 static void
 create_advanced_field(io_stat_graph_t *gio, GtkWidget *box)
 {
@@ -1469,7 +1702,7 @@ create_advanced_field(io_stat_graph_t *gio, GtkWidget *box)
        gio->calc_field=gtk_entry_new_with_max_length(30);
        gtk_box_pack_start(GTK_BOX(box), gio->calc_field, FALSE, FALSE, 0);
        gtk_widget_show(gio->calc_field);
-       SIGNAL_CONNECT(gio->filter_button, "activate", field_callback, gio);
+       SIGNAL_CONNECT(gio->calc_field, "activate", filter_callback, gio);
 }
 
 
@@ -1484,6 +1717,7 @@ create_advanced_box(io_stat_graph_t *gio, GtkWidget *box)
        gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
        gtk_widget_hide(hbox);
 
+       gio->calc_type=CALC_TYPE_SUM;
        create_advanced_menu(gio, hbox, "Calc:", create_calc_types_menu_items);
        create_advanced_field(gio, hbox);
 }
@@ -1501,71 +1735,59 @@ filter_button_clicked(GtkWidget *w, gpointer uio)
 static void
 create_filter_box(io_stat_graph_t *gio, GtkWidget *box, int num)
 {
+       GtkWidget *option_menu;
+       GtkWidget *menu;
+       GtkWidget *menu_item;
        GtkWidget *hbox;
        GtkWidget *label;
         char str[256];
+       int i;
 
-       hbox=gtk_hbox_new(FALSE, 0);
+       hbox=gtk_hbox_new(FALSE, 3);
        gtk_container_add(GTK_CONTAINER(box), hbox);
        gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
        gtk_widget_show(hbox);
 
-
-       sprintf(str, "Filter:%d", num);
-       label=gtk_label_new(str);
-       gtk_widget_show(label);
-       gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-
-       gio->display_button=gtk_toggle_button_new();
+       g_snprintf(str, 256, "Graph %d", num);
+    gio->display_button=gtk_toggle_button_new_with_label(str);
        gtk_box_pack_start(GTK_BOX(hbox), gio->display_button, FALSE, FALSE, 0);
        gtk_widget_show(gio->display_button);
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
        SIGNAL_CONNECT(gio->display_button, "toggled", filter_callback, gio);
 
-
-
-       label=gtk_label_new("   Color:");
+       label=gtk_label_new("Color");
        gtk_widget_show(label);
        gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
 
-       gio->color_button=gtk_toggle_button_new();
-       gtk_box_pack_start(GTK_BOX(hbox), gio->color_button, FALSE, FALSE, 0);
-       gtk_widget_show(gio->color_button);
-
 #if GTK_MAJOR_VERSION < 2
+    /* setting the color of the display button doesn't work */
        rc_style = gtk_rc_style_new ();
-       rc_style->bg[GTK_STATE_NORMAL] = gio->color;
-       rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_BG;
-       rc_style->bg[GTK_STATE_ACTIVE] = gio->color;
-       rc_style->color_flags[GTK_STATE_ACTIVE] |= GTK_RC_BG;
-       rc_style->bg[GTK_STATE_PRELIGHT] = gio->color;
-       rc_style->color_flags[GTK_STATE_PRELIGHT] |= GTK_RC_BG;
-       rc_style->bg[GTK_STATE_SELECTED] = gio->color;
-       rc_style->color_flags[GTK_STATE_SELECTED] |= GTK_RC_BG;
-       rc_style->bg[GTK_STATE_INSENSITIVE] = gio->color;
-       rc_style->color_flags[GTK_STATE_INSENSITIVE] |= GTK_RC_BG;
-       gtk_widget_modify_style (gio->color_button, rc_style);
+       rc_style->fg[GTK_STATE_NORMAL] = gio->color;
+       rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG;
+       rc_style->fg[GTK_STATE_ACTIVE] = gio->color;
+       rc_style->color_flags[GTK_STATE_ACTIVE] |= GTK_RC_FG;
+       rc_style->fg[GTK_STATE_PRELIGHT] = gio->color;
+       rc_style->color_flags[GTK_STATE_PRELIGHT] |= GTK_RC_FG;
+       rc_style->fg[GTK_STATE_SELECTED] = gio->color;
+       rc_style->color_flags[GTK_STATE_SELECTED] |= GTK_RC_FG;
+       rc_style->fg[GTK_STATE_INSENSITIVE] = gio->color;
+       rc_style->color_flags[GTK_STATE_INSENSITIVE] |= GTK_RC_FG;
+       gtk_widget_modify_style (label, rc_style);
        gtk_rc_style_unref (rc_style);
 #else
-       gtk_widget_modify_bg(gio->color_button, GTK_STATE_NORMAL, &gio->color); 
-       gtk_widget_modify_bg(gio->color_button, GTK_STATE_ACTIVE, &gio->color); 
-       gtk_widget_modify_bg(gio->color_button, GTK_STATE_PRELIGHT, &gio->color); 
-       gtk_widget_modify_bg(gio->color_button, GTK_STATE_SELECTED, &gio->color); 
-       gtk_widget_modify_bg(gio->color_button, GTK_STATE_INSENSITIVE, &gio->color); 
+       gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gio->color); 
+       gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &gio->color); 
+       gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &gio->color); 
+       gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &gio->color); 
+       gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &gio->color); 
 #endif
 /*     gtk_signal_connect(GTK_OBJECT(gio->display_button), "toggled", GTK_SIGNAL_FUNC(filter_callback), gio);*/
 
 
-       /* filter prefs dialog --- comment out static label */
-       /* label=gtk_label_new("   Filter:"); */
-
        /* filter prefs dialog */
-       label=gtk_label_new("   ");
-       gtk_widget_show(label);
-       gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-       gio->filter_bt = gtk_button_new_with_label("Filter:");
+       gio->filter_bt=BUTTON_NEW_FROM_STOCK(ETHEREAL_STOCK_DISPLAY_FILTER_ENTRY);
 
-       sprintf(str, "Ethereal: Display Filter  IO-Stat (Filter:%d)", num);
+       g_snprintf(str, 256, "Ethereal: Display Filter  IO-Stat (Filter:%d)", num);
        if(gio->args->title){
                free(gio->args->title);
        }
@@ -1577,29 +1799,61 @@ create_filter_box(io_stat_graph_t *gio, GtkWidget *box, int num)
        gtk_box_pack_start(GTK_BOX(hbox), gio->filter_bt, FALSE, TRUE, 0);
        gtk_widget_show(gio->filter_bt);
 
-       gio->filter_button=gtk_entry_new_with_max_length(256);
+       gio->filter_field=gtk_entry_new_with_max_length(256);
 
        /* filter prefs dialog */
-       OBJECT_SET_DATA(gio->filter_bt, E_FILT_TE_PTR_KEY, gio->filter_button);
+       OBJECT_SET_DATA(gio->filter_bt, E_FILT_TE_PTR_KEY, gio->filter_field);
        /* filter prefs dialog */
 
-       gtk_box_pack_start(GTK_BOX(hbox), gio->filter_button, FALSE, FALSE, 0);
-       gtk_widget_show(gio->filter_button);
-       SIGNAL_CONNECT(gio->filter_button, "activate", filter_callback, gio);
+       gtk_box_pack_start(GTK_BOX(hbox), gio->filter_field, FALSE, FALSE, 0);
+       gtk_widget_show(gio->filter_field);
+       SIGNAL_CONNECT(gio->filter_field, "activate", filter_callback, gio);
+    SIGNAL_CONNECT(gio->filter_field, "changed", filter_te_syntax_check_cb, NULL);
 
        create_advanced_box(gio, hbox);
 
+
+       /*
+        * create PlotStyle menu
+        */
+       g_snprintf(str, 256, " Style:");
+       label=gtk_label_new(str);
+       gtk_widget_show(label);
+       gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+       option_menu=gtk_option_menu_new();
+       menu=gtk_menu_new();
+       for(i=0;i<MAX_PLOT_STYLES;i++){
+               menu_item=gtk_menu_item_new_with_label(plot_style_name[i]);
+               OBJECT_SET_DATA(menu_item, "plot_style", i);
+               SIGNAL_CONNECT(menu_item, "activate", plot_style_select, &gio->io->graphs[num-1]);
+               gtk_widget_show(menu_item);
+               gtk_menu_append(GTK_MENU(menu), menu_item);
+       }
+       gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PLOT_STYLE);
+
+       gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
+       gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
+       gtk_widget_show(option_menu);
+
+
        return;
 }
 
 static void
 create_filter_area(io_stat_t *io, GtkWidget *box)
 {
+       GtkWidget *frame;
        GtkWidget *vbox;
        int i;
 
-       vbox=gtk_vbox_new(FALSE, 0);
-       gtk_container_add(GTK_CONTAINER(box), vbox);
+    frame=gtk_frame_new("Graphs");
+       gtk_container_add(GTK_CONTAINER(box), frame);
+       gtk_widget_show(frame);
+
+       vbox=gtk_vbox_new(FALSE, 1);
+       gtk_container_add(GTK_CONTAINER(frame), vbox);
+    gtk_container_border_width(GTK_CONTAINER(vbox), 3);
        gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
        gtk_widget_show(vbox);
 
@@ -1616,11 +1870,10 @@ init_io_stat_window(io_stat_t *io)
 {
        GtkWidget *vbox;
        GtkWidget *hbox;
+    GtkWidget *bt_close;
 
        /* create the main window */
-       io->window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
-
-       gtk_widget_set_name(io->window, "I/O Statistics");
+       io->window=window_new(GTK_WINDOW_TOPLEVEL, "I/O Graphs");
 
        vbox=gtk_vbox_new(FALSE, 0);
        gtk_container_add(GTK_CONTAINER(io->window), vbox);
@@ -1628,16 +1881,28 @@ init_io_stat_window(io_stat_t *io)
 
        create_draw_area(io, vbox);
 
-       hbox=gtk_hbox_new(FALSE, 0);
+       hbox=gtk_hbox_new(FALSE, 3);
        gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
+    gtk_container_border_width(GTK_CONTAINER(hbox), 3);
        gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
        gtk_widget_show(hbox);
 
        create_filter_area(io, hbox);
        create_ctrl_area(io, hbox);
 
-       gtk_widget_show(io->window);
-       gtk_window_set_title(GTK_WINDOW(io->window),"IO-Stat");
+       io_stat_set_title(io);
+
+    hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+       gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+    gtk_widget_show(hbox);
+
+    bt_close = OBJECT_GET_DATA(hbox, GTK_STOCK_CLOSE);
+    window_set_cancel_button(io->window, bt_close, window_cancel_button_cb);
+
+    SIGNAL_CONNECT(io->window, "delete_event", window_delete_event_cb, NULL);
+
+    gtk_widget_show(io->window);
+    window_present(io->window);
 }
 
 
@@ -1653,13 +1918,8 @@ gtk_iostat_cb(GtkWidget *w _U_, gpointer d _U_)
 void
 register_tap_listener_gtk_iostat(void)
 {
-       io_stat_widget_table = g_hash_table_new(io_stat_widget_hash,
-                       io_stat_widget_equal);
        register_ethereal_tap("io,stat", gtk_iostat_init);
-}
 
-void
-register_tap_menu_gtkiostat(void)
-{
-       register_tap_menu_item("IO/IO-Stat", gtk_iostat_cb);
+       register_tap_menu_item("_IO Graphs", REGISTER_TAP_GROUP_GENERIC,
+        gtk_iostat_cb, NULL, NULL, NULL);
 }