Fix almost all accesses to ->window
[obnox/wireshark/wip.git] / gtk / dcerpc_stat.c
index d949db5756a9918d4e113a64d233d18fb75aa43b..9647a5bfa009a573569420fbbdd2ca2f2e77eb14 100644 (file)
@@ -1,30 +1,30 @@
 /* dcerpc_stat.c
  * dcerpc_stat   2002 Ronnie Sahlberg
  *
- * $Id: dcerpc_stat.c,v 1.22 2003/09/19 07:24:37 guy Exp $
+ * $Id$
  *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@ethereal.com>
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
  * Copyright 1998 Gerald Combs
- * 
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
  * of the License, or (at your option) any later version.
- * 
+ *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
-/* This module provides rpc call/reply SRT statistics to ethereal,
+/* This module provides rpc call/reply SRT statistics to Wireshark,
  * and displays them graphically.
- * It is only used by ethereal and not tethereal
+ * It is only used by Wireshark and not tshark
  *
  * It serves as an example on how to use the tap api.
  */
 # include "config.h"
 #endif
 
+#include <stdio.h>
 #include <string.h>
+
 #include <gtk/gtk.h>
-#include "menu.h"
-#include "epan/packet_info.h"
-#include "simple_dialog.h"
-#include "dlg_utils.h"
-#include "ui_util.h"
-#include "tap.h"
-#include "../register.h"
-#include "packet-dcerpc.h"
-#include "../globals.h"
-#include "compat_macros.h"
-#include "service_response_time_table.h"
 
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-dcerpc.h>
+
+#include "../stat_menu.h"
+#include "../simple_dialog.h"
+
+#include "gtk/gui_stat_menu.h"
+#include "gtk/dlg_utils.h"
+#include "gtk/gui_utils.h"
+#include "gtk/filter_dlg.h"
+#include "gtk/stock_icons.h"
+#include "gtk/service_response_time_table.h"
+#include "gtk/gtkglobals.h"
+#include "gtk/main.h"
+#include "gtk/filter_autocomplete.h"
 
-extern GtkWidget   *main_display_filter_widget;
+#include "gtk/old-gtk-compat.h"
 
 /* used to keep track of the statistics for an entire program interface */
-typedef struct _rpcstat_t {
+typedef struct _dcerpcstat_t {
        GtkWidget *win;
        srt_stat_table srt_table;
-       char *prog;
+       const char *prog;
        e_uuid_t uuid;
        guint16 ver;
        int num_procedures;
-} rpcstat_t;
+} dcerpcstat_t;
 
 
-static int
+static gboolean
 uuid_equal(e_uuid_t *uuid1, e_uuid_t *uuid2)
 {
-       if( (uuid1->Data1!=uuid2->Data1)
-         ||(uuid1->Data2!=uuid2->Data2)
-         ||(uuid1->Data3!=uuid2->Data3)
-         ||(uuid1->Data4[0]!=uuid2->Data4[0])
-         ||(uuid1->Data4[1]!=uuid2->Data4[1])
-         ||(uuid1->Data4[2]!=uuid2->Data4[2])
-         ||(uuid1->Data4[3]!=uuid2->Data4[3])
-         ||(uuid1->Data4[4]!=uuid2->Data4[4])
-         ||(uuid1->Data4[5]!=uuid2->Data4[5])
-         ||(uuid1->Data4[6]!=uuid2->Data4[6])
-         ||(uuid1->Data4[7]!=uuid2->Data4[7]) ){
-               return 0;
+       if( (uuid1->Data1    != uuid2->Data1)
+         ||(uuid1->Data2    != uuid2->Data2)
+         ||(uuid1->Data3    != uuid2->Data3)
+         ||(uuid1->Data4[0] != uuid2->Data4[0])
+         ||(uuid1->Data4[1] != uuid2->Data4[1])
+         ||(uuid1->Data4[2] != uuid2->Data4[2])
+         ||(uuid1->Data4[3] != uuid2->Data4[3])
+         ||(uuid1->Data4[4] != uuid2->Data4[4])
+         ||(uuid1->Data4[5] != uuid2->Data4[5])
+         ||(uuid1->Data4[6] != uuid2->Data4[6])
+         ||(uuid1->Data4[7] != uuid2->Data4[7]) ){
+               return FALSE;
        }
-       return 1;
+       return TRUE;
+}
+
+static char *
+dcerpcstat_gen_title(dcerpcstat_t *rs)
+{
+       char *title;
+
+       title = g_strdup_printf("DCE-RPC Service Response Time statistics for %s major version %u: %s", rs->prog, rs->ver, cf_get_display_name(&cfile));
+       return title;
 }
-       
 
 static void
-dcerpcstat_reset(rpcstat_t *rs)
+dcerpcstat_set_title(dcerpcstat_t *rs)
 {
-       char title_string[256];
-       reset_srt_table_data(&rs->srt_table);
+       char *title;
 
-       snprintf(title_string, 255, "DCE-RPC Service Response Time statistics for %s version %d.%d : %s", rs->prog, rs->ver&0xff, rs->ver>>8, cf_get_display_name(&cfile));
-       gtk_window_set_title(GTK_WINDOW(rs->win), title_string);
+       title = dcerpcstat_gen_title(rs);
+       gtk_window_set_title(GTK_WINDOW(rs->win), title);
+       g_free(title);
+}
+
+static void
+dcerpcstat_reset(void *rs_arg)
+{
+       dcerpcstat_t *rs = rs_arg;
+
+       reset_srt_table_data(&rs->srt_table);
+       dcerpcstat_set_title(rs);
 }
 
 
-static int
-dcerpcstat_packet(rpcstat_t *rs, packet_info *pinfo, epan_dissect_t *edt _U_, dcerpc_info *ri)
+static gboolean
+dcerpcstat_packet(void *rs_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *ri_arg)
 {
+       dcerpcstat_t *rs = rs_arg;
+       const dcerpc_info *ri = ri_arg;
+
        if(!ri->call_data){
-               return 0;
+               return FALSE;
        }
        if(!ri->call_data->req_frame){
-               /* we have not seen the request so we dont know the delta*/
-               return 0;
+               /* we have not seen the request so we don't know the delta*/
+               return FALSE;
        }
-       if(ri->call_data->opnum>=rs->num_procedures){
-               /* dont handle this since its outside of known table */
-               return 0;
+       if(ri->call_data->opnum >= rs->num_procedures){
+               /* don't handle this since its outside of known table */
+               return FALSE;
        }
 
        /* we are only interested in reply packets */
-       if(ri->request){
-               return 0;
+       if(ri->ptype != PDU_RESP){
+               return FALSE;
        }
 
        /* we are only interested in certain program/versions */
        if( (!uuid_equal( (&ri->call_data->uuid), (&rs->uuid)))
-         ||(ri->call_data->ver!=rs->ver)){
-               return 0;
+         ||(ri->call_data->ver != rs->ver)){
+               return FALSE;
        }
 
 
        add_srt_table_data(&rs->srt_table, ri->call_data->opnum, &ri->call_data->req_time, pinfo);
 
 
-       return 1;
+       return TRUE;
 }
 
 static void
-dcerpcstat_draw(rpcstat_t *rs)
+dcerpcstat_draw(void *rs_arg)
 {
+       dcerpcstat_t *rs = rs_arg;
+
        draw_srt_table_data(&rs->srt_table);
 }
 
@@ -138,12 +169,10 @@ dcerpcstat_draw(rpcstat_t *rs)
  *
  * there should not be any other critical regions in gtk2
  */
-void protect_thread_critical_region(void);
-void unprotect_thread_critical_region(void);
 static void
 win_destroy_cb(GtkWindow *win _U_, gpointer data)
 {
-       rpcstat_t *rs=(rpcstat_t *)data;
+       dcerpcstat_t *rs = (dcerpcstat_t *)data;
 
        protect_thread_critical_region();
        remove_tap_listener(rs);
@@ -158,97 +187,133 @@ win_destroy_cb(GtkWindow *win _U_, gpointer data)
 /* When called, this function will create a new instance of gtk-dcerpcstat.
  */
 static void
-gtk_dcerpcstat_init(char *optarg)
+gtk_dcerpcstat_init(const char *optarg, void* userdata _U_)
 {
-       rpcstat_t *rs;
+       dcerpcstat_t *rs;
        guint32 i, max_procs;
-       char title_string[256];
-       char filter_string[256];
+       char *title_string;
+       char *filter_string;
        GtkWidget *vbox;
        GtkWidget *stat_label;
        GtkWidget *filter_label;
+       GtkWidget *bbox;
+       GtkWidget *close_bt;
        dcerpc_sub_dissector *procs;
        e_uuid_t uuid;
-       int d1,d2,d3,d40,d41,d42,d43,d44,d45,d46,d47;
+       guint d1,d2,d3,d40,d41,d42,d43,d44,d45,d46,d47;
        int major, minor;
-       int pos=0;
-        char *filter=NULL;
-        GString *error_string;
-
-       if(sscanf(optarg,"dcerpc,srt,%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x,%d.%d,%n", &d1,&d2,&d3,&d40,&d41,&d42,&d43,&d44,&d45,&d46,&d47,&major,&minor,&pos)==13){
-               uuid.Data1=d1;
-               uuid.Data2=d2;
-               uuid.Data3=d3;
-               uuid.Data4[0]=d40;
-               uuid.Data4[1]=d41;
-               uuid.Data4[2]=d42;
-               uuid.Data4[3]=d43;
-               uuid.Data4[4]=d44;
-               uuid.Data4[5]=d45;
-               uuid.Data4[6]=d46;
-               uuid.Data4[7]=d47;
-               if(pos){
-                       filter=optarg+pos;
+       guint16 ver;
+       int pos = 0;
+       const char *filter = NULL;
+       GString *error_string;
+       int hf_opnum;
+
+       /*
+        * XXX - DCE RPC statistics are maintained only by major version,
+        * not by major and minor version, so the minor version number is
+        * ignored.
+        *
+        * Should we just stop supporting minor version numbers here?
+        * Or should we allow it to be omitted?  Or should we keep
+        * separate statistics for different minor version numbers,
+        * and allow the minor version number to be omitted, and
+        * report aggregate statistics for all minor version numbers
+        * if it's omitted?
+        */
+       if(sscanf(
+                  optarg,
+                  "dcerpc,srt,%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x,%d.%d,%n",
+                  &d1,&d2,&d3,&d40,&d41,&d42,&d43,&d44,&d45,&d46,&d47,&major,&minor,&pos)
+          == 13) {
+               uuid.Data1    = d1;
+               uuid.Data2    = d2;
+               uuid.Data3    = d3;
+               uuid.Data4[0] = d40;
+               uuid.Data4[1] = d41;
+               uuid.Data4[2] = d42;
+               uuid.Data4[3] = d43;
+               uuid.Data4[4] = d44;
+               uuid.Data4[5] = d45;
+               uuid.Data4[6] = d46;
+               uuid.Data4[7] = d47;
+               if(pos) {
+                       filter = optarg+pos;
                } else {
-                       filter=NULL;
+                       filter = NULL;
                }
        } else {
-               fprintf(stderr, "ethereal: invalid \"-z dcerpc,srt,<uuid>,<major version>.<minor version>[,<filter>]\" argument\n");
+               fprintf(stderr, "wireshark: invalid \"-z dcerpc,srt,<uuid>,<major version>.<minor version>[,<filter>]\" argument\n");
                exit(1);
        }
+       if ((major < 0) || (major > 65535)) {
+               fprintf(stderr,"wireshark: dcerpcstat_init() Major version number %d is invalid - must be positive and <= 65535\n", major);
+               exit(1);
+       }
+       if ((minor < 0) || (minor > 65535)) {
+               fprintf(stderr,"wireshark: dcerpcstat_init() Minor version number %d is invalid - must be positive and <= 65535\n", minor);
+               exit(1);
+       }
+       ver = major;
 
-
-       rs=g_malloc(sizeof(rpcstat_t));
-       rs->prog=dcerpc_get_proto_name(&uuid, (minor<<8)|(major&0xff) );
+       rs = g_malloc(sizeof(dcerpcstat_t));
+       rs->prog = dcerpc_get_proto_name(&uuid, ver);
        if(!rs->prog){
                g_free(rs);
-               fprintf(stderr,"ethereal: dcerpcstat_init() Protocol with uuid:%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x v%d.%d not supported\n",uuid.Data1,uuid.Data2,uuid.Data3,uuid.Data4[0],uuid.Data4[1],uuid.Data4[2],uuid.Data4[3],uuid.Data4[4],uuid.Data4[5],uuid.Data4[6],uuid.Data4[7],major,minor);
+               fprintf(stderr,
+                       "wireshark: dcerpcstat_init() Protocol with uuid:%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x v%u not supported\n",
+                       uuid.Data1,uuid.Data2,uuid.Data3,uuid.Data4[0],uuid.Data4[1],uuid.Data4[2],uuid.Data4[3],uuid.Data4[4],uuid.Data4[5],uuid.Data4[6],uuid.Data4[7],ver);
                exit(1);
        }
-       procs=dcerpc_get_proto_sub_dissector(&uuid, (minor<<8)|(major&0xff) );
-       rs->uuid=uuid;
-       rs->ver=(minor<<8)|(major&0xff);
+       hf_opnum = dcerpc_get_proto_hf_opnum(&uuid, ver);
+       procs    = dcerpc_get_proto_sub_dissector(&uuid, ver);
+       rs->uuid = uuid;
+       rs->ver  = ver;
 
-       rs->win=gtk_window_new(GTK_WINDOW_TOPLEVEL);
+       rs->win  = dlg_window_new("dcerpc-stat");  /* transient_for top_level */
+       gtk_window_set_destroy_with_parent(GTK_WINDOW(rs->win), TRUE);
+
+       dcerpcstat_set_title(rs);
        gtk_window_set_default_size(GTK_WINDOW(rs->win), 550, 400);
-       snprintf(title_string, 255, "DCE-RPC Service Response Time statistics for %s version %d.%d : %s", rs->prog, rs->ver&0xff, rs->ver>>8, cf_get_display_name(&cfile));
-       gtk_window_set_title(GTK_WINDOW(rs->win), title_string);
-       SIGNAL_CONNECT(rs->win, "destroy", win_destroy_cb, rs);
 
-       vbox=gtk_vbox_new(FALSE, 0);
+       vbox = gtk_vbox_new(FALSE, 3);
        gtk_container_add(GTK_CONTAINER(rs->win), vbox);
-       gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
-       gtk_widget_show(vbox);
+       gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
 
-       stat_label=gtk_label_new(title_string);
+       title_string = dcerpcstat_gen_title(rs);
+       stat_label   = gtk_label_new(title_string);
+       g_free(title_string);
        gtk_box_pack_start(GTK_BOX(vbox), stat_label, FALSE, FALSE, 0);
-       gtk_widget_show(stat_label);
 
-       snprintf(filter_string,255,"Filter:%s",filter?filter:"");
-       filter_label=gtk_label_new(filter_string);
+       filter_string = g_strdup_printf("Filter: %s",filter ? filter : "");
+       filter_label  = gtk_label_new(filter_string);
+       g_free(filter_string);
+       gtk_label_set_line_wrap(GTK_LABEL(filter_label), TRUE);
        gtk_box_pack_start(GTK_BOX(vbox), filter_label, FALSE, FALSE, 0);
-       gtk_widget_show(filter_label);
 
        for(i=0,max_procs=0;procs[i].name;i++){
                if(procs[i].num>max_procs){
-                       max_procs=procs[i].num;
+                       max_procs = procs[i].num;
                }
        }
-       rs->num_procedures=max_procs+1;
+       rs->num_procedures = max_procs+1;
 
        /* We must display TOP LEVEL Widget before calling init_srt_table() */
-       gtk_widget_show(rs->win);
+       gtk_widget_show_all(rs->win);
 
-       init_srt_table(&rs->srt_table, max_procs+1, vbox, NULL);
+       if(hf_opnum != -1){
+               init_srt_table(&rs->srt_table, max_procs+1, vbox, proto_registrar_get_nth(hf_opnum)->abbrev);
+       } else {
+               init_srt_table(&rs->srt_table, max_procs+1, vbox, NULL);
+       }
 
-               for(i=0;i<(max_procs+1);i++){
+       for(i=0;i<(max_procs+1);i++){
                int j;
-               char *proc_name;
+               const char *proc_name;
 
-               proc_name="unknown";
+               proc_name = "unknown";
                for(j=0;procs[j].name;j++){
-                       if(procs[j].num==i){
-                               proc_name=procs[j].name;
+                       if (procs[j].num == i){
+                               proc_name = procs[j].name;
                        }
                }
 
@@ -256,195 +321,227 @@ gtk_dcerpcstat_init(char *optarg)
        }
 
 
-       error_string=register_tap_listener("dcerpc", rs, filter, (void*)dcerpcstat_reset, (void*)dcerpcstat_packet, (void*)dcerpcstat_draw);
+       error_string = register_tap_listener("dcerpc", rs, filter, 0, dcerpcstat_reset, dcerpcstat_packet, dcerpcstat_draw);
        if(error_string){
                /* error, we failed to attach to the tap. clean up */
-               simple_dialog(ESD_TYPE_WARN, NULL, error_string->str);
+               simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
                g_string_free(error_string, TRUE);
                free_srt_table_data(&rs->srt_table);
                g_free(rs);
                return;
        }
 
+       /* Button row. */
+       bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+       gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+       close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+       window_set_cancel_button(rs->win, close_bt, window_cancel_button_cb);
+
+       g_signal_connect(rs->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+       g_signal_connect(rs->win, "destroy",      G_CALLBACK(win_destroy_cb), rs);
 
        gtk_widget_show_all(rs->win);
-       redissect_packets(&cfile);
-}
+       window_present(rs->win);
 
+       cf_retap_packets(&cfile);
+       gdk_window_raise(gtk_widget_get_window(rs->win));
+}
 
 
-static e_uuid_t *dcerpc_uuid_program=NULL;
-static guint16 dcerpc_version;
-static GtkWidget *dlg=NULL, *dlg_box;
-static GtkWidget *prog_box;
-static GtkWidget *prog_label, *prog_opt, *prog_menu;
-static GtkWidget *vers_label, *vers_opt, *vers_menu;
-static GtkWidget *filter_box;
-static GtkWidget *filter_label, *filter_entry;
-static GtkWidget *start_button;
-static dcerpc_uuid_key *current_uuid_key=NULL;
-static dcerpc_uuid_value *current_uuid_value=NULL;
-static dcerpc_uuid_key *new_uuid_key=NULL;
-static dcerpc_uuid_value *new_uuid_value=NULL;
 
+static e_uuid_t          *dcerpc_uuid_program;
+static guint16            dcerpc_version;
+static GtkWidget         *dlg = NULL;
+static GtkWidget         *filter_entry;
+static dcerpc_uuid_key   *current_uuid_key;
+static dcerpc_uuid_value *current_uuid_value;
+static dcerpc_uuid_key   *new_uuid_key;
+static dcerpc_uuid_value *new_uuid_value;
 
 static void
 dcerpcstat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
 {
-       char *filter;
-       char str[256];
+       GString *str;
+       const char *filter;
 
-       filter=(char *)gtk_entry_get_text(GTK_ENTRY(filter_entry));
-       if(filter[0]==0){
-               sprintf(str, "dcerpc,srt,%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x,%d.%d",dcerpc_uuid_program->Data1,dcerpc_uuid_program->Data2,dcerpc_uuid_program->Data3,dcerpc_uuid_program->Data4[0],dcerpc_uuid_program->Data4[1],dcerpc_uuid_program->Data4[2],dcerpc_uuid_program->Data4[3],dcerpc_uuid_program->Data4[4],dcerpc_uuid_program->Data4[5],dcerpc_uuid_program->Data4[6],dcerpc_uuid_program->Data4[7],dcerpc_version&0xff,dcerpc_version>>8);
-
-       } else {
-               sprintf(str, "dcerpc,srt,%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x,%d.%d,%s",dcerpc_uuid_program->Data1,dcerpc_uuid_program->Data2,dcerpc_uuid_program->Data3,dcerpc_uuid_program->Data4[0],dcerpc_uuid_program->Data4[1],dcerpc_uuid_program->Data4[2],dcerpc_uuid_program->Data4[3],dcerpc_uuid_program->Data4[4],dcerpc_uuid_program->Data4[5],dcerpc_uuid_program->Data4[6],dcerpc_uuid_program->Data4[7],dcerpc_version&0xff,dcerpc_version>>8, filter);
+       if (dcerpc_uuid_program == NULL) {
+               simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please select a program");
+               return;
+       }
+       str = g_string_new("dcerpc,srt");
+       g_string_append_printf(str,
+           ",%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x,%u.%u",
+           dcerpc_uuid_program->Data1, dcerpc_uuid_program->Data2,
+           dcerpc_uuid_program->Data3,
+           dcerpc_uuid_program->Data4[0], dcerpc_uuid_program->Data4[1],
+           dcerpc_uuid_program->Data4[2], dcerpc_uuid_program->Data4[3],
+           dcerpc_uuid_program->Data4[4], dcerpc_uuid_program->Data4[5],
+           dcerpc_uuid_program->Data4[6], dcerpc_uuid_program->Data4[7],
+           dcerpc_version, 0);
+       filter = gtk_entry_get_text(GTK_ENTRY(filter_entry));
+       if(filter[0] != 0){
+               g_string_append_printf(str, ",%s", filter);
        }
 
-       gtk_dcerpcstat_init(str);
+       gtk_dcerpcstat_init(str->str,NULL);
+       g_string_free(str, TRUE);
 }
 
 
 static void
-dcerpcstat_version_select(GtkWidget *item _U_, gpointer key)
+dcerpcstat_version_select(GtkWidget *vers_combo_box, gpointer user_data _U_)
 {
-       int vers=(int)key;
-
-       dcerpc_version=vers;
-}
-
+       dcerpc_uuid_key *k;
 
+       if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(vers_combo_box), (gpointer)&k)) {
+               g_assert_not_reached();  /* Programming error: somehow no active item */
+       }
 
+       dcerpc_version = k->ver;
+}
 
-static void *
-dcerpcstat_find_vers(gpointer *key, gpointer *value _U_, gpointer *user_data _U_)
+static void
+dcerpcstat_find_vers(gpointer *key, gpointer *value _U_, gpointer user_data)
 {
-       dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
-       GtkWidget *menu_item;
+       dcerpc_uuid_key *k = (dcerpc_uuid_key *)key;
+       GtkWidget       *vers_combo_box = user_data;
        char vs[5];
 
-       if(!uuid_equal((&k->uuid), dcerpc_uuid_program)){
-               return NULL;
-       }
-
-       sprintf(vs,"%d.%d",k->ver&0xff,k->ver>>8);
-       menu_item=gtk_menu_item_new_with_label(vs);
-       SIGNAL_CONNECT(menu_item, "activate", dcerpcstat_version_select,
-                       ((int)k->ver));
-       gtk_widget_show(menu_item);
-       gtk_menu_append(GTK_MENU(vers_menu), menu_item);
-
-       if(dcerpc_version==0xffff){
-               dcerpc_version=k->ver;
+       if(!uuid_equal(&(k->uuid), dcerpc_uuid_program)){
+               return;
        }
-
-       return NULL;
+       g_snprintf(vs, sizeof(vs), "%u", k->ver);
+       ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(vers_combo_box), vs, k);
 }
 
-
 static void
-dcerpcstat_program_select(GtkWidget *item _U_, gpointer key)
+dcerpcstat_program_select(GtkWidget *prog_combo_box, gpointer user_data)
 {
-       dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
+       dcerpc_uuid_key *k;
+       GtkWidget *vers_combo_box;
 
-       dcerpc_uuid_program=&k->uuid;
+       vers_combo_box = user_data;
 
-       /* change version menu */
-       dcerpc_version=0xffff;
-       gtk_object_destroy(GTK_OBJECT(vers_menu));
-       vers_menu=gtk_menu_new();
-       g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_vers, NULL);
-       gtk_option_menu_set_menu(GTK_OPTION_MENU(vers_opt), vers_menu);
-}
+       if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(prog_combo_box), (gpointer)&k)) {
+               g_assert_not_reached();  /* Programming error: somehow no active item */
+       }
 
+       g_signal_handlers_disconnect_by_func(vers_combo_box, G_CALLBACK(dcerpcstat_version_select), NULL );
+       ws_combo_box_clear_text_and_pointer(GTK_COMBO_BOX(vers_combo_box));
 
-static void 
-dcerpcstat_add_program_to_menu(dcerpc_uuid_key *k, dcerpc_uuid_value *v)
-{
-       GtkWidget *program_menu_item;
+       g_assert((k != NULL) && "dcerpc_stat: invalid selection"); /* Somehow selected top level ?? */
+       dcerpc_uuid_program = &(k->uuid);
+
+       /* re-create version menu */
+       g_signal_handlers_disconnect_by_func(vers_combo_box, G_CALLBACK(dcerpcstat_version_select), NULL );
+       ws_combo_box_clear_text_and_pointer(GTK_COMBO_BOX(vers_combo_box));
 
-       program_menu_item=gtk_menu_item_new_with_label(v->name);
-       SIGNAL_CONNECT(program_menu_item, "activate", dcerpcstat_program_select, k);
+       g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_vers, vers_combo_box);
 
-       gtk_widget_show(program_menu_item);
-       gtk_menu_append(GTK_MENU(prog_menu), program_menu_item);
+       g_signal_connect(vers_combo_box, "changed", G_CALLBACK(dcerpcstat_version_select), NULL);
+       ws_combo_box_set_active(GTK_COMBO_BOX(vers_combo_box), 0); /* default: triggers dcerpcstat_version_select callback */
+
+}
 
-       if(!dcerpc_uuid_program){
-               dcerpc_uuid_program=&k->uuid;
+static GtkTreeIter
+dcerpcstat_add_program_to_menu(dcerpc_uuid_key *k, dcerpc_uuid_value *v, GtkWidget *prog_combo_box, int program_item_index)
+{
+       static GtkTreeIter iter;
+       char str[64];
+
+       switch(program_item_index%15){
+       case 0:
+               g_snprintf(str,sizeof(str),"%s ...",v->name);
+               iter = ws_combo_box_append_text_and_pointer_full(
+                       GTK_COMBO_BOX(prog_combo_box), NULL, str, NULL, FALSE); /* top-level entries are insensitive */
+               break;
+
+       default:
+               break;
        }
 
-       return;
+       return ws_combo_box_append_text_and_pointer_full(
+               GTK_COMBO_BOX(prog_combo_box), &iter, v->name, k, TRUE);
 }
 
-static void *
+static void
 dcerpcstat_find_next_program(gpointer *key, gpointer *value, gpointer *user_data _U_)
 {
-       dcerpc_uuid_key *k=(dcerpc_uuid_key *)key;
-       dcerpc_uuid_value *v=(dcerpc_uuid_value *)value;
+       dcerpc_uuid_key   *k = (dcerpc_uuid_key *)key;
+       dcerpc_uuid_value *v = (dcerpc_uuid_value *)value;
 
        /* first time called, just set new_uuid to this one */
-       if((current_uuid_key==NULL)&&(new_uuid_key==NULL)){
-               new_uuid_key=k;
-               new_uuid_value=v;
-               return NULL;
+       if((current_uuid_key==NULL) && (new_uuid_key==NULL)){
+               new_uuid_key   = k;
+               new_uuid_value = v;
+               return;
        }
 
-       /* if we havent got a current one yet, just check the new
+       /* if we haven't got a current one yet, just check the new
           and scan for the first one alphabetically  */
        if(current_uuid_key==NULL){
                if(strcmp(new_uuid_value->name, v->name)>0){
-                       new_uuid_key=k;
-                       new_uuid_value=v;
-                       return NULL;
+                       new_uuid_key   = k;
+                       new_uuid_value = v;
+                       return;
                }
-               return NULL;
+               return;
        }
 
        /* searching for the next one we are only interested in those
           that sorts alphabetically after the current one */
-       if(strcmp(current_uuid_value->name, v->name)>=0){
+       if(strcmp(current_uuid_value->name, v->name) >= 0){
                /* this one doesnt so just skip it */
-               return NULL;
+               return;
        }
 
        /* is it the first potential new entry? */
        if(new_uuid_key==NULL){
-               new_uuid_key=k;
-               new_uuid_value=v;
-               return NULL;
+               new_uuid_key   = k;
+               new_uuid_value = v;
+               return;
        }
 
        /* does it sort before the current new one? */
-       if(strcmp(new_uuid_value->name, v->name)>0){
-               new_uuid_key=k;
-               new_uuid_value=v;
-               return NULL;
+       if(strcmp(new_uuid_value->name, v->name) > 0){
+               new_uuid_key   = k;
+               new_uuid_value = v;
+               return;
        }
 
-       return NULL;
-}
-
-
-static void
-dlg_destroy_cb(void)
-{
-       dlg=NULL;
+       return;
 }
 
 
 static void
-dlg_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
+dlg_destroy_cb(GtkWidget *w _U_, gpointer user_data _U_)
 {
-       gtk_widget_destroy(GTK_WIDGET(parent_w));
+       dlg = NULL;
 }
 
 
 static void
 gtk_dcerpcstat_cb(GtkWidget *w _U_, gpointer d _U_)
 {
-       GtkWidget *bbox, *cancel_button;
-       char *filter;
+       GtkWidget       *dlg_box;
+       GtkWidget       *prog_box,   *prog_label, *prog_combo_box;
+       GtkWidget       *vers_label, *vers_combo_box;
+       GtkWidget       *filter_box, *filter_bt;
+       GtkWidget       *bbox, *start_button, *cancel_button;
+        GtkCellRenderer *cell_renderer;
+#if 0
+       GtkTreeIter      program_first_item_iter;
+#endif
+       const char      *filter;
+       int              program_item_index = 0;
+
+       static construct_args_t args = {
+         "Service Response Time Statistics Filter",
+         FALSE,
+         FALSE,
+         FALSE
+       };
 
        /* if the window is already open, bring it to front and
           un-minimize it, as necessary */
@@ -453,104 +550,146 @@ gtk_dcerpcstat_cb(GtkWidget *w _U_, gpointer d _U_)
                return;
        }
 
-       dlg=dlg_window_new("Ethereal: DCE-RPC SRT Statistics");
-       SIGNAL_CONNECT(dlg, "destroy", dlg_destroy_cb, NULL);
-       dlg_box=gtk_vbox_new(FALSE, 0);
+       dlg = dlg_window_new("Wireshark: Compute DCE-RPC SRT statistics");
+       gtk_window_set_default_size(GTK_WINDOW(dlg), 400, -1);
+
+       dlg_box = gtk_vbox_new(FALSE, 10);
+       gtk_container_set_border_width(GTK_CONTAINER(dlg_box), 10);
        gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
        gtk_widget_show(dlg_box);
 
+       /* Program box */
+       prog_box = gtk_hbox_new(FALSE, 3);
 
-       prog_box=gtk_hbox_new(FALSE, 10);
        /* Program label */
        gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
-       prog_label=gtk_label_new("Program:");
+       prog_label = gtk_label_new("Program:");
        gtk_box_pack_start(GTK_BOX(prog_box), prog_label, FALSE, FALSE, 0);
        gtk_widget_show(prog_label);
 
        /* Program menu */
-       prog_opt=gtk_option_menu_new();
-       prog_menu=gtk_menu_new();
-       current_uuid_key=NULL;
-       current_uuid_value=NULL;
+       dcerpc_uuid_program = NULL;   /* default: no program selected */
+
+       /* The "program combo box" is implemented with a two-level tree.
+          Each top-level of the tree has (up to) 15 selectable "program name"
+          children and shows the name of the first child of that entry
+          as "child_name ...". Each of the top-level entries can be expanded
+          (to show the children) but is "insensitive": ie: cannot be selected.
+          (dcerpcstat_add_program_to_menu() does the actual work to add entries
+           to the combo box).
+            XXX: A simpler alternative might be to just do away with all the two-level
+                 complexity and just use a standard ws_combo_box... even though the
+                 list of "program names" is quite large.
+            XXX: The gtkrc file distributed with Windows Wireshark has the
+                 "appears-as-list" GtkComboBox style property set to 1 and thus
+                 on Windows the entries for this combo box will appear as a tree-view.
+                 The default is 0(FALSE). In this case the the combo box entries will
+                 display as a menu with sub-menus.
+                 A possibility would be to set "appears-as-list" to 0  just for this
+                 particular combo box on Windows so that the entries will appear as a
+                 menu even on Windows).
+       */
+       prog_combo_box = ws_combo_box_new_text_and_pointer_full(&cell_renderer);
+       {
+               /* XXX: Hack So that the top-level insensitive entries don't show
+                       as "grayed out"; The "foreground normal" color is used instead.
+                       This may not really be necessary but seems better to me.
+               */
+               GtkStyle *s;
+               s = gtk_widget_get_style(prog_combo_box);
+               g_object_set(cell_renderer,
+                            "foreground-gdk", &(s->fg[GTK_STATE_NORMAL]),
+                            "foreground-set", TRUE,
+                            NULL);
+       }
+
+       current_uuid_key   = NULL;
+       current_uuid_value = NULL;
        do {
-               new_uuid_key=NULL;
-               new_uuid_value=NULL;
+               new_uuid_key   = NULL;
+               new_uuid_value = NULL;
                g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_next_program, NULL);
                if(new_uuid_key){
-                       dcerpcstat_add_program_to_menu(new_uuid_key, new_uuid_value);
+#if 0
+                       GtkTreeIter tmp_iter;
+                       tmp_iter = dcerpcstat_add_program_to_menu(new_uuid_key, new_uuid_value,
+                                                                 prog_combo_box, program_item_index);
+                       if (program_item_index == 0)
+                               program_first_item_iter = tmp_iter;
+#else
+                       dcerpcstat_add_program_to_menu(new_uuid_key, new_uuid_value,
+                                                       prog_combo_box, program_item_index);
+#endif
+                       program_item_index += 1;
                }
-               current_uuid_key=new_uuid_key;
-               current_uuid_value=new_uuid_value;
-       } while(new_uuid_key!=NULL);
-
-
-       gtk_option_menu_set_menu(GTK_OPTION_MENU(prog_opt), prog_menu);
-       gtk_box_pack_start(GTK_BOX(prog_box), prog_opt, TRUE, TRUE, 0);
-       gtk_widget_show(prog_opt);
+               current_uuid_key   = new_uuid_key;
+               current_uuid_value = new_uuid_value;
+       } while(new_uuid_key != NULL);
+       gtk_box_pack_start(GTK_BOX(prog_box), prog_combo_box, TRUE, TRUE, 0);
+       gtk_widget_show(prog_combo_box);
 
        /* Version label */
        gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
-       vers_label=gtk_label_new("Version:");
+       vers_label = gtk_label_new("Version:");
        gtk_box_pack_start(GTK_BOX(prog_box), vers_label, FALSE, FALSE, 0);
        gtk_widget_show(vers_label);
 
-       /* Version menu */
-       vers_opt=gtk_option_menu_new();
-       vers_menu=gtk_menu_new();
-       dcerpc_version=0xffff;
-       g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_vers, NULL);
-       gtk_option_menu_set_menu(GTK_OPTION_MENU(vers_opt), vers_menu);
-       gtk_box_pack_start(GTK_BOX(prog_box), vers_opt, TRUE, TRUE, 0);
-       gtk_widget_show(vers_opt);
+       /* Version combo-box */
+       /* Note: version combo box rows set when dcerpcstat_program_select() callback invoked */
+       vers_combo_box = ws_combo_box_new_text_and_pointer();
+       gtk_box_pack_start(GTK_BOX(prog_box), vers_combo_box, TRUE, TRUE, 0);
+       gtk_widget_show(vers_combo_box);
 
+       g_signal_connect(prog_combo_box, "changed", G_CALLBACK(dcerpcstat_program_select), vers_combo_box);
+#if 0 /* Don't select an active entry given the way the drop down treeview appears if a default (active) entry is set */
+       ws_combo_box_set_active_iter(GTK_COMBO_BOX(prog_combo_box), &program_first_item_iter); /* triggers callback */
+#endif
        gtk_box_pack_start(GTK_BOX(dlg_box), prog_box, TRUE, TRUE, 0);
        gtk_widget_show(prog_box);
 
+       /* Filter box */
+       filter_box = gtk_hbox_new(FALSE, 3);
 
-       /* filter box */
-       filter_box=gtk_hbox_new(FALSE, 10);
        /* Filter label */
-       gtk_container_set_border_width(GTK_CONTAINER(filter_box), 10);
-       filter_label=gtk_label_new("Filter:");
-       gtk_box_pack_start(GTK_BOX(filter_box), filter_label, FALSE, FALSE, 0);
-       gtk_widget_show(filter_label);
-
-       filter_entry=gtk_entry_new_with_max_length(250);
-       gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, FALSE, FALSE, 0);
-       filter=gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
+       filter_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
+       g_signal_connect(filter_bt, "clicked", G_CALLBACK(display_filter_construct_cb), &args);
+       gtk_box_pack_start(GTK_BOX(filter_box), filter_bt, FALSE, FALSE, 0);
+       gtk_widget_show(filter_bt);
+
+       /* Filter entry */
+       filter_entry = gtk_entry_new();
+       g_signal_connect(filter_entry, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
+       g_object_set_data(G_OBJECT(filter_box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+       g_signal_connect(filter_entry, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
+       g_signal_connect(dlg, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
+       gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, TRUE, TRUE, 0);
+       filter = gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
        if(filter){
                gtk_entry_set_text(GTK_ENTRY(filter_entry), filter);
+       } else {
+               colorize_filter_te_as_empty(filter_entry);
        }
        gtk_widget_show(filter_entry);
-       
+
        gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
        gtk_widget_show(filter_box);
 
+       g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_entry);
 
-       bbox=gtk_hbutton_box_new();
-       gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
-       gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
-       gtk_container_add(GTK_CONTAINER(dlg_box), bbox);
+       /* button box */
+       bbox = dlg_button_row_new(WIRESHARK_STOCK_CREATE_STAT, GTK_STOCK_CANCEL, NULL);
+       gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
        gtk_widget_show(bbox);
 
-       /* the start button */
-       start_button=gtk_button_new_with_label("Create Stat");
-       SIGNAL_CONNECT_OBJECT(start_button, "clicked", 
-                              dcerpcstat_start_button_clicked, NULL);
-       GTK_WIDGET_SET_FLAGS(start_button, GTK_CAN_DEFAULT);
-       gtk_box_pack_start(GTK_BOX(bbox), start_button, TRUE, TRUE, 0);
-       gtk_widget_grab_default(start_button);
-       gtk_widget_show(start_button);
-
-#if GTK_MAJOR_VERSION < 2
-       cancel_button=gtk_button_new_with_label("Cancel");
-#else
-       cancel_button=gtk_button_new_from_stock(GTK_STOCK_CANCEL);
-#endif
-       SIGNAL_CONNECT(cancel_button, "clicked", dlg_cancel_cb, dlg);
-       GTK_WIDGET_SET_FLAGS(cancel_button, GTK_CAN_DEFAULT);
-       gtk_box_pack_start(GTK_BOX(bbox), cancel_button, TRUE, TRUE, 0);
-       gtk_widget_show(cancel_button);
+       start_button = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_CREATE_STAT);
+       g_signal_connect_swapped(start_button, "clicked",
+                                G_CALLBACK(dcerpcstat_start_button_clicked), NULL);
+
+       cancel_button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+       window_set_cancel_button(dlg, cancel_button, window_cancel_button_cb);
+
+       g_signal_connect(dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+       g_signal_connect(dlg, "destroy", G_CALLBACK(dlg_destroy_cb), NULL);
 
        /* Catch the "activate" signal on the filter text entry, so that
           if the user types Return there, we act as if the "Create Stat"
@@ -559,26 +698,20 @@ gtk_dcerpcstat_cb(GtkWidget *w _U_, gpointer d _U_)
           focus. */
        dlg_set_activate(filter_entry, start_button);
 
-       /* Catch the "key_press_event" signal in the window, so that we can
-          catch the ESC key being pressed and act as if the "Cancel" button
-          had been selected. */
-       dlg_set_cancel(dlg, cancel_button);
+       gtk_widget_grab_default(start_button );
 
        /* Give the initial focus to the "Filter" entry box. */
        gtk_widget_grab_focus(filter_entry);
 
        gtk_widget_show_all(dlg);
+       window_present(dlg);
 }
 
 void
 register_tap_listener_gtkdcerpcstat(void)
 {
-       register_ethereal_tap("dcerpc,srt,", gtk_dcerpcstat_init);
-}
+       register_stat_cmd_arg("dcerpc,srt,", gtk_dcerpcstat_init,NULL);
 
-void
-register_tap_menu_gtkdcerpcstat(void)
-{
-       register_tap_menu_item("Statistics/Service Response Time/DCE-RPC",
-           gtk_dcerpcstat_cb, NULL, NULL);
+       register_stat_menu_item("DCE-RPC...", REGISTER_STAT_GROUP_RESPONSE_TIME,
+           gtk_dcerpcstat_cb, NULL, NULL, NULL);
 }