aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am27
-rw-r--r--configure.in7
-rw-r--r--file.c24
-rw-r--r--gtk2/.cvsignore9
-rw-r--r--gtk2/Makefile.am101
-rw-r--r--gtk2/Makefile.nmake68
-rw-r--r--gtk2/capture_dlg.c955
-rw-r--r--gtk2/capture_dlg.h32
-rw-r--r--gtk2/capture_prefs.c167
-rw-r--r--gtk2/capture_prefs.h33
-rw-r--r--gtk2/color_dlg.c1166
-rw-r--r--gtk2/color_dlg.h31
-rw-r--r--gtk2/color_utils.c53
-rw-r--r--gtk2/color_utils.h33
-rw-r--r--gtk2/colors.c291
-rw-r--r--gtk2/colors.h67
-rw-r--r--gtk2/column_prefs.c377
-rw-r--r--gtk2/column_prefs.h29
-rw-r--r--gtk2/decode_as_dlg.c1349
-rw-r--r--gtk2/decode_as_dlg.h33
-rw-r--r--gtk2/dfilter_expr_dlg.c1207
-rw-r--r--gtk2/dfilter_expr_dlg.h31
-rw-r--r--gtk2/display_opts.c337
-rw-r--r--gtk2/display_opts.h31
-rw-r--r--gtk2/dlg_utils.c167
-rw-r--r--gtk2/dlg_utils.h46
-rw-r--r--gtk2/file_dlg.c647
-rw-r--r--gtk2/file_dlg.h42
-rw-r--r--gtk2/filter_prefs.c1178
-rw-r--r--gtk2/filter_prefs.h51
-rw-r--r--gtk2/find_dlg.c282
-rw-r--r--gtk2/find_dlg.h33
-rw-r--r--gtk2/follow_dlg.c819
-rw-r--r--gtk2/follow_dlg.h33
-rw-r--r--gtk2/goto_dlg.c169
-rw-r--r--gtk2/goto_dlg.h31
-rw-r--r--gtk2/gtkglobals.h58
-rw-r--r--gtk2/gui_prefs.c732
-rw-r--r--gtk2/gui_prefs.h34
-rw-r--r--gtk2/help_dlg.c397
-rw-r--r--gtk2/help_dlg.h35
-rw-r--r--gtk2/keys.h48
-rw-r--r--gtk2/main.c2467
-rw-r--r--gtk2/main.h102
-rw-r--r--gtk2/menu.c527
-rw-r--r--gtk2/menu.h43
-rw-r--r--gtk2/nameres_prefs.c129
-rw-r--r--gtk2/nameres_prefs.h33
-rw-r--r--gtk2/packet_win.c263
-rw-r--r--gtk2/packet_win.h39
-rw-r--r--gtk2/plugins_dlg.c164
-rw-r--r--gtk2/prefs_dlg.c1239
-rw-r--r--gtk2/prefs_dlg.h42
-rw-r--r--gtk2/print_dlg.c690
-rw-r--r--gtk2/print_mswin.c202
-rw-r--r--gtk2/print_mswin.h27
-rw-r--r--gtk2/print_prefs.c250
-rw-r--r--gtk2/print_prefs.h34
-rw-r--r--gtk2/progress_dlg.c380
-rw-r--r--gtk2/proto_dlg.c412
-rw-r--r--gtk2/proto_dlg.h32
-rw-r--r--gtk2/proto_draw.c1051
-rw-r--r--gtk2/proto_draw.h70
-rw-r--r--gtk2/proto_hier_stats_dlg.c253
-rw-r--r--gtk2/proto_hier_stats_dlg.h27
-rw-r--r--gtk2/simple_dialog.c179
-rw-r--r--gtk2/stream_prefs.c213
-rw-r--r--gtk2/stream_prefs.h34
-rw-r--r--gtk2/summary_dlg.c231
-rw-r--r--gtk2/summary_dlg.h31
-rw-r--r--gtk2/tcp_graph.c3772
-rw-r--r--gtk2/tcp_graph.h27
-rw-r--r--gtk2/ui_util.c205
-rw-r--r--gtk2/ui_util.h55
74 files changed, 24471 insertions, 12 deletions
diff --git a/Makefile.am b/Makefile.am
index 63b7d4e530..9baac3b3dd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,22 +1,22 @@
# Makefile.am
# Automake file for Ethereal
#
-# $Id: Makefile.am,v 1.463 2002/08/30 02:08:50 sharpe Exp $
+# $Id: Makefile.am,v 1.464 2002/08/31 09:55:18 oabad Exp $
#
# Ethereal - Network traffic analyzer
# By Gerald Combs <gerald@ethereal.com>
# 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.
@@ -63,7 +63,7 @@ ACLOCAL_AMFLAGS = `./aclocal-flags`
bin_PROGRAMS = @ethereal_bin@ @editcap_bin@ @mergecap_bin@ @tethereal_bin@ @dftest_bin@ @randpkt_bin@ @text2pcap_bin@
bin_SCRIPTS = @idl2eth_bin@
man1_MANS = @ethereal_man@ @editcap_man@ @mergecap_man@ @tethereal_man@ @text2pcap_man@ @idl2eth_man@
-man_MANS =
+man_MANS =
EXTRA_PROGRAMS = ethereal ethereal_static tethereal tethereal_static editcap mergecap dftest text2pcap
EXTRA_SCRIPTS = idl2eth
@@ -543,7 +543,7 @@ noinst_HEADERS = \
packet-ypbind.h \
packet-yppasswd.h \
packet-ypserv.h \
- packet-ypxfr.h
+ packet-ypxfr.h
ETHEREAL_COMMON_SRC = \
afn.c \
@@ -645,7 +645,7 @@ ethereal_static_SOURCES = \
statusbar.h \
summary.c \
summary.h \
- ui_util.h
+ ui_util.h
EXTRA_ethereal_SOURCES = \
snprintf.c \
@@ -673,12 +673,21 @@ ethereal_optional_objects = @SNPRINTF_O@ @STRERROR_O@ \
# Additional libs that I know how to build. These will be
# linked into the ethereal executable.
+if USE_GTK2
+ethereal_additional_libs = \
+ wiretap/libwiretap.a \
+ gtk2/libui.a \
+ epan/libethereal.a \
+ epan/ftypes/libftypes.a \
+ epan/dfilter/libdfilter.a
+else
ethereal_additional_libs = \
wiretap/libwiretap.a \
gtk/libui.a \
epan/libethereal.a \
epan/ftypes/libftypes.a \
epan/dfilter/libdfilter.a
+endif
# This is the automake dependency variable for the executable
ethereal_DEPENDENCIES = \
@@ -710,7 +719,7 @@ ethereal_static_LDADD = \
$(ethereal_optional_objects) \
$(ethereal_additional_libs) \
@SNMP_LIBS@ @SSL_LIBS@ \
- @PCAP_LIBS@ @GTK_LIBS@
+ @PCAP_LIBS@ @GTK_LIBS@
ethereal_LDFLAGS = -export-dynamic
ethereal_static_LDFLAGS = -Wl,-static
@@ -743,7 +752,7 @@ tethereal_DEPENDENCIES = \
tethereal_static_DEPENDENCIES = \
$(ethereal_optional_objects) \
- $(tethereal_additional_libs)
+ $(tethereal_additional_libs)
# This automake variable adds to the link-line for the executable
tethereal_LDADD = wiretap/libwiretap.a \
diff --git a/configure.in b/configure.in
index 8a11548fac..e7da50152d 100644
--- a/configure.in
+++ b/configure.in
@@ -1,4 +1,4 @@
-# $Id: configure.in,v 1.174 2002/08/28 00:37:17 jmayer Exp $
+# $Id: configure.in,v 1.175 2002/08/31 09:55:18 oabad Exp $
dnl
dnl Process this file with autoconf 2.13 or later to produce a
dnl configure script; 2.12 doesn't generate a "configure" script that
@@ -219,6 +219,7 @@ AC_ARG_ENABLE(ethereal,
AC_ARG_ENABLE(gtk2,
[ --enable-gtk2 build Glib2/Gtk2+-based (t)ethereal. [default=no]],enable_gtk2=yes,enable_gtk2=no)
+AM_CONDITIONAL(USE_GTK2, test x$enable_gtk2 = xyes)
# GTK checks
# We don't add $GLIB_LIBS to LIBS, because we don't want to force all
@@ -247,7 +248,7 @@ fi
if test "$GTK_OK" = "two" ; then
ethereal_bin="ethereal"
ethereal_man="ethereal.1"
- ethereal_SUBDIRS="gtk" # <--- change to gtk2, if we can't use the same directory
+ ethereal_SUBDIRS="gtk2"
# Ignore GLIB_CFLAGS
AM_PATH_GLIB_2_0(2.0.0, , AC_MSG_ERROR(GLib distribution not found.), gmodule)
@@ -702,6 +703,7 @@ AC_OUTPUT(
Makefile
doc/Makefile
gtk/Makefile
+ gtk2/Makefile
packaging/Makefile
packaging/nsis/Makefile
packaging/rpm/Makefile
@@ -753,6 +755,7 @@ echo " Build dftest : $enable_dftest"
echo ""
echo " Install setuid : $setuid_message"
echo " Use plugins : $have_plugins"
+echo " Use GTK+ v2 library : $enable_gtk2"
echo " Use pcap library : $want_pcap"
echo " Use zlib library : $zlib_message"
echo " Use IPv6 name resolution : $enable_ipv6"
diff --git a/file.c b/file.c
index 6c098b14e1..0fb3f8c56a 100644
--- a/file.c
+++ b/file.c
@@ -1,7 +1,7 @@
/* file.c
* File I/O routines
*
- * $Id: file.c,v 1.287 2002/08/28 21:00:06 jmayer Exp $
+ * $Id: file.c,v 1.288 2002/08/31 09:55:18 oabad Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@@ -63,9 +63,17 @@
#include <epan/epan.h>
#include <epan/filesystem.h>
+#if GTK_MAJOR_VERSION == 1
#include "gtk/main.h"
+#else
+#include "gtk2/main.h"
+#endif
#include "color.h"
+#if GTK_MAJOR_VERSION == 1
#include "gtk/color_utils.h"
+#else
+#include "gtk2/color_utils.h"
+#endif
#include "column.h"
#include <epan/packet.h>
#include "print.h"
@@ -77,12 +85,21 @@
#include "ui_util.h"
#include "statusbar.h"
#include "prefs.h"
+#if GTK_MAJOR_VERSION == 1
#include "gtk/proto_draw.h"
#include "gtk/packet_win.h"
+#else
+#include "gtk2/proto_draw.h"
+#include "gtk2/packet_win.h"
+#endif
#include <epan/dfilter/dfilter.h>
#include <epan/conversation.h>
#include "globals.h"
+#if GTK_MAJOR_VERSION == 1
#include "gtk/colors.h"
+#else
+#include "gtk2/colors.h"
+#endif
#include <epan/epan_dissect.h>
extern GtkWidget *packet_list, *byte_nb_ptr, *tree_view;
@@ -1429,7 +1446,12 @@ change_time_formats(capture_file *cf)
for (i = 0; i < cf->cinfo.num_cols; i++) {
if (cf->cinfo.fmt_matx[i][COL_CLS_TIME]) {
gtk_clist_set_column_width(GTK_CLIST(packet_list), i,
+#if GTK_MAJOR_VERSION == 1
gdk_string_width(pl_style->font, get_column_longest_string(COL_CLS_TIME)));
+#else
+ gdk_string_width(gdk_font_from_description(pl_style->font_desc),
+ get_column_longest_string(COL_CLS_TIME)));
+#endif
}
}
diff --git a/gtk2/.cvsignore b/gtk2/.cvsignore
new file mode 100644
index 0000000000..51895503d5
--- /dev/null
+++ b/gtk2/.cvsignore
@@ -0,0 +1,9 @@
+Makefile
+Makefile.in
+.deps
+stamp-h.in
+config.status
+aclocal.m4
+*.obj
+*.lib
+*.pdb
diff --git a/gtk2/Makefile.am b/gtk2/Makefile.am
new file mode 100644
index 0000000000..08f2b0fdd3
--- /dev/null
+++ b/gtk2/Makefile.am
@@ -0,0 +1,101 @@
+# Makefile.am
+# Automake file for the GTK2 interface routines for Ethereal
+#
+# $Id: Makefile.am,v 1.1 2002/08/31 09:55:20 oabad Exp $
+#
+# Ethereal - Network traffic analyzer
+# By Gerald Combs <gerald@ethereal.com>
+# 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.
+
+noinst_LIBRARIES = libui.a
+
+CLEANFILES = \
+ libui.a \
+ *~
+
+libui_a_SOURCES = \
+ capture_dlg.c \
+ capture_dlg.h \
+ capture_prefs.c \
+ capture_prefs.h \
+ color_dlg.c \
+ color_dlg.h \
+ colors.c \
+ colors.h \
+ color_utils.c \
+ color_utils.h \
+ column_prefs.c \
+ column_prefs.h \
+ decode_as_dlg.c \
+ decode_as_dlg.h \
+ dfilter_expr_dlg.c \
+ dfilter_expr_dlg.h \
+ display_opts.c \
+ display_opts.h \
+ dlg_utils.c \
+ dlg_utils.h \
+ file_dlg.c \
+ file_dlg.h \
+ filter_prefs.c \
+ filter_prefs.h \
+ find_dlg.c \
+ find_dlg.h \
+ follow_dlg.c \
+ follow_dlg.h \
+ goto_dlg.c \
+ goto_dlg.h \
+ gtkglobals.h \
+ gui_prefs.c \
+ gui_prefs.h \
+ help_dlg.c \
+ help_dlg.h \
+ keys.h \
+ main.c \
+ main.h \
+ menu.c \
+ menu.h \
+ nameres_prefs.c \
+ nameres_prefs.h \
+ packet_win.c \
+ packet_win.h \
+ plugins_dlg.c \
+ prefs_dlg.c \
+ prefs_dlg.h \
+ print_dlg.c \
+ print_prefs.c \
+ print_prefs.h \
+ progress_dlg.c \
+ proto_dlg.c \
+ proto_dlg.h \
+ proto_draw.c \
+ proto_draw.h \
+ proto_hier_stats_dlg.h \
+ proto_hier_stats_dlg.c \
+ simple_dialog.c \
+ stream_prefs.c \
+ stream_prefs.h \
+ summary_dlg.c \
+ summary_dlg.h \
+ tcp_graph.c \
+ tcp_graph.h \
+ ui_util.c \
+ ui_util.h
+
+EXTRA_DIST = \
+ Makefile.nmake \
+ print_mswin.c \
+ print_mswin.h
diff --git a/gtk2/Makefile.nmake b/gtk2/Makefile.nmake
new file mode 100644
index 0000000000..2bfd1a7962
--- /dev/null
+++ b/gtk2/Makefile.nmake
@@ -0,0 +1,68 @@
+## Makefile for building ethereal.exe with Microsoft C and nmake
+## Use: $(MAKE) /$(MAKEFLAGS) -f makefile.nmake
+#
+# $Id: Makefile.nmake,v 1.1 2002/08/31 09:55:20 oabad Exp $
+
+include ..\config.nmake
+
+############### no need to modify below this line #########
+
+CFLAGS=-DHAVE_CONFIG_H /I.. /I../wiretap \
+ /I$(GLIB_DIR) /I$(GTK_DIR) /I$(GLIB_DIR)/gmodule \
+ /I$(GTK_DIR)\gdk /I$(GTK_DIR)\gdk\win32 \
+ /I$(PCAP_DIR)\WPCAP\LIBPCAP /I$(PCAP_DIR)\WPCAP\LIBPCAP\bpf \
+ /I$(PCAP_DIR)\WPCAP\LIBPCAP\lbl \
+ /I$(PCAP_DIR)\include -D_U_="" $(LOCAL_CFLAGS)
+
+CVARSDLL=-DWIN32 -DNULL=0 -D_MT -D_DLL
+
+.c.obj::
+ $(CC) $(CVARSDLL) $(CFLAGS) -Fd.\ -c $<
+
+# gtkclist.obj is not in here because it is gtk+-1.2 code,
+# while the DLL for GTK+ on windows is gtk+-1.3, and there's
+# some functions that have disappeared in gtk+-1.3. I might
+# get around to #ifdef'ing them out in our gtkclist.c.
+OBJECTS=capture_dlg.obj \
+ capture_prefs.obj \
+ color_dlg.obj \
+ colors.obj \
+ color_utils.obj \
+ column_prefs.obj \
+ decode_as_dlg.obj \
+ dfilter_expr_dlg.obj \
+ display_opts.obj \
+ dlg_utils.obj \
+ file_dlg.obj \
+ filter_prefs.obj \
+ find_dlg.obj \
+ follow_dlg.obj \
+ goto_dlg.obj \
+ gui_prefs.obj \
+ help_dlg.obj \
+ main.obj \
+ menu.obj \
+ nameres_prefs.obj \
+ packet_win.obj \
+ plugins_dlg.obj \
+ prefs_dlg.obj \
+ print_dlg.obj \
+ print_mswin.obj \
+ print_prefs.obj \
+ progress_dlg.obj \
+ proto_dlg.obj \
+ proto_draw.obj \
+ proto_hier_stats_dlg.obj \
+ simple_dialog.obj \
+ stream_prefs.obj \
+ summary_dlg.obj \
+ tcp_graph.obj \
+ ui_util.obj
+
+
+libui.lib : ..\config.h $(OBJECTS)
+ lib /out:libui.lib $(OBJECTS)
+
+
+clean:
+ rm -f $(OBJECTS) libui.lib $(PDB_FILE)
diff --git a/gtk2/capture_dlg.c b/gtk2/capture_dlg.c
new file mode 100644
index 0000000000..1a37f28b42
--- /dev/null
+++ b/gtk2/capture_dlg.c
@@ -0,0 +1,955 @@
+/* capture_dlg.c
+ * Routines for packet capture windows
+ *
+ * $Id: capture_dlg.c,v 1.1 2002/08/31 09:55:20 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_LIBPCAP
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <gtk/gtk.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <time.h>
+
+#include <pcap.h>
+
+#ifdef NEED_SNPRINTF_H
+# include "snprintf.h"
+#endif
+
+#include "capture.h"
+#include "globals.h"
+#include <epan/resolv.h>
+#include "main.h"
+#include "ui_util.h"
+#include "capture_dlg.h"
+#include "filter_prefs.h"
+#include "simple_dialog.h"
+#include "dlg_utils.h"
+#include "pcap-util.h"
+#include "prefs.h"
+#include "ringbuffer.h"
+
+#ifdef _WIN32
+#include "capture-wpcap.h"
+#endif
+
+/* Capture callback data keys */
+#define E_CAP_IFACE_KEY "cap_iface"
+#define E_CAP_SNAP_CB_KEY "cap_snap_cb"
+#define E_CAP_SNAP_SB_KEY "cap_snap_sb"
+#define E_CAP_PROMISC_KEY "cap_promisc"
+#define E_CAP_FILT_KEY "cap_filter_te"
+#define E_CAP_FILE_TE_KEY "cap_file_te"
+#define E_CAP_RING_ON_TB_KEY "cap_ringbuffer_on_tb"
+#define E_CAP_RING_NBF_LB_KEY "cap_ringbuffer_nbf_lb"
+#define E_CAP_RING_NBF_SB_KEY "cap_ringbuffer_nbf_sb"
+#define E_CAP_SYNC_KEY "cap_sync"
+#define E_CAP_AUTO_SCROLL_KEY "cap_auto_scroll"
+#define E_CAP_COUNT_CB_KEY "cap_count_cb"
+#define E_CAP_COUNT_SB_KEY "cap_count_sb"
+#define E_CAP_FILESIZE_CB_KEY "cap_filesize_cb"
+#define E_CAP_FILESIZE_SB_KEY "cap_filesize_sb"
+#define E_CAP_FILESIZE_LB_KEY "cap_filesize_lb"
+#define E_CAP_DURATION_CB_KEY "cap_duration_cb"
+#define E_CAP_DURATION_SB_KEY "cap_duration_sb"
+#define E_CAP_M_RESOLVE_KEY "cap_m_resolve"
+#define E_CAP_N_RESOLVE_KEY "cap_n_resolve"
+#define E_CAP_T_RESOLVE_KEY "cap_t_resolve"
+
+#define E_FS_CALLER_PTR_KEY "fs_caller_ptr"
+#define E_FILE_SEL_DIALOG_PTR_KEY "file_sel_dialog_ptr"
+
+static void
+capture_prep_file_cb(GtkWidget *w, gpointer te);
+
+static void
+cap_prep_fs_ok_cb(GtkWidget *w, gpointer data);
+
+static void
+cap_prep_fs_cancel_cb(GtkWidget *w, gpointer data);
+
+static void
+cap_prep_fs_destroy_cb(GtkWidget *win, gpointer data);
+
+static void
+capture_prep_adjust_sensitivity(GtkWidget *tb, gpointer parent_w);
+
+static void
+capture_prep_ok_cb(GtkWidget *ok_bt, gpointer parent_w);
+
+static void
+capture_prep_close_cb(GtkWidget *close_bt, gpointer parent_w);
+
+static void
+capture_prep_destroy_cb(GtkWidget *win, gpointer user_data);
+
+void
+capture_stop_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ capture_stop();
+}
+
+/*
+ * Keep a static pointer to the current "Capture Options" window, if
+ * any, so that if somebody tries to do "Capture:Start" while there's
+ * already a "Capture Options" window up, we just pop up the existing
+ * one, rather than creating a new one.
+ */
+static GtkWidget *cap_open_w;
+
+void
+capture_prep_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ GtkWidget *main_vb,
+ *capture_fr, *capture_vb,
+ *if_hb, *if_cb, *if_lb,
+ *snap_hb, *snap_cb, *snap_sb, *snap_lb,
+ *promisc_cb,
+ *filter_hb, *filter_bt, *filter_te,
+ *file_fr, *file_vb,
+ *file_hb, *file_bt, *file_te,
+ *ringbuffer_hb, *ringbuffer_on_tb, *ringbuffer_nbf_lb, *ringbuffer_nbf_sb,
+ *display_fr, *display_vb,
+ *sync_cb, *auto_scroll_cb,
+ *limit_fr, *limit_vb,
+ *count_hb, *count_cb, *count_sb, *count_lb,
+ *filesize_hb, *filesize_cb, *filesize_sb, *filesize_lb,
+ *duration_hb, *duration_cb, *duration_sb, *duration_lb,
+ *resolv_fr, *resolv_vb,
+ *m_resolv_cb, *n_resolv_cb, *t_resolv_cb,
+ *bbox, *ok_bt, *cancel_bt;
+ GtkAccelGroup *accel_group;
+ GtkAdjustment *snap_adj, *ringbuffer_nbf_adj,
+ *count_adj, *filesize_adj, *duration_adj;
+ GList *if_list;
+ int err;
+ char err_str[PCAP_ERRBUF_SIZE];
+
+ if (cap_open_w != NULL) {
+ /* There's already a "Capture Options" dialog box; reactivate it. */
+ reactivate_window(cap_open_w);
+ return;
+ }
+
+#ifdef _WIN32
+ /* Is WPcap loaded? */
+ if (!has_wpcap) {
+ simple_dialog(ESD_TYPE_CRIT, NULL,
+ "Unable to load WinPcap (wpcap.dll); Ethereal will not be able\n"
+ "to capture packets.\n\n"
+ "In order to capture packets, WinPcap must be installed; see\n"
+ "\n"
+ " http://winpcap.polito.it/\n"
+ "\n"
+ "or the mirror at\n"
+ "\n"
+ " http://winpcap.mirror.ethereal.com/\n"
+ "\n"
+ "or the mirror at\n"
+ "\n"
+ " http://www.mirrors.wiretapped.net/security/packet-capture/winpcap/\n"
+ "\n"
+ "for a downloadable version of WinPcap and for instructions\n"
+ "on how to install WinPcap.");
+ return;
+ }
+#endif
+
+ if_list = get_interface_list(&err, err_str);
+ if (if_list == NULL && err == CANT_GET_INTERFACE_LIST) {
+ simple_dialog(ESD_TYPE_WARN, NULL, "Can't get list of interfaces: %s",
+ err_str);
+ }
+
+ cap_open_w = dlg_window_new("Ethereal: Capture Options");
+ g_signal_connect(G_OBJECT(cap_open_w), "destroy",
+ G_CALLBACK(capture_prep_destroy_cb), NULL);
+
+ /* Accelerator group for the accelerators (or, as they're called in
+ Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
+ Ctrl+<key> is an accelerator). */
+ accel_group = gtk_accel_group_new();
+ gtk_window_add_accel_group(GTK_WINDOW(cap_open_w), accel_group);
+
+ main_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(cap_open_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* Capture-related options frame */
+ capture_fr = gtk_frame_new("Capture");
+ gtk_container_add(GTK_CONTAINER(main_vb), capture_fr);
+ gtk_widget_show(capture_fr);
+
+ capture_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(capture_fr), capture_vb);
+ gtk_widget_show(capture_vb);
+
+ /* Interface row */
+ if_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(capture_vb), if_hb);
+ gtk_widget_show(if_hb);
+
+ if_lb = gtk_label_new("Interface:");
+ gtk_box_pack_start(GTK_BOX(if_hb), if_lb, FALSE, FALSE, 6);
+ gtk_widget_show(if_lb);
+
+ if_cb = gtk_combo_new();
+ if (if_list != NULL)
+ gtk_combo_set_popdown_strings(GTK_COMBO(if_cb), if_list);
+ if (cfile.iface == NULL && prefs.capture_device != NULL) {
+ /* No interface was specified on the command line or in a previous
+ capture, but there is one specified in the preferences file;
+ make the one from the preferences file the default */
+ cfile.iface = g_strdup(prefs.capture_device);
+ }
+ if (cfile.iface != NULL)
+ gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), cfile.iface);
+ else if (if_list != NULL)
+ gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), if_list->data);
+ gtk_box_pack_start(GTK_BOX(if_hb), if_cb, TRUE, TRUE, 6);
+ gtk_widget_show(if_cb);
+
+ free_interface_list(if_list);
+
+ /* Capture length row */
+ snap_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(capture_vb), snap_hb);
+ gtk_widget_show(snap_hb);
+
+ snap_cb = dlg_check_button_new_with_label_with_mnemonic(
+ "_Limit each packet to", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(snap_cb),
+ capture_opts.has_snaplen);
+ g_signal_connect(G_OBJECT(snap_cb), "toggled",
+ G_CALLBACK(capture_prep_adjust_sensitivity),
+ GTK_OBJECT(cap_open_w));
+ gtk_box_pack_start(GTK_BOX(snap_hb), snap_cb, FALSE, FALSE, 0);
+ gtk_widget_show(snap_cb);
+
+ snap_adj = (GtkAdjustment *) gtk_adjustment_new((float) capture_opts.snaplen,
+ MIN_PACKET_SIZE, WTAP_MAX_PACKET_SIZE, 1.0, 10.0, 0.0);
+ snap_sb = gtk_spin_button_new (snap_adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (snap_sb), TRUE);
+ gtk_widget_set_size_request(snap_sb, 80, -1);
+ gtk_box_pack_start (GTK_BOX(snap_hb), snap_sb, FALSE, FALSE, 0);
+ gtk_widget_show(snap_sb);
+
+ snap_lb = gtk_label_new("bytes");
+ gtk_misc_set_alignment(GTK_MISC(snap_lb), 0, 0.5);
+ gtk_box_pack_start(GTK_BOX(snap_hb), snap_lb, FALSE, FALSE, 0);
+ gtk_widget_show(snap_lb);
+
+ /* Promiscuous mode row */
+ promisc_cb = dlg_check_button_new_with_label_with_mnemonic(
+ "Capture packets in _promiscuous mode", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(promisc_cb),
+ capture_opts.promisc_mode);
+ gtk_container_add(GTK_CONTAINER(capture_vb), promisc_cb);
+ gtk_widget_show(promisc_cb);
+
+ /* Filter row */
+ filter_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(capture_vb), filter_hb);
+ gtk_widget_show(filter_hb);
+
+ filter_bt = gtk_button_new_with_label("Filter:");
+ g_signal_connect(G_OBJECT(filter_bt), "clicked",
+ G_CALLBACK(capture_filter_construct_cb), NULL);
+ gtk_box_pack_start(GTK_BOX(filter_hb), filter_bt, FALSE, FALSE, 3);
+ gtk_widget_show(filter_bt);
+
+ filter_te = gtk_entry_new();
+ if (cfile.cfilter) gtk_entry_set_text(GTK_ENTRY(filter_te), cfile.cfilter);
+ gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
+ gtk_box_pack_start(GTK_BOX(filter_hb), filter_te, TRUE, TRUE, 3);
+ gtk_widget_show(filter_te);
+
+ /* Capture file-related options frame */
+ file_fr = gtk_frame_new("Capture file(s)");
+ gtk_container_add(GTK_CONTAINER(main_vb), file_fr);
+ gtk_widget_show(file_fr);
+
+ file_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(file_fr), file_vb);
+ gtk_widget_show(file_vb);
+
+ /* File row */
+ file_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(file_vb), file_hb);
+ gtk_widget_show(file_hb);
+
+ file_bt = gtk_button_new_with_label("File:");
+ gtk_box_pack_start(GTK_BOX(file_hb), file_bt, FALSE, FALSE, 3);
+ gtk_widget_show(file_bt);
+
+ file_te = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(file_hb), file_te, TRUE, TRUE, 3);
+ gtk_widget_show(file_te);
+
+ g_signal_connect(G_OBJECT(file_bt), "clicked",
+ G_CALLBACK(capture_prep_file_cb), GTK_OBJECT(file_te));
+
+ /* Ring buffer row */
+ ringbuffer_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(file_vb), ringbuffer_hb);
+ gtk_widget_show(ringbuffer_hb);
+
+ ringbuffer_on_tb = dlg_check_button_new_with_label_with_mnemonic(
+ "Use _ring buffer", accel_group);
+ /* Ring buffer mode is allowed only if we're not doing an "Update list of
+ packets in real time" capture, so force it off if we're doing such
+ a capture. */
+ if (capture_opts.sync_mode)
+ capture_opts.ringbuffer_on = FALSE;
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(ringbuffer_on_tb),
+ capture_opts.ringbuffer_on);
+ g_signal_connect(G_OBJECT(ringbuffer_on_tb), "toggled",
+ G_CALLBACK(capture_prep_adjust_sensitivity),
+ GTK_OBJECT(cap_open_w));
+ gtk_box_pack_start(GTK_BOX(ringbuffer_hb), ringbuffer_on_tb, FALSE, FALSE, 0);
+ gtk_widget_show(ringbuffer_on_tb);
+
+ ringbuffer_nbf_lb = gtk_label_new("Number of files");
+ gtk_misc_set_alignment(GTK_MISC(ringbuffer_nbf_lb), 1, 0.5);
+ gtk_box_pack_start(GTK_BOX(ringbuffer_hb), ringbuffer_nbf_lb, FALSE, FALSE, 6);
+ gtk_widget_show(ringbuffer_nbf_lb);
+
+ ringbuffer_nbf_adj = (GtkAdjustment *) gtk_adjustment_new((float) capture_opts.ringbuffer_num_files,
+ RINGBUFFER_MIN_NUM_FILES, RINGBUFFER_MAX_NUM_FILES, 1.0, 10.0, 0.0);
+ ringbuffer_nbf_sb = gtk_spin_button_new (ringbuffer_nbf_adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (ringbuffer_nbf_sb), TRUE);
+ gtk_widget_set_size_request(ringbuffer_nbf_sb, 40, -1);
+ gtk_box_pack_start (GTK_BOX(ringbuffer_hb), ringbuffer_nbf_sb, TRUE, TRUE, 0);
+ gtk_widget_show(ringbuffer_nbf_sb);
+
+ /* Display-related options frame */
+ display_fr = gtk_frame_new("Display options");
+ gtk_container_add(GTK_CONTAINER(main_vb), display_fr);
+ gtk_widget_show(display_fr);
+
+ display_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(display_fr), display_vb);
+ gtk_widget_show(display_vb);
+
+ /* "Update display in real time" row */
+ sync_cb = dlg_check_button_new_with_label_with_mnemonic(
+ "_Update list of packets in real time", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(sync_cb),
+ capture_opts.sync_mode);
+ g_signal_connect(G_OBJECT(sync_cb), "toggled",
+ G_CALLBACK(capture_prep_adjust_sensitivity),
+ GTK_OBJECT(cap_open_w));
+ gtk_container_add(GTK_CONTAINER(display_vb), sync_cb);
+ gtk_widget_show(sync_cb);
+
+ /* "Auto-scroll live update" row */
+ auto_scroll_cb = dlg_check_button_new_with_label_with_mnemonic(
+ "_Automatic scrolling in live capture", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(auto_scroll_cb), auto_scroll_live);
+ gtk_container_add(GTK_CONTAINER(display_vb), auto_scroll_cb);
+ gtk_widget_show(auto_scroll_cb);
+
+ /* Capture limits frame */
+ limit_fr = gtk_frame_new("Capture limits");
+ gtk_container_add(GTK_CONTAINER(main_vb), limit_fr);
+ gtk_widget_show(limit_fr);
+
+ limit_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(limit_fr), limit_vb);
+ gtk_widget_show(limit_vb);
+
+ /* Count row */
+ count_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(limit_vb), count_hb);
+ gtk_widget_show(count_hb);
+
+ count_cb = gtk_check_button_new_with_label("Stop capture after");
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(count_cb),
+ capture_opts.has_autostop_count);
+ g_signal_connect(G_OBJECT(count_cb), "toggled",
+ G_CALLBACK(capture_prep_adjust_sensitivity),
+ GTK_OBJECT(cap_open_w));
+ gtk_box_pack_start(GTK_BOX(count_hb), count_cb, FALSE, FALSE, 0);
+ gtk_widget_show(count_cb);
+
+ count_adj = (GtkAdjustment *) gtk_adjustment_new(capture_opts.autostop_count,
+ 1, INT_MAX, 1.0, 10.0, 0.0);
+ count_sb = gtk_spin_button_new (count_adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (count_sb), TRUE);
+ gtk_widget_set_size_request(count_sb, 80, -1);
+ gtk_box_pack_start (GTK_BOX(count_hb), count_sb, FALSE, FALSE, 0);
+ gtk_widget_show(count_sb);
+
+ count_lb = gtk_label_new("packet(s) captured");
+ gtk_misc_set_alignment(GTK_MISC(count_lb), 0, 0.5);
+ gtk_box_pack_start(GTK_BOX(count_hb), count_lb, FALSE, FALSE, 0);
+ gtk_widget_show(count_lb);
+
+ /* Filesize row */
+ filesize_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(limit_vb), filesize_hb);
+ gtk_widget_show(filesize_hb);
+
+ filesize_cb = gtk_check_button_new_with_label("");
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(filesize_cb),
+ capture_opts.has_autostop_filesize);
+ g_signal_connect(G_OBJECT(filesize_cb), "toggled",
+ G_CALLBACK(capture_prep_adjust_sensitivity),
+ GTK_OBJECT(cap_open_w));
+ gtk_box_pack_start(GTK_BOX(filesize_hb), filesize_cb, FALSE, FALSE, 0);
+ gtk_widget_show(filesize_cb);
+
+ filesize_adj = (GtkAdjustment *) gtk_adjustment_new(capture_opts.autostop_filesize,
+ 1, INT_MAX, 1.0, 10.0, 0.0);
+ filesize_sb = gtk_spin_button_new (filesize_adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (filesize_sb), TRUE);
+ gtk_widget_set_size_request(filesize_sb, 80, -1);
+ gtk_box_pack_start (GTK_BOX(filesize_hb), filesize_sb, FALSE, FALSE, 0);
+ gtk_widget_show(filesize_sb);
+
+ filesize_lb = gtk_label_new("");
+ gtk_misc_set_alignment(GTK_MISC(filesize_lb), 0, 0.5);
+ gtk_box_pack_start(GTK_BOX(filesize_hb), filesize_lb, FALSE, FALSE, 0);
+ gtk_widget_show(filesize_lb);
+
+ /* Duration row */
+ duration_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(limit_vb), duration_hb);
+ gtk_widget_show(duration_hb);
+
+ duration_cb = gtk_check_button_new_with_label("Stop capture after");
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(duration_cb),
+ capture_opts.has_autostop_duration);
+ g_signal_connect(G_OBJECT(duration_cb), "toggled",
+ G_CALLBACK(capture_prep_adjust_sensitivity),
+ GTK_OBJECT(cap_open_w));
+ gtk_box_pack_start(GTK_BOX(duration_hb), duration_cb, FALSE, FALSE, 0);
+ gtk_widget_show(duration_cb);
+
+ duration_adj = (GtkAdjustment *) gtk_adjustment_new(capture_opts.autostop_duration,
+ 1, INT_MAX, 1.0, 10.0, 0.0);
+ duration_sb = gtk_spin_button_new (duration_adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (duration_sb), TRUE);
+ gtk_widget_set_size_request(duration_sb, 80, -1);
+ gtk_box_pack_start (GTK_BOX(duration_hb), duration_sb, FALSE, FALSE, 0);
+ gtk_widget_show(duration_sb);
+
+ duration_lb = gtk_label_new("second(s)");
+ gtk_misc_set_alignment(GTK_MISC(duration_lb), 0, 0.5);
+ gtk_box_pack_start(GTK_BOX(duration_hb), duration_lb, FALSE, FALSE, 0);
+ gtk_widget_show(duration_lb);
+
+ /* Resolution options frame */
+ resolv_fr = gtk_frame_new("Name resolution");
+ gtk_container_add(GTK_CONTAINER(main_vb), resolv_fr);
+ gtk_widget_show(resolv_fr);
+
+ resolv_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(resolv_fr), resolv_vb);
+ gtk_widget_show(resolv_vb);
+
+ m_resolv_cb = dlg_check_button_new_with_label_with_mnemonic(
+ "Enable _MAC name resolution", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(m_resolv_cb),
+ g_resolv_flags & RESOLV_MAC);
+ gtk_container_add(GTK_CONTAINER(resolv_vb), m_resolv_cb);
+ gtk_widget_show(m_resolv_cb);
+
+ n_resolv_cb = dlg_check_button_new_with_label_with_mnemonic(
+ "Enable _network name resolution", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(n_resolv_cb),
+ g_resolv_flags & RESOLV_NETWORK);
+ gtk_container_add(GTK_CONTAINER(resolv_vb), n_resolv_cb);
+ gtk_widget_show(n_resolv_cb);
+
+ t_resolv_cb = dlg_check_button_new_with_label_with_mnemonic(
+ "Enable _transport name resolution", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(t_resolv_cb),
+ g_resolv_flags & RESOLV_TRANSPORT);
+ gtk_container_add(GTK_CONTAINER(resolv_vb), t_resolv_cb);
+ gtk_widget_show(t_resolv_cb);
+
+ /* Button row: OK and cancel buttons */
+ 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(main_vb), bbox);
+ gtk_widget_show(bbox);
+
+ ok_bt = gtk_button_new_from_stock (GTK_STOCK_OK);
+ g_signal_connect(G_OBJECT(ok_bt), "clicked",
+ G_CALLBACK(capture_prep_ok_cb), GTK_OBJECT(cap_open_w));
+ GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
+ gtk_widget_grab_default(ok_bt);
+ gtk_widget_show(ok_bt);
+
+ cancel_bt = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+ g_signal_connect(G_OBJECT(cancel_bt), "clicked",
+ G_CALLBACK(capture_prep_close_cb), GTK_OBJECT(cap_open_w));
+ GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
+ gtk_widget_show(cancel_bt);
+
+ /* Attach pointers to needed widgets to the capture prefs window/object */
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_IFACE_KEY, if_cb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_SNAP_CB_KEY, snap_cb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_SNAP_SB_KEY, snap_sb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_PROMISC_KEY, promisc_cb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_FILT_KEY, filter_te);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_FILE_TE_KEY, file_te);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_RING_ON_TB_KEY, ringbuffer_on_tb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_RING_NBF_LB_KEY, ringbuffer_nbf_lb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_RING_NBF_SB_KEY, ringbuffer_nbf_sb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_SYNC_KEY, sync_cb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_AUTO_SCROLL_KEY, auto_scroll_cb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_COUNT_CB_KEY, count_cb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_COUNT_SB_KEY, count_sb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_FILESIZE_CB_KEY, filesize_cb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_FILESIZE_SB_KEY, filesize_sb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_FILESIZE_LB_KEY, filesize_lb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_DURATION_CB_KEY, duration_cb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_DURATION_SB_KEY, duration_sb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_M_RESOLVE_KEY, m_resolv_cb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_N_RESOLVE_KEY, n_resolv_cb);
+ gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_T_RESOLVE_KEY, t_resolv_cb);
+
+ /* Set the sensitivity of various widgets as per the settings of other
+ widgets. */
+ capture_prep_adjust_sensitivity(NULL, cap_open_w);
+
+ /* Catch the "activate" signal on the frame number and file name text
+ entries, so that if the user types Return there, we act as if the
+ "OK" button had been selected, as happens if Return is typed if some
+ widget that *doesn't* handle the Return key has the input focus. */
+ dlg_set_activate(filter_te, ok_bt);
+ dlg_set_activate(file_te, ok_bt);
+
+ /* 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(cap_open_w, cancel_bt);
+
+ /* XXX - why does not
+
+ gtk_widget_grab_focus(if_cb);
+
+ give the initial focus to the "Interface" combo box?
+
+ Or should I phrase that as "why does GTK+ continually frustrate
+ attempts to make GUIs driveable from the keyboard?" We have to
+ go catch the activate signal on every single GtkEntry widget
+ (rather than having widgets whose activate signal is *not*
+ caught not catch the Return keystroke, so that it passes on,
+ ultimately, to the window, which can activate the default
+ widget, i.e. the "OK" button); we have to catch the "key_press_event"
+ signal and have the handler check for ESC, so that we can have ESC
+ activate the "Cancel" button; in order to support Alt+<key> mnemonics
+ for buttons and the like, we may have to construct an accelerator
+ group by hand and set up the accelerators by hand (if that even
+ works - I've not tried it yet); we have to do a "gtk_widget_grab_focus()"
+ to keep some container widget from getting the initial focus, so that
+ you don't have to tab into the first widget in order to start typing
+ in it; and it now appears that you simply *can't* make a combo box
+ get the initial focus, at least not in the obvious fashion. Sigh.... */
+
+ gtk_widget_show(cap_open_w);
+}
+
+static void
+capture_prep_file_cb(GtkWidget *w, gpointer file_te)
+{
+ GtkWidget *caller = gtk_widget_get_toplevel(w);
+ GtkWidget *fs;
+
+ /* Has a file selection dialog box already been opened for that top-level
+ widget? */
+ fs = gtk_object_get_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY);
+
+ if (fs != NULL) {
+ /* Yes. Just re-activate that dialog box. */
+ reactivate_window(fs);
+ return;
+ }
+
+ fs = gtk_file_selection_new ("Ethereal: Capture File");
+
+ gtk_object_set_data(GTK_OBJECT(fs), E_CAP_FILE_TE_KEY, file_te);
+
+ /* Set the E_FS_CALLER_PTR_KEY for the new dialog to point to our caller. */
+ gtk_object_set_data(GTK_OBJECT(fs), E_FS_CALLER_PTR_KEY, caller);
+
+ /* Set the E_FILE_SEL_DIALOG_PTR_KEY for the caller to point to us */
+ gtk_object_set_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY, fs);
+
+ /* Call a handler when the file selection box is destroyed, so we can inform
+ our caller, if any, that it's been destroyed. */
+ g_signal_connect(G_OBJECT(fs), "destroy",
+ G_CALLBACK(cap_prep_fs_destroy_cb), NULL);
+
+ g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), "clicked",
+ G_CALLBACK(cap_prep_fs_ok_cb), fs);
+
+ /* Connect the cancel_button to destroy the widget */
+ g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(fs)->cancel_button), "clicked",
+ G_CALLBACK(cap_prep_fs_cancel_cb), fs);
+
+ /* 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(fs, GTK_FILE_SELECTION(fs)->cancel_button);
+
+ gtk_widget_show(fs);
+}
+
+static void
+cap_prep_fs_ok_cb(GtkWidget *w _U_, gpointer data)
+{
+ gtk_entry_set_text(GTK_ENTRY(gtk_object_get_data(GTK_OBJECT(data),
+ E_CAP_FILE_TE_KEY)),
+ gtk_file_selection_get_filename (GTK_FILE_SELECTION(data)));
+ gtk_widget_destroy(GTK_WIDGET(data));
+}
+
+static void
+cap_prep_fs_cancel_cb(GtkWidget *w _U_, gpointer data)
+{
+ gtk_widget_destroy(GTK_WIDGET(data));
+}
+
+static void
+cap_prep_fs_destroy_cb(GtkWidget *win, gpointer data _U_)
+{
+ GtkWidget *caller;
+
+ /* Get the widget that requested that we be popped up.
+ (It should arrange to destroy us if it's destroyed, so
+ that we don't get a pointer to a non-existent window here.) */
+ caller = gtk_object_get_data(GTK_OBJECT(win), E_FS_CALLER_PTR_KEY);
+
+ /* Tell it we no longer exist. */
+ gtk_object_set_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY, NULL);
+
+ /* Now nuke this window. */
+ gtk_grab_remove(GTK_WIDGET(win));
+ gtk_widget_destroy(GTK_WIDGET(win));
+}
+
+static void
+capture_prep_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w) {
+ GtkWidget *if_cb, *snap_cb, *snap_sb, *promisc_cb, *filter_te,
+ *file_te, *ringbuffer_on_tb, *ringbuffer_nbf_sb,
+ *sync_cb, *auto_scroll_cb,
+ *count_cb, *count_sb,
+ *filesize_cb, *filesize_sb,
+ *duration_cb, *duration_sb,
+ *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
+ gchar *if_text;
+ gchar *if_name;
+ const gchar *filter_text;
+ const gchar *save_file;
+
+ if_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_IFACE_KEY);
+ snap_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SNAP_CB_KEY);
+ snap_sb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SNAP_SB_KEY);
+ promisc_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_PROMISC_KEY);
+ filter_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILT_KEY);
+ file_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILE_TE_KEY);
+ ringbuffer_on_tb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_RING_ON_TB_KEY);
+ ringbuffer_nbf_sb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_RING_NBF_SB_KEY);
+ sync_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SYNC_KEY);
+ auto_scroll_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_AUTO_SCROLL_KEY);
+ count_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_COUNT_CB_KEY);
+ count_sb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_COUNT_SB_KEY);
+ filesize_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILESIZE_CB_KEY);
+ filesize_sb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILESIZE_SB_KEY);
+ duration_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_DURATION_CB_KEY);
+ duration_sb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_DURATION_SB_KEY);
+ m_resolv_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_M_RESOLVE_KEY);
+ n_resolv_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_N_RESOLVE_KEY);
+ t_resolv_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_T_RESOLVE_KEY);
+
+ if_text =
+ g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry)));
+ /* Windows combo entries have a description followed by the interface name */
+ if_name = strrchr(if_text, ' ');
+ if (if_name == NULL) {
+ if_name = if_text;
+ } else {
+ if_name++;
+ }
+ if (if_name == NULL) {
+ simple_dialog(ESD_TYPE_CRIT, NULL,
+ "You didn't specify an interface on which to capture packets.");
+ g_free(if_text);
+ return;
+ }
+ if (cfile.iface)
+ g_free(cfile.iface);
+ cfile.iface = g_strdup(if_name);
+ g_free(if_text);
+
+ capture_opts.has_snaplen =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(snap_cb));
+ if (capture_opts.has_snaplen) {
+ capture_opts.snaplen =
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(snap_sb));
+ if (capture_opts.snaplen < 1)
+ capture_opts.snaplen = WTAP_MAX_PACKET_SIZE;
+ else if (capture_opts.snaplen < MIN_PACKET_SIZE)
+ capture_opts.snaplen = MIN_PACKET_SIZE;
+ }
+
+ capture_opts.promisc_mode =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(promisc_cb));
+
+ /* XXX - don't try to get clever and set "cfile.filter" to NULL if the
+ filter string is empty, as an indication that we don't have a filter
+ and thus don't have to set a filter when capturing - the version of
+ libpcap in Red Hat Linux 6.1, and versions based on later patches
+ in that series, don't bind the AF_PACKET socket to an interface
+ until a filter is set, which means they aren't bound at all if
+ no filter is set, which means no packets arrive as input on that
+ socket, which means Ethereal never sees any packets. */
+ filter_text = gtk_entry_get_text(GTK_ENTRY(filter_te));
+ if (cfile.cfilter)
+ g_free(cfile.cfilter);
+ g_assert(filter_text != NULL);
+ cfile.cfilter = g_strdup(filter_text);
+
+ save_file = gtk_entry_get_text(GTK_ENTRY(file_te));
+ if (save_file && save_file[0]) {
+ /* User specified a file to which the capture should be written. */
+ save_file = g_strdup(save_file);
+ } else {
+ /* User didn't specify a file; save to a temporary file. */
+ save_file = NULL;
+ }
+
+ capture_opts.has_autostop_count =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(count_cb));
+ if (capture_opts.has_autostop_count)
+ capture_opts.autostop_count =
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(count_sb));
+
+ capture_opts.has_autostop_filesize =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(filesize_cb));
+ if (capture_opts.has_autostop_filesize)
+ capture_opts.autostop_filesize =
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(filesize_sb));
+
+ capture_opts.has_autostop_duration =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(duration_cb));
+ if (capture_opts.has_autostop_duration)
+ capture_opts.autostop_duration =
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(duration_sb));
+
+ capture_opts.sync_mode =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sync_cb));
+
+ auto_scroll_live =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(auto_scroll_cb));
+
+ g_resolv_flags = RESOLV_NONE;
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_resolv_cb)))
+ g_resolv_flags |= RESOLV_MAC;
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(n_resolv_cb)))
+ g_resolv_flags |= RESOLV_NETWORK;
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(t_resolv_cb)))
+ g_resolv_flags |= RESOLV_TRANSPORT;
+
+ capture_opts.ringbuffer_on =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ringbuffer_on_tb)) &&
+ !(capture_opts.sync_mode);
+ if (capture_opts.ringbuffer_on) {
+ if (save_file == NULL) {
+ simple_dialog(ESD_TYPE_CRIT, NULL,
+ "You must specify a save file if you want to use the ring buffer.");
+ return;
+ } else if (!capture_opts.has_autostop_filesize) {
+ simple_dialog(ESD_TYPE_CRIT, NULL,
+ "You must specify a file size at which to rotate the capture files\n"
+ "if you want to use the ring buffer.");
+ return;
+ }
+ }
+
+ capture_opts.ringbuffer_num_files =
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(ringbuffer_nbf_sb));
+ if (capture_opts.ringbuffer_num_files < RINGBUFFER_MIN_NUM_FILES)
+ capture_opts.ringbuffer_num_files = RINGBUFFER_MIN_NUM_FILES;
+ else if (capture_opts.ringbuffer_num_files > RINGBUFFER_MAX_NUM_FILES)
+ capture_opts.ringbuffer_num_files = RINGBUFFER_MAX_NUM_FILES;
+
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+
+ do_capture(save_file);
+}
+
+static void
+capture_prep_close_cb(GtkWidget *close_bt _U_, gpointer parent_w)
+{
+ gtk_grab_remove(GTK_WIDGET(parent_w));
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+capture_prep_destroy_cb(GtkWidget *win, gpointer user_data _U_)
+{
+ GtkWidget *capture_prep_filter_w;
+ GtkWidget *fs;
+
+ /* Is there a filter edit/selection dialog associated with this
+ Capture Options dialog? */
+ capture_prep_filter_w = gtk_object_get_data(GTK_OBJECT(win), E_FILT_DIALOG_PTR_KEY);
+
+ if (capture_prep_filter_w != NULL) {
+ /* Yes. Destroy it. */
+ gtk_widget_destroy(capture_prep_filter_w);
+ }
+
+ /* Is there a file selection dialog associated with this
+ Print File dialog? */
+ fs = gtk_object_get_data(GTK_OBJECT(win), E_FILE_SEL_DIALOG_PTR_KEY);
+
+ if (fs != NULL) {
+ /* Yes. Destroy it. */
+ gtk_widget_destroy(fs);
+ }
+
+ /* Note that we no longer have a "Capture Options" dialog box. */
+ cap_open_w = NULL;
+}
+
+/*
+ * Adjust the sensitivity of various widgets as per the current setting
+ * of other widgets.
+ */
+static void
+capture_prep_adjust_sensitivity(GtkWidget *tb _U_, gpointer parent_w)
+{
+ GtkWidget *snap_cb, *snap_sb,
+ *ringbuffer_on_tb, *ringbuffer_nbf_lb, *ringbuffer_nbf_sb,
+ *sync_cb, *auto_scroll_cb,
+ *count_cb, *count_sb,
+ *filesize_cb, *filesize_sb, *filesize_lb,
+ *duration_cb, *duration_sb;
+
+ snap_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SNAP_CB_KEY);
+ snap_sb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SNAP_SB_KEY);
+ ringbuffer_on_tb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_RING_ON_TB_KEY);
+ ringbuffer_nbf_lb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_RING_NBF_LB_KEY);
+ ringbuffer_nbf_sb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_RING_NBF_SB_KEY);
+ sync_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SYNC_KEY);
+ auto_scroll_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_AUTO_SCROLL_KEY);
+ count_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_COUNT_CB_KEY);
+ count_sb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_COUNT_SB_KEY);
+ filesize_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILESIZE_CB_KEY);
+ filesize_sb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILESIZE_SB_KEY);
+ filesize_lb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILESIZE_LB_KEY);
+ duration_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_DURATION_CB_KEY);
+ duration_sb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_DURATION_SB_KEY);
+
+ /* The snapshot length spinbox is sensitive iff the "Limit each packet
+ to" checkbox is on. */
+ gtk_widget_set_sensitive(GTK_WIDGET(snap_sb),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(snap_cb)));
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sync_cb))) {
+ /* "Update list of packets in real time" captures enabled; we don't
+ support ring buffer mode for those captures, so turn ring buffer
+ mode off if it's on, and make its toggle button, and the spin
+ button for the number of ring buffer files (and the spin button's
+ label), insensitive. */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ringbuffer_on_tb))) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ringbuffer_on_tb), FALSE);
+ }
+ gtk_widget_set_sensitive(GTK_WIDGET(ringbuffer_on_tb), FALSE);
+
+ /* Auto-scroll mode is meaningful only in "Update list of packets
+ in real time" captures, so make its toggle button sensitive. */
+ gtk_widget_set_sensitive(GTK_WIDGET(auto_scroll_cb), TRUE);
+ } else {
+ /* "Update list of packets in real time" captures disabled; that
+ means ring buffer mode is OK, so make its toggle button
+ sensitive. */
+ gtk_widget_set_sensitive(GTK_WIDGET(ringbuffer_on_tb), TRUE);
+
+ /* Auto-scroll mode is meaningful only in "Update list of packets
+ in real time" captures, so make its toggle button insensitive. */
+ gtk_widget_set_sensitive(GTK_WIDGET(auto_scroll_cb), FALSE);
+ }
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ringbuffer_on_tb))) {
+ /* Ring buffer mode enabled. Make the spin button for the number
+ of ring buffer files, and its label, sensitive. */
+ gtk_widget_set_sensitive(GTK_WIDGET(ringbuffer_nbf_lb), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ringbuffer_nbf_sb), TRUE);
+
+ /* Also, indicate that the file size is a size at which to switch
+ ring buffer files, not a size at which to stop the capture,
+ turn its button on. */
+ gtk_label_set_text(GTK_LABEL(GTK_BIN(filesize_cb)->child),
+ "Rotate capture file every");
+ gtk_label_set_text(GTK_LABEL(filesize_lb), "kilobyte(s)");
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(filesize_cb), TRUE);
+ } else {
+ /* Ring buffer mode disabled. Make the spin button for the number
+ of ring buffer files, and its label insensitive. */
+ gtk_widget_set_sensitive(GTK_WIDGET(ringbuffer_nbf_lb), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ringbuffer_nbf_sb), FALSE);
+
+ /* Also, indicate that the file size is a size at which to stop the
+ capture, not a size at which to switch ring buffer files. */
+ gtk_label_set_text(GTK_LABEL(GTK_BIN(filesize_cb)->child),
+ "Stop capture after");
+ gtk_label_set_text(GTK_LABEL(filesize_lb), "kilobyte(s) captured");
+ }
+
+ /* The maximum packet count spinbox is sensitive iff the "Stop capture
+ after N packets captured" checkbox is on. */
+ gtk_widget_set_sensitive(GTK_WIDGET(count_sb),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(count_cb)));
+
+ /* The maximum file size spinbox is sensitive iff the "Stop capture
+ after N kilobytes captured" checkbox is on. */
+ gtk_widget_set_sensitive(GTK_WIDGET(filesize_sb),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(filesize_cb)));
+
+ /* The capture duration spinbox is sensitive iff the "Stop capture
+ after N seconds" checkbox is on. */
+ gtk_widget_set_sensitive(GTK_WIDGET(duration_sb),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(duration_cb)));
+}
+
+#endif /* HAVE_LIBPCAP */
diff --git a/gtk2/capture_dlg.h b/gtk2/capture_dlg.h
new file mode 100644
index 0000000000..5198244630
--- /dev/null
+++ b/gtk2/capture_dlg.h
@@ -0,0 +1,32 @@
+/* capture_dlg.h
+ * Definitions for packet capture windows
+ *
+ * $Id: capture_dlg.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+#ifndef __CAPTURE_DLG_H__
+#define __CAPTURE_DLG_H__
+
+void capture_prep_cb(GtkWidget *, gpointer);
+void capture_stop_cb(GtkWidget *, gpointer);
+
+#endif /* capture.h */
diff --git a/gtk2/capture_prefs.c b/gtk2/capture_prefs.c
new file mode 100644
index 0000000000..fca8cd0527
--- /dev/null
+++ b/gtk2/capture_prefs.c
@@ -0,0 +1,167 @@
+/* capture_prefs.c
+ * Dialog box for capture preferences
+ *
+ * $Id: capture_prefs.c,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_LIBPCAP
+
+#include <string.h>
+#include <errno.h>
+#include <gtk/gtk.h>
+
+#include <pcap.h>
+
+#include "globals.h"
+#include "capture_prefs.h"
+#include "gtkglobals.h"
+#include "prefs.h"
+#include "prefs_dlg.h"
+#include "ui_util.h"
+#include "pcap-util.h"
+#include "main.h"
+
+#define DEVICE_KEY "device"
+#define PROM_MODE_KEY "prom_mode"
+#define CAPTURE_REAL_TIME_KEY "capture_real_time"
+#define AUTO_SCROLL_KEY "auto_scroll"
+
+#define CAPTURE_TABLE_ROWS 4
+GtkWidget*
+capture_prefs_show(void)
+{
+ GtkWidget *main_tb, *main_vb;
+ GtkWidget *if_cb, *if_lb, *promisc_cb, *sync_cb, *auto_scroll_cb;
+ GList *if_list;
+ int err;
+ char err_str[PCAP_ERRBUF_SIZE];
+
+ /* Main vertical box */
+ main_vb = gtk_vbox_new(FALSE, 7);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+
+ /* Main table */
+ main_tb = gtk_table_new(CAPTURE_TABLE_ROWS, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
+ gtk_widget_show(main_tb);
+
+ /* Default device */
+ if_lb = gtk_label_new("Interface:");
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), if_lb, 0, 1, 0, 1);
+ gtk_misc_set_alignment(GTK_MISC(if_lb), 1.0, 0.5);
+ gtk_widget_show(if_lb);
+
+ if_cb = gtk_combo_new();
+ /*
+ * XXX - what if we can't get the list?
+ */
+ if_list = get_interface_list(&err, err_str);
+ if (if_list != NULL)
+ gtk_combo_set_popdown_strings(GTK_COMBO(if_cb), if_list);
+ if (prefs.capture_device)
+ gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry),
+ prefs.capture_device);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), if_cb, 1, 2, 0, 1);
+ gtk_widget_show(if_cb);
+ gtk_object_set_data(GTK_OBJECT(main_vb), DEVICE_KEY, if_cb);
+
+ free_interface_list(if_list);
+
+ /* Promiscuous mode */
+ promisc_cb = create_preference_check_button(main_tb, 1,
+ "Capture packets in promiscuous mode:", NULL,
+ prefs.capture_prom_mode);
+ gtk_object_set_data(GTK_OBJECT(main_vb), PROM_MODE_KEY, promisc_cb);
+
+ /* Real-time capture */
+ sync_cb = create_preference_check_button(main_tb, 2,
+ "Update list of packets in real time:", NULL,
+ prefs.capture_real_time);
+ gtk_object_set_data(GTK_OBJECT(main_vb), CAPTURE_REAL_TIME_KEY,
+ sync_cb);
+
+ /* Auto-scroll real-time capture */
+ auto_scroll_cb = create_preference_check_button(main_tb, 3,
+ "Automatic scrolling in live capture:", NULL,
+ prefs.capture_auto_scroll);
+ gtk_object_set_data(GTK_OBJECT(main_vb), AUTO_SCROLL_KEY,
+ auto_scroll_cb);
+
+ /* Show 'em what we got */
+ gtk_widget_show_all(main_vb);
+
+ return(main_vb);
+}
+
+void
+capture_prefs_fetch(GtkWidget *w)
+{
+ GtkWidget *if_cb, *promisc_cb, *sync_cb, *auto_scroll_cb;
+ gchar *if_text;
+
+ if_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(w), DEVICE_KEY);
+ promisc_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(w),
+ PROM_MODE_KEY);
+ sync_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(w),
+ CAPTURE_REAL_TIME_KEY);
+ auto_scroll_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(w),
+ AUTO_SCROLL_KEY);
+
+ if (prefs.capture_device != NULL) {
+ g_free(prefs.capture_device);
+ prefs.capture_device = NULL;
+ }
+ if_text = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry)));
+ /* Strip out white space */
+ g_strstrip(if_text);
+ /* If there was nothing but white space, treat that as an
+ indication that the user doesn't want to wire in a default
+ device, and just wants the first device in the list chosen. */
+ if (*if_text == '\0') {
+ g_free(if_text);
+ if_text = NULL;
+ }
+ prefs.capture_device = if_text;
+
+ prefs.capture_prom_mode = GTK_TOGGLE_BUTTON (promisc_cb)->active;
+
+ prefs.capture_real_time = GTK_TOGGLE_BUTTON (sync_cb)->active;
+
+ prefs.capture_auto_scroll = GTK_TOGGLE_BUTTON (auto_scroll_cb)->active;
+}
+
+void
+capture_prefs_apply(GtkWidget *w _U_)
+{
+}
+
+void
+capture_prefs_destroy(GtkWidget *w _U_)
+{
+}
+
+#endif /* HAVE_LIBPCAP */
diff --git a/gtk2/capture_prefs.h b/gtk2/capture_prefs.h
new file mode 100644
index 0000000000..a9ccbd98ef
--- /dev/null
+++ b/gtk2/capture_prefs.h
@@ -0,0 +1,33 @@
+/* capture_prefs.h
+ * Definitions for capture preferences window
+ *
+ * $Id: capture_prefs.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifndef __CAPTURE_PREFS_H__
+#define __CAPTURE_PREFS_H__
+
+GtkWidget *capture_prefs_show(void);
+void capture_prefs_fetch(GtkWidget *w);
+void capture_prefs_apply(GtkWidget *w);
+void capture_prefs_destroy(GtkWidget *w);
+
+#endif
diff --git a/gtk2/color_dlg.c b/gtk2/color_dlg.c
new file mode 100644
index 0000000000..e763a1e323
--- /dev/null
+++ b/gtk2/color_dlg.c
@@ -0,0 +1,1166 @@
+/* color_dlg.c
+ * Definitions for dialog boxes for color filters
+ *
+ * $Id: color_dlg.c,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <errno.h>
+
+#include "gtk/main.h"
+#include <epan/packet.h>
+#include "colors.h"
+#include "color_dlg.h"
+#include "file.h"
+#include <epan/dfilter/dfilter.h>
+#include "simple_dialog.h"
+#include "dlg_utils.h"
+#include "ui_util.h"
+#include "dfilter_expr_dlg.h"
+
+
+static GtkWidget* colorize_dialog_new(void);
+static void add_filter_to_clist(gpointer filter_arg, gpointer clist_arg);
+static void color_filter_up_cb(GtkButton *button, gpointer user_data);
+static void color_filter_down_cb(GtkButton *button, gpointer user_data);
+static void remember_selected_row(GtkCList *clist, gint row, gint column,
+ GdkEvent *event, gpointer user_data);
+static void unremember_selected_row(GtkCList *clist, gint row, gint column,
+ GdkEvent *event, gpointer user_data);
+static void color_destroy_cb(GtkButton *button, gpointer user_data);
+static void destroy_edit_dialog_cb(gpointer filter_arg, gpointer dummy);
+static void color_new_cb(GtkButton *button, gpointer user_data);
+static void color_edit_cb(GtkButton *button, gpointer user_data);
+static void color_delete_cb(GtkWidget *widget, gpointer user_data);
+static void color_save_cb(GtkButton *button, gpointer user_data);
+static void color_ok_cb(GtkButton *button, gpointer user_data);
+static void color_cancel_cb(GtkWidget *widget, gpointer user_data);
+static void color_apply_cb(GtkButton *button, gpointer user_data);
+
+static void edit_color_filter_dialog_new(GtkWidget *color_filters,
+ GtkWidget **colorize_filter_name,
+ GtkWidget **colorize_filter_text);
+static void edit_color_filter_destroy_cb(GtkObject *object,
+ gpointer user_data);
+static void edit_color_filter_fg_cb(GtkButton *button, gpointer user_data);
+static void edit_color_filter_bg_cb(GtkButton *button, gpointer user_data);
+static void edit_color_filter_ok_cb(GtkButton *button, gpointer user_data);
+static void edit_color_filter_cancel_cb(GtkObject *object, gpointer user_data);
+
+static GtkWidget* color_sel_win_new(color_filter_t *colorf, gboolean);
+static void color_sel_ok_cb(GtkButton *button, gpointer user_data);
+static void color_sel_cancel_cb(GtkObject *object, gpointer user_data);
+
+static GtkWidget *colorize_win;
+static gint num_of_filters; /* number of filters being displayed */
+static gint row_selected; /* row in color_filters that is selected */
+
+static gchar *titles[2] = { "Name", "Filter String" };
+
+#define COLOR_UP_LB "color_up_lb"
+#define COLOR_DOWN_LB "color_down_lb"
+#define COLOR_EDIT_LB "color_edit_lb"
+#define COLOR_DELETE_LB "color_delete_lb"
+#define COLOR_FILTERS_CL "color_filters_cl"
+#define COLOR_FILTER "color_filter"
+#define COLOR_SELECTION_FG "color_selection_fg"
+#define COLOR_SELECTION_BG "color_selection_bg"
+#define COLOR_SELECTION_PARENT "color_selection_parent"
+
+static void
+filter_expr_cb(GtkWidget *w _U_, gpointer filter_te)
+{
+
+ dfilter_expr_dlg_new(GTK_WIDGET(filter_te));
+}
+
+
+/* Callback for the "Display:Colorize Display" menu item. */
+void
+color_display_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ if (colorize_win != NULL) {
+ /* There's already a color dialog box active; reactivate it. */
+ reactivate_window(colorize_win);
+ } else {
+ /* Create a new "Colorize Display" dialog. */
+ colorize_win = colorize_dialog_new();
+ }
+}
+
+/* Create the "Add color to protocols" dialog. */
+static GtkWidget*
+colorize_dialog_new (void)
+{
+ GtkWidget *color_win;
+ GtkWidget *vbox1;
+ GtkWidget *hbox1;
+ GtkWidget *vbox2;
+ GtkWidget *vbox4;
+ GtkWidget *color_filter_up;
+ GtkWidget *label4;
+ GtkWidget *color_filter_down;
+ GtkWidget *scrolledwindow1;
+ GtkWidget *color_filters;
+ GtkWidget *color_new;
+ GtkWidget *color_edit;
+ GtkWidget *color_delete;
+ GtkWidget *color_save;
+ GtkWidget *hbox3;
+ GtkWidget *color_ok;
+ GtkWidget *color_apply;
+ GtkWidget *color_cancel;
+ GtkTooltips *tooltips;
+
+ row_selected = -1; /* no row selected */
+ tooltips = gtk_tooltips_new ();
+
+ color_win = dlg_window_new ("Add color to protocols");
+ gtk_object_set_data (GTK_OBJECT (color_win), "color_win", color_win);
+
+ vbox1 = gtk_vbox_new (FALSE, 0);
+ gtk_widget_ref (vbox1);
+ gtk_object_set_data_full (GTK_OBJECT (color_win), "vbox1", vbox1,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (vbox1);
+ gtk_container_add (GTK_CONTAINER (color_win), vbox1);
+
+ hbox1 = gtk_hbox_new (FALSE, 0);
+ gtk_widget_ref (hbox1);
+ gtk_object_set_data_full (GTK_OBJECT (color_win), "hbox1", hbox1,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (hbox1);
+ gtk_box_pack_start (GTK_BOX (vbox1), hbox1, TRUE, TRUE, 0);
+
+ /* vbox2 holds the Up and Down Buttons and label */
+ vbox2 = gtk_vbox_new (TRUE, 0);
+ gtk_widget_ref (vbox2);
+ gtk_object_set_data_full (GTK_OBJECT (color_win), "vbox2", vbox2,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (vbox2);
+ gtk_box_pack_start (GTK_BOX (hbox1), vbox2, FALSE, TRUE, 0);
+ gtk_widget_set_usize (vbox2, 150, -2);
+
+ color_filter_up = gtk_button_new_with_label (("Up"));
+ gtk_widget_ref (color_filter_up);
+ gtk_object_set_data_full (GTK_OBJECT (color_win), "color_filter_up", color_filter_up,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (color_filter_up);
+ gtk_box_pack_start (GTK_BOX (vbox2), color_filter_up, FALSE, FALSE, 0);
+ gtk_tooltips_set_tip (tooltips, color_filter_up, ("Move filter higher in list"), NULL);
+ gtk_widget_set_sensitive (color_filter_up, FALSE);
+
+ label4 = gtk_label_new (("Move filter\nup or down\n[List is processed \n"
+ "in order until\nmatch is found]"));
+ gtk_widget_ref (label4);
+ gtk_object_set_data_full (GTK_OBJECT (color_win), "label4", label4,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (label4);
+ gtk_box_pack_start (GTK_BOX (vbox2), label4, FALSE, FALSE, 0);
+
+ color_filter_down = gtk_button_new_with_label (("Down"));
+ gtk_widget_ref (color_filter_down);
+ gtk_object_set_data_full (GTK_OBJECT (color_win), "color_filter_down", color_filter_down,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (color_filter_down);
+ gtk_box_pack_start (GTK_BOX (vbox2), color_filter_down, FALSE, FALSE, 0);
+ gtk_tooltips_set_tip (tooltips, color_filter_down, ("Move filter lower in list"), NULL);
+ gtk_widget_set_sensitive (color_filter_down, FALSE);
+ /* End vbox2 */
+
+ /* create the list of filters */
+ scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_ref (scrolledwindow1);
+ gtk_object_set_data_full (GTK_OBJECT (color_win), "scrolledwindow1", scrolledwindow1,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (scrolledwindow1);
+ gtk_box_pack_start (GTK_BOX (hbox1), scrolledwindow1, TRUE, TRUE, 0);
+
+ color_filters = gtk_clist_new_with_titles(2, titles);
+
+ num_of_filters = 0;
+ g_slist_foreach(filter_list, add_filter_to_clist, color_filters);
+
+ gtk_widget_show (color_filters);
+ gtk_container_add (GTK_CONTAINER (scrolledwindow1), color_filters);
+ gtk_widget_set_usize (color_filters, 300, -2);
+ gtk_clist_set_column_width (GTK_CLIST (color_filters), 0, 80);
+ gtk_clist_set_column_width (GTK_CLIST (color_filters), 1, 80);
+ gtk_clist_column_titles_show (GTK_CLIST (color_filters));
+ /* end list of filters */
+
+ /* vbox4 is first button column */
+ vbox4 = gtk_vbox_new (FALSE, 0);
+ gtk_widget_ref (vbox4);
+ gtk_object_set_data_full (GTK_OBJECT (color_win), "vbox4", vbox4,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (vbox4);
+ gtk_box_pack_start (GTK_BOX (hbox1), vbox4, TRUE, FALSE, 5);
+#if 0
+ gtk_widget_set_usize (vbox4, -2, 30);
+#endif
+
+ color_new = gtk_button_new_with_label (("New"));
+ gtk_widget_ref (color_new);
+ gtk_object_set_data_full (GTK_OBJECT (color_win), "color_new", color_new,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (color_new);
+ gtk_box_pack_start (GTK_BOX (vbox4), color_new, FALSE, FALSE, 5);
+ gtk_widget_set_usize (color_new, 50, 20);
+ gtk_tooltips_set_tip (tooltips, color_new, ("Create a new colorization filter after selected filter"), NULL);
+
+ color_edit = gtk_button_new_with_label (("Edit"));
+ gtk_widget_ref (color_edit);
+ gtk_object_set_data_full (GTK_OBJECT (color_win), "color_edit", color_edit,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (color_edit);
+ gtk_widget_set_usize(color_edit, 50, 20);
+ gtk_box_pack_start (GTK_BOX (vbox4), color_edit, FALSE, FALSE, 5);
+ gtk_tooltips_set_tip (tooltips, color_edit, ("Change color of selected filter"), NULL);
+ gtk_widget_set_sensitive (color_edit, FALSE);
+
+ color_delete = gtk_button_new_with_label (("Delete"));
+ gtk_widget_ref (color_delete);
+ gtk_object_set_data_full (GTK_OBJECT (color_win), "color_delete", color_delete,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (color_delete);
+ gtk_box_pack_start (GTK_BOX (vbox4), color_delete, FALSE, FALSE, 5);
+ gtk_widget_set_usize (color_delete, 50, 20);
+ gtk_tooltips_set_tip (tooltips, color_delete, ("Delete selected colorization filter"), NULL);
+ gtk_widget_set_sensitive (color_delete, FALSE);
+
+ color_save = gtk_button_new_with_label (("Save"));
+ gtk_widget_ref (color_save);
+ gtk_object_set_data_full (GTK_OBJECT (color_win), "color_save", color_save,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (color_save);
+ gtk_box_pack_start (GTK_BOX (vbox4), color_save, FALSE, FALSE, 5);
+ gtk_widget_set_usize (color_save, 50, 20);
+ gtk_tooltips_set_tip (tooltips, color_save, ("Save all filters to disk"), NULL);
+
+ /* hbox3 is bottom button row */
+ hbox3 = gtk_hbox_new (FALSE, 0);
+ gtk_widget_ref (hbox3);
+ gtk_object_set_data_full (GTK_OBJECT (color_win), "hbox3", hbox3,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (hbox3);
+ gtk_box_pack_start (GTK_BOX (vbox1), hbox3, TRUE, FALSE, 5);
+ gtk_widget_set_usize (hbox3, 177, 30);
+
+ color_ok = gtk_button_new_with_label (("OK"));
+ gtk_widget_ref (color_ok);
+ gtk_object_set_data_full (GTK_OBJECT (color_win), "color_ok", color_ok,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (color_ok);
+ gtk_box_pack_start (GTK_BOX (hbox3), color_ok, TRUE, FALSE, 0);
+ gtk_widget_set_usize (color_ok, 50, 20);
+ gtk_tooltips_set_tip (tooltips, color_ok, ("Accept filter list; apply changes"), NULL);
+
+ color_apply = gtk_button_new_with_label (("Apply"));
+ gtk_widget_ref (color_apply);
+ gtk_object_set_data_full (GTK_OBJECT (color_win), "color_apply", color_apply,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (color_apply);
+ gtk_box_pack_start (GTK_BOX (hbox3), color_apply, TRUE, FALSE, 0);
+ gtk_widget_set_usize (color_apply, 50, 20);
+ gtk_tooltips_set_tip (tooltips, color_apply, ("Apply filters in list"), NULL);
+
+ color_cancel = gtk_button_new_with_label (("Cancel"));
+ gtk_widget_ref (color_cancel);
+ gtk_object_set_data_full (GTK_OBJECT (color_win), "color_cancel", color_cancel,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (color_cancel);
+ gtk_box_pack_start (GTK_BOX (hbox3), color_cancel, TRUE, FALSE, 0);
+ gtk_widget_set_usize (color_cancel, 50, 20);
+ gtk_tooltips_set_tip (tooltips, color_cancel, ("No more filter changes; don't apply"), NULL);
+
+ gtk_signal_connect (GTK_OBJECT (color_win), "destroy",
+ GTK_SIGNAL_FUNC (color_destroy_cb),
+ NULL);
+ gtk_object_set_data(GTK_OBJECT (color_filter_up), COLOR_FILTERS_CL,
+ color_filters);
+ gtk_signal_connect (GTK_OBJECT (color_filter_up), "clicked",
+ GTK_SIGNAL_FUNC (color_filter_up_cb),
+ NULL);
+ gtk_object_set_data(GTK_OBJECT (color_filter_down), COLOR_FILTERS_CL,
+ color_filters);
+ gtk_signal_connect (GTK_OBJECT (color_filter_down), "clicked",
+ GTK_SIGNAL_FUNC (color_filter_down_cb),
+ NULL);
+ gtk_signal_connect (GTK_OBJECT (color_filters), "select_row",
+ GTK_SIGNAL_FUNC (remember_selected_row),
+ NULL);
+ gtk_signal_connect (GTK_OBJECT (color_filters), "unselect_row",
+ GTK_SIGNAL_FUNC (unremember_selected_row),
+ NULL);
+ gtk_object_set_data(GTK_OBJECT (color_filters), COLOR_UP_LB,
+ color_filter_up);
+ gtk_object_set_data(GTK_OBJECT (color_filters), COLOR_DOWN_LB,
+ color_filter_down);
+ gtk_object_set_data(GTK_OBJECT (color_filters), COLOR_EDIT_LB,
+ color_edit);
+ gtk_object_set_data(GTK_OBJECT (color_filters), COLOR_DELETE_LB,
+ color_delete);
+ gtk_object_set_data(GTK_OBJECT (color_new), COLOR_FILTERS_CL,
+ color_filters);
+ gtk_signal_connect (GTK_OBJECT (color_new), "clicked",
+ GTK_SIGNAL_FUNC (color_new_cb),
+ NULL);
+ gtk_object_set_data(GTK_OBJECT (color_edit), COLOR_FILTERS_CL,
+ color_filters);
+ gtk_signal_connect (GTK_OBJECT (color_edit), "clicked",
+ GTK_SIGNAL_FUNC (color_edit_cb),
+ NULL);
+ gtk_object_set_data(GTK_OBJECT (color_delete), COLOR_EDIT_LB,
+ color_edit);
+ gtk_object_set_data(GTK_OBJECT (color_delete), COLOR_FILTERS_CL,
+ color_filters);
+ gtk_signal_connect (GTK_OBJECT (color_delete), "clicked",
+ GTK_SIGNAL_FUNC (color_delete_cb),
+ NULL);
+ gtk_signal_connect (GTK_OBJECT (color_save), "clicked",
+ GTK_SIGNAL_FUNC (color_save_cb),
+ NULL);
+ gtk_signal_connect (GTK_OBJECT (color_ok), "clicked",
+ GTK_SIGNAL_FUNC (color_ok_cb),
+ NULL);
+ gtk_signal_connect (GTK_OBJECT (color_apply), "clicked",
+ GTK_SIGNAL_FUNC (color_apply_cb),
+ NULL);
+ gtk_signal_connect (GTK_OBJECT (color_cancel), "clicked",
+ GTK_SIGNAL_FUNC (color_cancel_cb),
+ NULL);
+
+ gtk_widget_grab_focus (color_filters);
+ gtk_object_set_data (GTK_OBJECT (color_win), "tooltips", tooltips);
+ gtk_widget_show (color_win);
+
+ dlg_set_cancel(color_win, color_cancel);
+
+ return color_win;
+}
+
+static void
+add_filter_to_clist(gpointer filter_arg, gpointer clist_arg)
+{
+ color_filter_t *colorf = filter_arg;
+ GtkWidget *color_filters = clist_arg;
+ gchar *data[2];
+ gint row;
+
+ data[0] = colorf->filter_name;
+ data[1] = colorf->filter_text;
+ row = gtk_clist_append(GTK_CLIST(color_filters), data);
+ gtk_clist_set_row_data(GTK_CLIST(color_filters), row, colorf);
+ gtk_clist_set_foreground(GTK_CLIST(color_filters), row, &colorf->fg_color);
+ gtk_clist_set_background(GTK_CLIST(color_filters), row, &colorf->bg_color);
+ num_of_filters++;
+}
+
+/* Move the selected filter up in the list */
+static void
+color_filter_up_cb (GtkButton *button,
+ gpointer user_data _U_)
+{
+ gint filter_number;
+ GtkWidget *color_filters;
+ color_filter_t *colorf;
+
+ filter_number = row_selected;
+ g_assert(filter_number > 0);
+
+ color_filters = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(button),
+ COLOR_FILTERS_CL);
+ colorf = gtk_clist_get_row_data(GTK_CLIST(color_filters), filter_number);
+ gtk_clist_swap_rows(GTK_CLIST(color_filters), filter_number, filter_number-1);
+
+ /*
+ * That row is still selected, but it's now row N-1.
+ */
+ remember_selected_row(GTK_CLIST(color_filters), filter_number-1, 0, NULL,
+ NULL);
+
+ filter_list = g_slist_remove(filter_list, colorf);
+ filter_list = g_slist_insert(filter_list, colorf, filter_number-1);
+}
+
+/* Move the selected filter down in the list */
+static void
+color_filter_down_cb (GtkButton *button,
+ gpointer user_data _U_)
+{
+ gint filter_number;
+ GtkWidget *color_filters;
+ color_filter_t *colorf;
+
+ filter_number = row_selected;
+ g_assert(filter_number < num_of_filters - 1);
+
+ color_filters = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(button),
+ COLOR_FILTERS_CL);
+ colorf = gtk_clist_get_row_data(GTK_CLIST(color_filters), filter_number);
+ gtk_clist_swap_rows(GTK_CLIST(color_filters), filter_number+1, filter_number);
+
+ /*
+ * That row is still selected, but it's now row N+1.
+ */
+ remember_selected_row(GTK_CLIST(color_filters), filter_number+1, 0, NULL,
+ NULL);
+
+ filter_list = g_slist_remove(filter_list, colorf);
+ filter_list = g_slist_insert(filter_list, colorf, filter_number+1);
+}
+
+/* A row was selected; remember its row number */
+static void
+remember_selected_row (GtkCList *clist,
+ gint row,
+ gint column _U_,
+ GdkEvent *event _U_,
+ gpointer user_data _U_)
+{
+ GtkWidget *button;
+
+ row_selected = row;
+
+ /*
+ * A row is selected, so we can move it up *if* it's not at the top
+ * and move it down *if* it's not at the bottom.
+ */
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(clist),
+ COLOR_UP_LB);
+ gtk_widget_set_sensitive (button, row > 0);
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(clist),
+ COLOR_DOWN_LB);
+ gtk_widget_set_sensitive (button, row < num_of_filters - 1);
+
+ /*
+ * A row is selected, so we can operate on it.
+ */
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(clist),
+ COLOR_EDIT_LB);
+ gtk_widget_set_sensitive (button, TRUE);
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(clist),
+ COLOR_DELETE_LB);
+ gtk_widget_set_sensitive (button, TRUE);
+}
+
+/* A row was unselected; un-remember its row number */
+static void
+unremember_selected_row (GtkCList *clist,
+ gint row _U_,
+ gint column _U_,
+ GdkEvent *event _U_,
+ gpointer user_data _U_)
+{
+ GtkWidget *button;
+
+ row_selected = -1;
+
+ /*
+ * No row is selected, so we can't do operations that affect the
+ * selected row.
+ */
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(clist),
+ COLOR_UP_LB);
+ gtk_widget_set_sensitive (button, FALSE);
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(clist),
+ COLOR_DOWN_LB);
+ gtk_widget_set_sensitive (button, FALSE);
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(clist),
+ COLOR_EDIT_LB);
+ gtk_widget_set_sensitive (button, FALSE);
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(clist),
+ COLOR_DELETE_LB);
+ gtk_widget_set_sensitive (button, FALSE);
+}
+
+/* Called when the dialog box is being destroyed; destroy any edit
+ * dialogs opened from this dialog, and null out the pointer to this
+ * dialog.
+ jjj*/
+static void
+color_destroy_cb (GtkButton *button _U_,
+ gpointer user_data _U_)
+{
+ /* Destroy any edit dialogs we have open. */
+ g_slist_foreach(filter_list, destroy_edit_dialog_cb, NULL);
+
+ colorize_win = NULL;
+}
+
+static void
+destroy_edit_dialog_cb(gpointer filter_arg, gpointer dummy _U_)
+{
+ color_filter_t *colorf = (color_filter_t *)filter_arg;
+
+ if (colorf->edit_dialog != NULL)
+ gtk_widget_destroy(colorf->edit_dialog);
+}
+
+/* XXX - we don't forbid having more than one "Edit color filter" dialog
+ open, so these shouldn't be static. */
+static GtkWidget *filt_name_entry;
+static GtkWidget *filt_text_entry;
+
+/* Create a new filter in the list, and pop up an "Edit color filter"
+ dialog box to edit it. */
+static void
+color_new_cb (GtkButton *button,
+ gpointer user_data _U_)
+{
+ color_filter_t *colorf;
+ GtkWidget *color_filters;
+ gchar *data[2];
+ gint row;
+
+ colorf = new_color_filter("name", "filter"); /* Adds at end! */
+
+ color_filters = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(button),
+ COLOR_FILTERS_CL);
+ data[0] = colorf->filter_name;
+ data[1] = colorf->filter_text;
+ row = gtk_clist_append(GTK_CLIST(color_filters), data);
+ gtk_clist_set_row_data(GTK_CLIST(color_filters), row, colorf);
+ num_of_filters++;
+
+ /* select the new row */
+ gtk_clist_select_row(GTK_CLIST(color_filters), row, -1);
+ edit_color_filter_dialog_new(color_filters, &filt_name_entry,
+ &filt_text_entry);
+}
+
+/* Pop up an "Edit color filter" dialog box to edit an existing filter. */
+static void
+color_edit_cb (GtkButton *button,
+ gpointer user_data _U_)
+{
+ GtkWidget *color_filters;
+
+ color_filters = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(button),
+ COLOR_FILTERS_CL);
+ g_assert(row_selected != -1);
+ edit_color_filter_dialog_new(color_filters, &filt_name_entry,
+ &filt_text_entry);
+}
+
+/* Delete a color from the list. */
+static void
+color_delete_cb(GtkWidget *widget, gpointer user_data _U_)
+{
+ GtkWidget *color_filters;
+ color_filter_t *colorf;
+
+ if(row_selected != -1){
+ color_filters = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(widget),
+ COLOR_FILTERS_CL);
+ colorf = gtk_clist_get_row_data(GTK_CLIST(color_filters), row_selected);
+
+ /* Remove this color filter from the CList displaying the
+ color filters. */
+ gtk_clist_remove(GTK_CLIST(color_filters), row_selected);
+ num_of_filters--;
+
+ /* Destroy any "Edit color filter" dialog boxes editing it. */
+ if (colorf->edit_dialog != NULL)
+ gtk_widget_destroy(colorf->edit_dialog);
+
+ /* Remove the color filter from the list of color filters. */
+ delete_color_filter(colorf);
+
+ /* Select the previous row, if there is one. */
+ if (row_selected > 0) {
+ row_selected--;
+ gtk_clist_select_row(GTK_CLIST(color_filters), row_selected, 0);
+ }
+ }
+}
+
+/* Save color filters to the color filter file. */
+static void
+color_save_cb (GtkButton *button _U_,
+ gpointer user_data _U_)
+{
+ if (!write_filters())
+ simple_dialog(ESD_TYPE_CRIT, NULL, "Could not open filter file: %s",
+ strerror(errno));
+
+}
+
+/* Exit dialog and apply new list of color filters to the capture. */
+static void
+color_ok_cb (GtkButton *button _U_,
+ gpointer user_data _U_)
+{
+ /* colorize list */
+ colorize_packets(&cfile);
+
+ /* Destroy the dialog box. */
+ gtk_widget_destroy(colorize_win);
+}
+
+/* Exit dialog without colorizing packets with the new list.
+ XXX - should really undo any changes to the list.... */
+static void
+color_cancel_cb (GtkWidget *widget _U_,
+ gpointer user_data _U_)
+{
+ /* Destroy the dialog box. */
+ gtk_widget_destroy(colorize_win);
+}
+
+/* Apply new list of color filters to the capture. */
+static void
+color_apply_cb (GtkButton *button _U_,
+ gpointer user_data _U_)
+{
+ colorize_packets(&cfile);
+}
+
+/* Create an "Edit color filter" dialog for a given color filter, and
+ associate it with that color filter. */
+static void
+edit_color_filter_dialog_new (GtkWidget *color_filters,
+ GtkWidget **colorize_filter_name,
+ GtkWidget **colorize_filter_text)
+{
+ color_filter_t *colorf;
+ GtkWidget *edit_dialog;
+ GtkWidget *vbox3;
+ GtkWidget *hbox6;
+ GtkWidget *color_filter_name;
+ GtkWidget *hbox7;
+ GtkWidget *add_expression_bt;
+ GtkWidget *color_filter_text;
+ GtkWidget *hbox5;
+ GtkWidget *colorize_filter_fg;
+ GtkWidget *colorize_filter_bg;
+ GtkWidget *hbox4;
+ GtkWidget *edit_color_filter_ok;
+ GtkWidget *edit_color_filter_cancel;
+ GtkTooltips *tooltips;
+ GtkStyle *style;
+
+ colorf = gtk_clist_get_row_data(GTK_CLIST(color_filters), row_selected);
+ if (colorf->edit_dialog != NULL) {
+ /* There's already an edit box open for this filter; reactivate it. */
+ reactivate_window(colorf->edit_dialog);
+ return;
+ }
+
+ tooltips = gtk_tooltips_new ();
+
+ edit_dialog = dlg_window_new ("Edit color filter");
+ gtk_object_set_data (GTK_OBJECT (edit_dialog), "edit_dialog", edit_dialog);
+ colorf->edit_dialog = edit_dialog;
+
+ vbox3 = gtk_vbox_new (FALSE, 0);
+ gtk_widget_ref (vbox3);
+ gtk_object_set_data_full (GTK_OBJECT (edit_dialog), "vbox3", vbox3,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (vbox3);
+ gtk_container_add (GTK_CONTAINER (edit_dialog), vbox3);
+
+ hbox6 = gtk_hbox_new (FALSE, 0);
+ gtk_widget_ref (hbox6);
+ gtk_object_set_data_full (GTK_OBJECT (edit_dialog), "hbox6", hbox6,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (hbox6);
+ gtk_box_pack_start (GTK_BOX (vbox3), hbox6, TRUE, FALSE, 5);
+
+ color_filter_name = gtk_label_new (("Name: "));
+ gtk_widget_ref (color_filter_name);
+ gtk_object_set_data_full (GTK_OBJECT (edit_dialog), "color_filter_name", color_filter_name,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (color_filter_name);
+ gtk_box_pack_start (GTK_BOX (hbox6), color_filter_name, FALSE, FALSE, 0);
+
+ *colorize_filter_name = gtk_entry_new ();
+ gtk_widget_ref (*colorize_filter_name);
+ gtk_object_set_data_full (GTK_OBJECT (edit_dialog), "*colorize_filter_name", *colorize_filter_name,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_entry_set_text(GTK_ENTRY(*colorize_filter_name), colorf->filter_name);
+
+ style = gtk_style_copy(gtk_widget_get_style(*colorize_filter_name));
+ style->base[GTK_STATE_NORMAL] = colorf->bg_color;
+ style->fg[GTK_STATE_NORMAL] = colorf->fg_color;
+ gtk_widget_set_style(*colorize_filter_name, style);
+
+ gtk_widget_show (*colorize_filter_name);
+ gtk_box_pack_start (GTK_BOX (hbox6), *colorize_filter_name, TRUE, TRUE, 0);
+ gtk_tooltips_set_tip (tooltips, *colorize_filter_name, ("This is the editable name of the filter. (No @ characters allowed.)"), NULL);
+
+ hbox7 = gtk_hbox_new (FALSE, 0);
+ gtk_widget_ref (hbox7);
+ gtk_object_set_data_full (GTK_OBJECT (edit_dialog), "hbox7", hbox7,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (hbox7);
+ gtk_box_pack_start (GTK_BOX (vbox3), hbox7, TRUE, FALSE, 5);
+
+ color_filter_text = gtk_label_new (("Filter text:"));
+ gtk_widget_ref (color_filter_text);
+ gtk_object_set_data_full (GTK_OBJECT (edit_dialog), "color_filter_text", color_filter_text,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (color_filter_text);
+ gtk_box_pack_start (GTK_BOX (hbox7), color_filter_text, FALSE, FALSE, 0);
+
+ *colorize_filter_text = gtk_entry_new ();
+ gtk_widget_ref (*colorize_filter_text);
+ gtk_object_set_data_full (GTK_OBJECT (edit_dialog), "*colorize_filter_text", *colorize_filter_text,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_entry_set_text(GTK_ENTRY(*colorize_filter_text), colorf->filter_text);
+
+
+#if 0
+ style = gtk_style_copy(gtk_widget_get_style(*colorize_filter_text));
+ style->base[GTK_STATE_NORMAL] = colorf->bg_color;
+ style->fg[GTK_STATE_NORMAL] = colorf->fg_color;
+#endif
+ gtk_widget_set_style(*colorize_filter_text, style);
+ gtk_widget_show (*colorize_filter_text);
+ gtk_box_pack_start (GTK_BOX (hbox7), *colorize_filter_text, TRUE, TRUE, 0);
+ gtk_tooltips_set_tip (tooltips, *colorize_filter_text, ("This is the editable text of the filter"), NULL);
+
+ /* Create the "Add Expression..." button, to pop up a dialog
+ for constructing filter comparison expressions. */
+ add_expression_bt = gtk_button_new_with_label("Add Expression...");
+ gtk_signal_connect(GTK_OBJECT(add_expression_bt), "clicked",
+ GTK_SIGNAL_FUNC(filter_expr_cb), *colorize_filter_text);
+ gtk_box_pack_start (GTK_BOX(hbox7), add_expression_bt, FALSE, FALSE, 3);
+ gtk_widget_show(add_expression_bt);
+
+ hbox5 = gtk_hbox_new (FALSE, 0);
+ gtk_widget_ref (hbox5);
+ gtk_object_set_data_full (GTK_OBJECT (edit_dialog), "hbox5", hbox5,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (hbox5);
+ gtk_box_pack_start (GTK_BOX (vbox3), hbox5, FALSE, FALSE, 5);
+ gtk_widget_set_usize (hbox5, -2, 60);
+
+ colorize_filter_fg = gtk_button_new_with_label (("Choose \nforeground\ncolor"));
+ gtk_widget_ref (colorize_filter_fg);
+ gtk_object_set_data_full (GTK_OBJECT (edit_dialog), "colorize_filter_fg", colorize_filter_fg,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (colorize_filter_fg);
+ gtk_box_pack_start (GTK_BOX (hbox5), colorize_filter_fg, TRUE, FALSE, 0);
+ gtk_tooltips_set_tip (tooltips, colorize_filter_fg, ("Select color for data display"), NULL);
+
+ colorize_filter_bg = gtk_button_new_with_label (("Choose\nbackground\ncolor"));
+ gtk_widget_ref (colorize_filter_bg);
+ gtk_object_set_data_full (GTK_OBJECT (edit_dialog), "colorize_filter_bg", colorize_filter_bg,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (colorize_filter_bg);
+ gtk_box_pack_start (GTK_BOX (hbox5), colorize_filter_bg, TRUE, FALSE, 0);
+ gtk_tooltips_set_tip (tooltips, colorize_filter_bg, ("Select color for data display"), NULL);
+
+ hbox4 = gtk_hbox_new (FALSE, 0);
+ gtk_widget_ref (hbox4);
+ gtk_object_set_data_full (GTK_OBJECT (edit_dialog), "hbox4", hbox4,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_show (hbox4);
+ gtk_box_pack_start (GTK_BOX (vbox3), hbox4, TRUE, FALSE, 5);
+ gtk_widget_set_usize (hbox4, -2, 30);
+
+ edit_color_filter_ok = gtk_button_new_with_label (("OK"));
+ gtk_widget_ref (edit_color_filter_ok);
+ gtk_object_set_data_full (GTK_OBJECT (edit_dialog), "edit_color_filter_ok", edit_color_filter_ok,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_set_usize (edit_color_filter_ok, 50, 20);
+ gtk_widget_show (edit_color_filter_ok);
+ gtk_box_pack_start (GTK_BOX (hbox4), edit_color_filter_ok, TRUE, FALSE, 0);
+ gtk_tooltips_set_tip (tooltips, edit_color_filter_ok, ("Accept filter color change"), NULL);
+
+ edit_color_filter_cancel = gtk_button_new_with_label (("Cancel"));
+ gtk_widget_ref (edit_color_filter_cancel);
+ gtk_object_set_data_full (GTK_OBJECT (edit_dialog), "edit_color_filter_cancel", edit_color_filter_cancel,
+ (GtkDestroyNotify) gtk_widget_unref);
+ gtk_widget_set_usize (edit_color_filter_cancel, 50, 20);
+ gtk_widget_show (edit_color_filter_cancel);
+ gtk_box_pack_start (GTK_BOX (hbox4), edit_color_filter_cancel, TRUE, FALSE, 0);
+ gtk_tooltips_set_tip (tooltips, edit_color_filter_cancel, ("Reject filter color change"), NULL);
+ gtk_object_set_data(GTK_OBJECT (edit_dialog), COLOR_FILTER,
+ colorf);
+ gtk_signal_connect (GTK_OBJECT (edit_dialog), "destroy",
+ GTK_SIGNAL_FUNC (edit_color_filter_destroy_cb),
+ NULL);
+ gtk_object_set_data(GTK_OBJECT (colorize_filter_fg), COLOR_FILTER,
+ colorf);
+ gtk_signal_connect (GTK_OBJECT (colorize_filter_fg), "clicked",
+ GTK_SIGNAL_FUNC (edit_color_filter_fg_cb),
+ NULL);
+ gtk_object_set_data(GTK_OBJECT (colorize_filter_bg), COLOR_FILTER,
+ colorf);
+ gtk_signal_connect (GTK_OBJECT (colorize_filter_bg), "clicked",
+ GTK_SIGNAL_FUNC (edit_color_filter_bg_cb),
+ NULL);
+ gtk_object_set_data(GTK_OBJECT (edit_color_filter_ok), COLOR_FILTERS_CL,
+ color_filters);
+ gtk_object_set_data(GTK_OBJECT (edit_color_filter_ok), COLOR_FILTER,
+ colorf);
+ gtk_signal_connect (GTK_OBJECT (edit_color_filter_ok), "clicked",
+ GTK_SIGNAL_FUNC (edit_color_filter_ok_cb),
+ edit_dialog);
+ gtk_signal_connect (GTK_OBJECT (edit_color_filter_cancel), "clicked",
+ GTK_SIGNAL_FUNC (edit_color_filter_cancel_cb),
+ edit_dialog);
+
+ gtk_object_set_data (GTK_OBJECT (edit_dialog), "tooltips", tooltips);
+
+ dlg_set_cancel(edit_dialog, edit_color_filter_cancel);
+
+ gtk_widget_show (edit_dialog);
+}
+
+/* Called when the dialog box is being destroyed; destroy any color
+ selection dialogs opened from this dialog, and null out the pointer
+ to this dialog. */
+static void
+edit_color_filter_destroy_cb (GtkObject *object,
+ gpointer user_data _U_)
+{
+ color_filter_t *colorf;
+ GtkWidget *color_sel;
+
+ colorf = (color_filter_t *) gtk_object_get_data(GTK_OBJECT(object),
+ COLOR_FILTER);
+
+ colorf->edit_dialog = NULL;
+
+ /* Destroy any color selection dialogs this dialog had open. */
+ color_sel = (GtkWidget *) gtk_object_get_data(object, COLOR_SELECTION_FG);
+ if (color_sel != NULL)
+ gtk_widget_destroy(color_sel);
+ color_sel = (GtkWidget *) gtk_object_get_data(object, COLOR_SELECTION_BG);
+ if (color_sel != NULL)
+ gtk_widget_destroy(color_sel);
+}
+
+/* Pop up a color selection box to choose the foreground color. */
+static void
+edit_color_filter_fg_cb (GtkButton *button,
+ gpointer user_data _U_)
+{
+ color_filter_t *colorf;
+ GtkWidget *color_selection_fg;
+
+ colorf = (color_filter_t *) gtk_object_get_data(GTK_OBJECT(button),
+ COLOR_FILTER);
+ /* Do we already have one open for this dialog? */
+ color_selection_fg = gtk_object_get_data(GTK_OBJECT (colorf->edit_dialog),
+ COLOR_SELECTION_FG);
+ if (color_selection_fg != NULL) {
+ /* Yes. Just reactivate it. */
+ reactivate_window(color_selection_fg);
+ } else {
+ /* No. Create a new color selection box, and associate it with
+ this dialog. */
+ color_selection_fg = color_sel_win_new(colorf, FALSE);
+ gtk_object_set_data(GTK_OBJECT (colorf->edit_dialog), COLOR_SELECTION_FG,
+ color_selection_fg);
+ gtk_object_set_data(GTK_OBJECT (color_selection_fg),
+ COLOR_SELECTION_PARENT, colorf->edit_dialog);
+ }
+}
+
+/* Pop up a color selection box to choose the background color. */
+static void
+edit_color_filter_bg_cb (GtkButton *button,
+ gpointer user_data _U_)
+{
+ color_filter_t *colorf;
+ GtkWidget *color_selection_bg;
+
+ colorf = (color_filter_t *) gtk_object_get_data(GTK_OBJECT(button),
+ COLOR_FILTER);
+
+ /* Do we already have one open for this dialog? */
+ color_selection_bg = gtk_object_get_data(GTK_OBJECT (colorf->edit_dialog),
+ COLOR_SELECTION_BG);
+ if (color_selection_bg != NULL) {
+ /* Yes. Just reactivate it. */
+ reactivate_window(color_selection_bg);
+ } else {
+ /* No. Create a new color selection box, and associate it with
+ this dialog. */
+ color_selection_bg = color_sel_win_new(colorf, TRUE);
+ gtk_object_set_data(GTK_OBJECT (colorf->edit_dialog), COLOR_SELECTION_BG,
+ color_selection_bg);
+ gtk_object_set_data(GTK_OBJECT (color_selection_bg),
+ COLOR_SELECTION_PARENT, colorf->edit_dialog);
+ }
+}
+
+/* accept color (and potential content) change */
+static void
+edit_color_filter_ok_cb (GtkButton *button,
+ gpointer user_data)
+{
+ GtkWidget *dialog;
+ GtkStyle *style;
+ GdkColor new_fg_color;
+ GdkColor new_bg_color;
+ gchar *filter_name;
+ gchar *filter_text;
+ color_filter_t *colorf;
+ dfilter_t *compiled_filter;
+ GtkWidget *color_filters;
+
+ dialog = (GtkWidget *)user_data;
+
+ style = gtk_widget_get_style(filt_name_entry);
+ new_bg_color = style->base[GTK_STATE_NORMAL];
+ new_fg_color = style->fg[GTK_STATE_NORMAL];
+
+ filter_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(filt_name_entry)));
+ filter_text = g_strdup(gtk_entry_get_text(GTK_ENTRY(filt_text_entry)));
+
+ if(strchr(filter_name,'@') || strchr(filter_text,'@')){
+ simple_dialog(ESD_TYPE_CRIT, NULL, "Filter names and strings must not"
+ " use the '@' character. Filter unchanged.");
+ g_free(filter_name);
+ g_free(filter_text);
+ return;
+ }
+
+ if(!dfilter_compile(filter_text, &compiled_filter)) {
+ simple_dialog(ESD_TYPE_CRIT, NULL, "Filter \"%s\" did not compile correctly.\n"
+ " Please try again. Filter unchanged.\n%s\n", filter_name,
+ dfilter_error_msg);
+ } else {
+ color_filters = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(button),
+ COLOR_FILTERS_CL);
+ colorf = (color_filter_t *) gtk_object_get_data(GTK_OBJECT(button),
+ COLOR_FILTER);
+
+ if (colorf->filter_name != NULL)
+ g_free(colorf->filter_name);
+ colorf->filter_name = filter_name;
+ if (colorf->filter_text != NULL)
+ g_free(colorf->filter_text);
+ colorf->filter_text = filter_text;
+ colorf->fg_color = new_fg_color;
+ colorf->bg_color = new_bg_color;
+ gtk_clist_set_foreground(GTK_CLIST(color_filters), row_selected,
+ &new_fg_color);
+ gtk_clist_set_background(GTK_CLIST(color_filters), row_selected,
+ &new_bg_color);
+ if(colorf->c_colorfilter != NULL)
+ dfilter_free(colorf->c_colorfilter);
+ colorf->c_colorfilter = compiled_filter;
+ /* gtk_clist_set_text frees old text (if any) and allocates new space */
+ gtk_clist_set_text(GTK_CLIST(color_filters), row_selected, 0,
+ filter_name);
+ gtk_clist_set_text(GTK_CLIST(color_filters), row_selected, 1,
+ filter_text);
+
+ /* Destroy the dialog box. */
+ gtk_widget_destroy(dialog);
+ }
+}
+
+/* Exit dialog and do not process list */
+static void
+edit_color_filter_cancel_cb (GtkObject *object _U_,
+ gpointer user_data)
+{
+ GtkWidget *dialog;
+
+ dialog = (GtkWidget *)user_data;
+
+ /* Destroy the dialog box. */
+ gtk_widget_destroy(dialog);
+}
+
+static GtkWidget*
+color_sel_win_new(color_filter_t *colorf, gboolean is_bg)
+{
+ gint title_len;
+ gchar *title;
+ static const gchar fg_title_format[] = "Choose foreground color for \"%s\"";
+ static const gchar bg_title_format[] = "Choose background color for \"%s\"";
+ GtkWidget *color_sel_win;
+ GdkColor *color;
+ GtkWidget *color_sel_ok;
+ GtkWidget *color_sel_cancel;
+ GtkWidget *color_sel_help;
+
+ if (is_bg) {
+ color = &colorf->bg_color;
+ title_len = strlen(bg_title_format) + strlen(colorf->filter_name);
+ title = g_malloc(title_len + 1);
+ sprintf(title, bg_title_format, colorf->filter_name);
+ } else {
+ color = &colorf->fg_color;
+ title_len = strlen(fg_title_format) + strlen(colorf->filter_name);
+ title = g_malloc(title_len + 1);
+ sprintf(title, fg_title_format, colorf->filter_name);
+ }
+ color_sel_win = gtk_color_selection_dialog_new(title);
+ g_free(title);
+ gtk_object_set_data (GTK_OBJECT (color_sel_win), "color_sel_win", color_sel_win);
+ gtk_container_set_border_width (GTK_CONTAINER (color_sel_win), 10);
+
+ if (color != NULL) {
+ gdouble cols[3];
+
+ cols[0] = (gdouble)color->red / 65536.0;
+ cols[1] = (gdouble)color->green / 65536.0;
+ cols[2] = (gdouble)color->blue / 65536.0;
+
+ gtk_color_selection_set_color(
+ GTK_COLOR_SELECTION(
+ GTK_COLOR_SELECTION_DIALOG(color_sel_win)->colorsel), cols);
+ }
+
+ color_sel_ok = GTK_COLOR_SELECTION_DIALOG (color_sel_win)->ok_button;
+ gtk_object_set_data (GTK_OBJECT (color_sel_win), "color_sel_ok", color_sel_ok);
+ gtk_widget_show (color_sel_ok);
+ GTK_WIDGET_SET_FLAGS (color_sel_ok, GTK_CAN_DEFAULT);
+
+ color_sel_cancel = GTK_COLOR_SELECTION_DIALOG (color_sel_win)->cancel_button;
+ gtk_object_set_data (GTK_OBJECT (color_sel_win), "color_sel_cancel", color_sel_cancel);
+ gtk_widget_show (color_sel_cancel);
+ GTK_WIDGET_SET_FLAGS (color_sel_cancel, GTK_CAN_DEFAULT);
+
+
+ color_sel_help = GTK_COLOR_SELECTION_DIALOG (color_sel_win)->help_button;
+ gtk_object_set_data (GTK_OBJECT (color_sel_win), "color_sel_help", color_sel_help);
+ gtk_widget_show (color_sel_help);
+
+
+ GTK_WIDGET_SET_FLAGS (color_sel_help, GTK_CAN_DEFAULT);
+ gtk_signal_connect (GTK_OBJECT (color_sel_win), "destroy",
+ GTK_SIGNAL_FUNC (color_sel_cancel_cb),
+ color_sel_win);
+
+ gtk_signal_connect (GTK_OBJECT (color_sel_ok), "clicked",
+ GTK_SIGNAL_FUNC (color_sel_ok_cb),
+ color_sel_win);
+ gtk_signal_connect (GTK_OBJECT (color_sel_cancel), "clicked",
+ GTK_SIGNAL_FUNC (color_sel_cancel_cb),
+ color_sel_win);
+
+ gtk_widget_show(color_sel_win);
+ return color_sel_win;
+}
+
+static void
+color_sel_win_destroy(GtkWidget *sel_win)
+{
+ GtkWidget *parent;
+ GtkWidget *color_selection_fg, *color_selection_bg;
+
+ /* Find the "Edit color filter" dialog box with which this is associated. */
+ parent = (GtkWidget *)gtk_object_get_data(GTK_OBJECT (sel_win),
+ COLOR_SELECTION_PARENT);
+
+ /* Find that dialog box's foreground and background color selection
+ boxes, if any. */
+ color_selection_fg = gtk_object_get_data(GTK_OBJECT (parent),
+ COLOR_SELECTION_FG);
+ color_selection_bg = gtk_object_get_data(GTK_OBJECT (parent),
+ COLOR_SELECTION_BG);
+
+ if (sel_win == color_selection_fg) {
+ /* This was its foreground color selection box; it isn't, anymore. */
+ gtk_object_set_data(GTK_OBJECT (parent), COLOR_SELECTION_FG, NULL);
+ }
+ if (sel_win == color_selection_bg) {
+ /* This was its background color selection box; it isn't, anymore. */
+ gtk_object_set_data(GTK_OBJECT (parent), COLOR_SELECTION_BG, NULL);
+ }
+
+ /* Now destroy it. */
+ gtk_widget_destroy(sel_win);
+}
+
+/* Retrieve selected color */
+static void
+color_sel_ok_cb (GtkButton *button _U_,
+ gpointer user_data)
+{
+ GdkColor new_color; /* Color from color selection dialog */
+ gdouble new_colors[3];
+ GtkWidget *color_dialog;
+ GtkStyle *style;
+ GtkWidget *parent;
+ GtkWidget *color_selection_fg, *color_selection_bg;
+ gboolean is_bg;
+
+ color_dialog = (GtkWidget *)user_data;
+
+ gtk_color_selection_get_color(GTK_COLOR_SELECTION(
+ GTK_COLOR_SELECTION_DIALOG(color_dialog)->colorsel), new_colors);
+
+ new_color.red = (guint16)(new_colors[0]*65535.0);
+ new_color.green = (guint16)(new_colors[1]*65535.0);
+ new_color.blue = (guint16)(new_colors[2]*65535.0);
+
+ if ( ! get_color(&new_color) ){
+ simple_dialog(ESD_TYPE_CRIT, NULL, "Could not allocate color. Try again.");
+ } else {
+ /* Find the "Edit color filter" dialog box with which this is
+ associated. */
+ parent = (GtkWidget *)gtk_object_get_data(GTK_OBJECT (color_dialog),
+ COLOR_SELECTION_PARENT);
+
+ /* Find that dialog box's foreground and background color selection
+ boxes, if any. */
+ color_selection_fg = gtk_object_get_data(GTK_OBJECT (parent),
+ COLOR_SELECTION_FG);
+ color_selection_bg = gtk_object_get_data(GTK_OBJECT (parent),
+ COLOR_SELECTION_BG);
+ is_bg = (color_dialog == color_selection_bg);
+
+ color_sel_win_destroy(color_dialog);
+
+ /* now apply the change to the fore/background */
+
+ style = gtk_style_copy(gtk_widget_get_style(filt_name_entry));
+ if (is_bg)
+ style->base[GTK_STATE_NORMAL] = new_color;
+ else
+ style->fg[GTK_STATE_NORMAL] = new_color;
+ gtk_widget_set_style(filt_name_entry, style);
+ gtk_widget_set_style(filt_text_entry, style);
+ }
+}
+
+/* Don't choose the selected color as the foreground or background
+ color for the filter. */
+static void
+color_sel_cancel_cb (GtkObject *object _U_,
+ gpointer user_data)
+{
+ GtkWidget *color_dialog;
+ color_dialog = (GtkWidget *)user_data;
+ /* nothing to change here. Just get rid of the dialog box. */
+
+ color_sel_win_destroy(color_dialog);
+}
diff --git a/gtk2/color_dlg.h b/gtk2/color_dlg.h
new file mode 100644
index 0000000000..172861db90
--- /dev/null
+++ b/gtk2/color_dlg.h
@@ -0,0 +1,31 @@
+/* color_dlg.h
+ * Definitions for dialog boxes for color filters
+ *
+ * $Id: color_dlg.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+#ifndef __COLOR_DLG_H__
+#define __COLOR_DLG_H__
+
+void color_display_cb(GtkWidget *w, gpointer d);
+
+#endif /* color_dlg.h */
diff --git a/gtk2/color_utils.c b/gtk2/color_utils.c
new file mode 100644
index 0000000000..df13ebc1ad
--- /dev/null
+++ b/gtk2/color_utils.c
@@ -0,0 +1,53 @@
+/* color_utils.c
+ * Utilities for converting between "toolkit-independent" and GDK
+ * notions of color
+ *
+ * $Id: color_utils.c,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+
+#include <gtk/gtk.h>
+
+#include "prefs.h" /* to declare "color_t" */
+
+void
+color_t_to_gdkcolor(GdkColor *target, color_t *source)
+{
+ target->pixel = source->pixel;
+ target->red = source->red;
+ target->green = source->green;
+ target->blue = source->blue;
+}
+
+void
+gdkcolor_to_color_t(color_t *target, GdkColor *source)
+{
+ target->pixel = source->pixel;
+ target->red = source->red;
+ target->green = source->green;
+ target->blue = source->blue;
+}
diff --git a/gtk2/color_utils.h b/gtk2/color_utils.h
new file mode 100644
index 0000000000..eb3c4cad3a
--- /dev/null
+++ b/gtk2/color_utils.h
@@ -0,0 +1,33 @@
+/* color_utils.h
+ * Declarations of utilities for converting between "toolkit-independent"
+ * and GDK notions of color
+ *
+ * $Id: color_utils.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+#ifndef __COLOR_UTILS_H__
+#define __COLOR_UTILS_H__
+
+void color_t_to_gdkcolor(GdkColor *, color_t *);
+void gdkcolor_to_color_t(color_t *, GdkColor *);
+
+#endif
diff --git a/gtk2/colors.c b/gtk2/colors.c
new file mode 100644
index 0000000000..ca36bd5d0a
--- /dev/null
+++ b/gtk2/colors.c
@@ -0,0 +1,291 @@
+/* colors.c
+ * Definitions for color structures and routines
+ *
+ * $Id: colors.c,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+/*
+ * Updated 1 Dec 10 jjm
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <errno.h>
+
+#include <epan/filesystem.h>
+
+#include "gtk/main.h"
+#include <epan/packet.h>
+#include "colors.h"
+#include "file.h"
+#include <epan/dfilter/dfilter.h>
+#include "simple_dialog.h"
+
+extern capture_file cf;
+
+static gboolean read_filters(void);
+
+GSList *filter_list;
+
+static GdkColormap* sys_cmap;
+static GdkColormap* our_cmap = NULL;
+
+GdkColor WHITE = { 0, 65535, 65535, 65535 };
+GdkColor BLACK = { 0, 0, 0, 0 };
+
+/* Initialize the filter structures (reading from file) */
+void
+colfilter_init(void)
+{
+ gboolean got_white, got_black;
+
+ sys_cmap = gdk_colormap_get_system();
+
+ /* Allocate "constant" colors. */
+ got_white = get_color(&WHITE);
+ got_black = get_color(&BLACK);
+
+ /* Got milk? */
+ if (!got_white) {
+ if (!got_black)
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "Could not allocate colors black or white.");
+ else
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "Could not allocate color white.");
+ } else {
+ if (!got_black)
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "Could not allocate color black.");
+ }
+
+ read_filters();
+}
+
+/* Create a new filter */
+color_filter_t *
+new_color_filter(gchar *name, /* The name of the filter to create */
+ gchar *filter_string) /* The string representing the filter */
+{
+ color_filter_t *colorf;
+
+ colorf = (color_filter_t *)g_malloc(sizeof (color_filter_t));
+ colorf->filter_name = g_strdup(name);
+ colorf->filter_text = g_strdup(filter_string);
+ colorf->bg_color = WHITE;
+ colorf->fg_color = BLACK;
+ colorf->c_colorfilter = NULL;
+ colorf->edit_dialog = NULL;
+ filter_list = g_slist_append(filter_list, colorf);
+ return colorf;
+}
+
+/* delete the specified filter */
+void
+delete_color_filter(color_filter_t *colorf)
+{
+ if (colorf->filter_name != NULL)
+ g_free(colorf->filter_name);
+ if (colorf->filter_text != NULL)
+ g_free(colorf->filter_text);
+ if (colorf->c_colorfilter != NULL)
+ dfilter_free(colorf->c_colorfilter);
+ filter_list = g_slist_remove(filter_list, colorf);
+ g_free(colorf);
+}
+
+static void
+prime_edt(gpointer data, gpointer user_data)
+{
+ color_filter_t *colorf = data;
+ epan_dissect_t *edt = user_data;
+
+ if (colorf->c_colorfilter != NULL)
+ epan_dissect_prime_dfilter(edt, colorf->c_colorfilter);
+}
+
+/* Prime the epan_dissect_t with all the compiler
+ * color filters in 'filter_list'. */
+void
+filter_list_prime_edt(epan_dissect_t *edt)
+{
+ g_slist_foreach(filter_list, prime_edt, edt);
+}
+
+
+/* read filters from the file */
+static gboolean
+read_filters(void)
+{
+ /* TODO: Lots more syntax checking on the file */
+ /* I hate these fixed length names! TODO: make more dynamic */
+ /* XXX - buffer overflow possibility here
+ * sscanf blocks max size of name and filter_exp; buf is used for
+ * reading only */
+ gchar name[256],filter_exp[256], buf[1024];
+ guint16 fg_r, fg_g, fg_b, bg_r, bg_g, bg_b;
+ GdkColor fg_color, bg_color;
+ color_filter_t *colorf;
+ gchar *path;
+ FILE *f;
+ dfilter_t *temp_dfilter;
+
+ /* decide what file to open (from dfilter code) */
+ path = get_persconffile_path("colorfilters", FALSE);
+ if ((f = fopen(path, "r")) == NULL) {
+ if (errno != ENOENT) {
+ simple_dialog(ESD_TYPE_CRIT, NULL,
+ "Could not open filter file\n\"%s\": %s.", path,
+ strerror(errno));
+ }
+ g_free((gchar *)path);
+ return FALSE;
+ }
+ g_free((gchar *)path);
+ path = NULL;
+
+ do {
+ if (fgets(buf,sizeof buf, f) == NULL)
+ break;
+
+ if (strspn(buf," \t") == (size_t)((strchr(buf,'*') - buf))) {
+ /* leading # comment */
+ continue;
+ }
+
+ /* we get the @ delimiter. It is not in any strings
+ * Format is:
+ * @name@filter expression@[background r,g,b][foreground r,g,b]
+ */
+ if (sscanf(buf," @%256[^@]@%256[^@]@[%hu,%hu,%hu][%hu,%hu,%hu]",
+ name, filter_exp, &bg_r, &bg_g, &bg_b, &fg_r, &fg_g, &fg_b)
+ == 8) {
+ /* we got a filter */
+
+ if (!dfilter_compile(filter_exp, &temp_dfilter)) {
+ simple_dialog(ESD_TYPE_CRIT, NULL,
+ "Could not compile color filter %s from saved filters.\n%s",
+ name, dfilter_error_msg);
+ continue;
+ }
+ if (!get_color(&fg_color)) {
+ /* oops */
+ simple_dialog(ESD_TYPE_CRIT, NULL,
+ "Could not allocate foreground color "
+ "specified in input file for %s.", name);
+ dfilter_free(temp_dfilter);
+ continue;
+ }
+ if (!get_color(&bg_color)) {
+ /* oops */
+ simple_dialog(ESD_TYPE_CRIT, NULL,
+ "Could not allocate background color "
+ "specified in input file for %s.", name);
+ dfilter_free(temp_dfilter);
+ continue;
+ }
+
+ colorf = new_color_filter(name, filter_exp);
+ colorf->c_colorfilter = temp_dfilter;
+ fg_color.red = fg_r;
+ fg_color.green = fg_g;
+ fg_color.blue = fg_b;
+ bg_color.red = bg_r;
+ bg_color.green = bg_g;
+ bg_color.blue = bg_b;
+
+ colorf->bg_color = bg_color;
+ colorf->fg_color = fg_color;
+ } /* if sscanf */
+ } while(!feof(f));
+ return TRUE;
+}
+
+static void
+write_filter(gpointer filter_arg, gpointer file_arg)
+{
+ color_filter_t *colorf = filter_arg;
+ FILE *f = file_arg;
+
+ fprintf(f,"@%s@%s@[%d,%d,%d][%d,%d,%d]\n",
+ colorf->filter_name,
+ colorf->filter_text,
+ colorf->bg_color.red,
+ colorf->bg_color.green,
+ colorf->bg_color.blue,
+ colorf->fg_color.red,
+ colorf->fg_color.green,
+ colorf->fg_color.blue);
+}
+
+/* save filters in filter file */
+gboolean
+write_filters(void)
+{
+ gchar *pf_dir_path;
+ const gchar *path;
+ FILE *f;
+
+ /* Create the directory that holds personal configuration files,
+ if necessary. */
+ if (create_persconffile_dir(&pf_dir_path) == -1) {
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "Can't create directory\n\"%s\"\nfor color files: %s.",
+ pf_dir_path, strerror(errno));
+ g_free(pf_dir_path);
+ return FALSE;
+ }
+
+ path = get_persconffile_path("colorfilters", TRUE);
+ if ((f = fopen(path, "w+")) == NULL) {
+ simple_dialog(ESD_TYPE_CRIT, NULL,
+ "Could not open\n%s\nfor writing: %s.",
+ path, strerror(errno));
+ return FALSE;
+ }
+ fprintf(f,"# DO NOT EDIT THIS FILE! It was created by Ethereal\n");
+ g_slist_foreach(filter_list, write_filter, f);
+ fclose(f);
+ return TRUE;
+}
+
+/* allocate a color from the color map */
+gboolean
+get_color (GdkColor *new_color)
+{
+ GdkVisual *pv;
+
+ if (!our_cmap) {
+ if ( !gdk_colormap_alloc_color (sys_cmap, new_color, FALSE, TRUE)) {
+ pv = gdk_visual_get_best();
+ if ( !(our_cmap = gdk_colormap_new(pv, TRUE)))
+ simple_dialog(ESD_TYPE_WARN, NULL, "Could not create new colormap");
+ } else
+ return (TRUE);
+ }
+ return ( gdk_colormap_alloc_color ( our_cmap, new_color, FALSE, TRUE) );
+}
diff --git a/gtk2/colors.h b/gtk2/colors.h
new file mode 100644
index 0000000000..e12f41b904
--- /dev/null
+++ b/gtk2/colors.h
@@ -0,0 +1,67 @@
+/* colors.h
+ * Definitions for color structures and routines
+ *
+ * $Id: colors.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+#ifndef __COLORS_H__
+#define __COLORS_H__
+
+#include <epan/proto.h>
+#include <epan/dfilter/dfilter.h>
+#include <gtk/gtk.h>
+#include <epan/epan.h>
+
+#define MAXCOLORS 255
+#define MAX_COLOR_FILTER_NAME_LEN 33
+#define MAX_COLOR_FILTER_STRING_LEN 256
+
+#define CFILTERS_CONTAINS_FILTER(filter) \
+ ((filter)->num_of_filters != 0)
+
+extern GdkColor WHITE;
+extern GdkColor BLACK;
+
+/* Data for a color filter. */
+typedef struct _color_filter {
+ gchar *filter_name; /* name of the filter */
+ gchar *filter_text; /* text of the filter expression */
+ GdkColor bg_color; /* background color for packets that match */
+ GdkColor fg_color; /* foreground color for packets that match */
+ dfilter_t *c_colorfilter; /* compiled filter expression */
+ GtkWidget *edit_dialog; /* if filter is being edited, dialog box for it */
+} color_filter_t;
+
+/* List of all color filters. */
+extern GSList *filter_list;
+
+void colfilter_init(void);
+
+gboolean write_filters(void);
+
+color_filter_t *new_color_filter(gchar *name, gchar *filter_string);
+void delete_color_filter(color_filter_t *colorf);
+
+gboolean get_color (GdkColor *new_color);
+
+void
+filter_list_prime_edt(epan_dissect_t *edt);
+
+#endif
diff --git a/gtk2/column_prefs.c b/gtk2/column_prefs.c
new file mode 100644
index 0000000000..062be11655
--- /dev/null
+++ b/gtk2/column_prefs.c
@@ -0,0 +1,377 @@
+/* column_prefs.c
+ * Dialog box for column preferences
+ *
+ * $Id: column_prefs.c,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+#include "globals.h"
+#include "column_prefs.h"
+#include "gtkglobals.h"
+#include "prefs.h"
+#include "column.h"
+
+static GtkWidget *column_l, *del_bt, *title_te, *fmt_m, *up_bt, *dn_bt;
+static gint cur_fmt, cur_row;
+
+static void column_list_select_cb(GtkCList *clist, gint row, gint column,
+ GdkEvent *event, gpointer user_data);
+static void column_list_unselect_cb(GtkCList *clist, gint row, gint column,
+ GdkEvent *event, gpointer user_data);
+static void column_list_new_cb(GtkWidget *, gpointer);
+static void column_entry_changed_cb(GtkEditable *, gpointer);
+static void column_menu_changed_cb(GtkWidget *, gpointer);
+static void column_list_delete_cb(GtkWidget *, gpointer);
+static void column_arrow_cb(GtkWidget *, gpointer);
+void column_set_arrow_button_sensitivity(GList *);
+
+#define E_COL_NAME_KEY "column_name"
+#define E_COL_LBL_KEY "column_label"
+#define E_COL_CM_KEY "in_col_cancel_mode"
+
+/* Create and display the column selection widgets. */
+/* Called when the 'Columns' preference notebook page is selected. */
+GtkWidget *
+column_prefs_show() {
+ GtkWidget *main_vb, *top_hb, *list_bb, *new_bt, *column_sc,
+ *tb, *lb, *menu, *mitem, *arrow_hb;
+ GList *clp = NULL;
+ fmt_data *cfmt;
+ gint i, row;
+ gchar *column_titles[] = {"Title", "Format"}, *col_ent[2];
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_widget_show(main_vb);
+ gtk_object_set_data(GTK_OBJECT(main_vb), E_COL_CM_KEY, (gpointer)FALSE);
+
+ /* Top row: Column list and buttons */
+ top_hb = gtk_hbox_new(FALSE, 5);
+ gtk_container_add(GTK_CONTAINER(main_vb), top_hb);
+ gtk_widget_show(top_hb);
+
+ list_bb = gtk_vbutton_box_new();
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (list_bb), GTK_BUTTONBOX_START);
+ gtk_container_add(GTK_CONTAINER(top_hb), list_bb);
+ gtk_widget_show(list_bb);
+
+ new_bt = gtk_button_new_from_stock(GTK_STOCK_NEW);
+ g_signal_connect(G_OBJECT(new_bt), "clicked",
+ G_CALLBACK(column_list_new_cb), NULL);
+ gtk_container_add(GTK_CONTAINER(list_bb), new_bt);
+ gtk_widget_show(new_bt);
+
+ del_bt = gtk_button_new_from_stock (GTK_STOCK_DELETE);
+ gtk_widget_set_sensitive(del_bt, FALSE);
+ g_signal_connect(G_OBJECT(del_bt), "clicked",
+ G_CALLBACK(column_list_delete_cb), NULL);
+ gtk_container_add(GTK_CONTAINER(list_bb), del_bt);
+ gtk_widget_show(del_bt);
+
+ arrow_hb = gtk_hbox_new(TRUE, 3);
+ gtk_container_add(GTK_CONTAINER(list_bb), arrow_hb);
+ gtk_widget_show(arrow_hb);
+
+ up_bt = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
+ gtk_widget_set_sensitive(up_bt, FALSE);
+ g_signal_connect(G_OBJECT(up_bt), "clicked",
+ G_CALLBACK(column_arrow_cb), NULL);
+ gtk_box_pack_start(GTK_BOX(arrow_hb), up_bt, TRUE, TRUE, 0);
+ gtk_widget_show(up_bt);
+
+ dn_bt = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
+ gtk_widget_set_sensitive(dn_bt, FALSE);
+ g_signal_connect(G_OBJECT(dn_bt), "clicked",
+ G_CALLBACK(column_arrow_cb), NULL);
+ gtk_box_pack_start(GTK_BOX(arrow_hb), dn_bt, TRUE, TRUE, 0);
+ gtk_widget_show(dn_bt);
+
+ column_sc = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(column_sc),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_size_request(column_sc, 250, 150);
+ gtk_container_add(GTK_CONTAINER(top_hb), column_sc);
+ gtk_widget_show(column_sc);
+
+ column_l = gtk_clist_new_with_titles(2, column_titles);
+ /* XXX - make this match the packet list prefs? */
+ gtk_clist_set_selection_mode(GTK_CLIST(column_l), GTK_SELECTION_SINGLE);
+ gtk_clist_column_titles_passive(GTK_CLIST(column_l));
+ gtk_clist_column_titles_show(GTK_CLIST(column_l));
+ gtk_clist_set_column_auto_resize(GTK_CLIST(column_l), 0, TRUE);
+ gtk_clist_set_column_auto_resize(GTK_CLIST(column_l), 1, TRUE);
+
+ gtk_signal_connect(GTK_OBJECT(column_l), "select-row",
+ GTK_SIGNAL_FUNC(column_list_select_cb), NULL);
+ gtk_signal_connect(GTK_OBJECT(column_l), "unselect-row",
+ GTK_SIGNAL_FUNC(column_list_unselect_cb), NULL);
+ gtk_container_add(GTK_CONTAINER(column_sc), column_l);
+ gtk_widget_show(column_l);
+
+ clp = g_list_first(prefs.col_list);
+ while (clp) {
+ cfmt = (fmt_data *) clp->data;
+ col_ent[0] = cfmt->title;
+ col_ent[1] = col_format_desc(get_column_format_from_str(cfmt->fmt));
+ row = gtk_clist_append(GTK_CLIST(column_l), col_ent);
+ gtk_clist_set_row_data(GTK_CLIST(column_l), row, clp);
+ clp = clp->next;
+ }
+
+ /* Colunm name entry and format selection */
+ tb = gtk_table_new(2, 2, FALSE);
+ gtk_container_add(GTK_CONTAINER(main_vb), tb);
+ gtk_table_set_row_spacings(GTK_TABLE(tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(tb), 15);
+ gtk_widget_show(tb);
+
+ lb = gtk_label_new("Column title:");
+ gtk_misc_set_alignment(GTK_MISC(lb), 1.0, 0.5);
+ gtk_table_attach_defaults(GTK_TABLE(tb), lb, 0, 1, 0, 1);
+ gtk_widget_show(lb);
+
+ title_te = gtk_entry_new();
+ gtk_table_attach_defaults(GTK_TABLE(tb), title_te, 1, 2, 0, 1);
+ gtk_signal_connect(GTK_OBJECT(title_te), "changed",
+ GTK_SIGNAL_FUNC(column_entry_changed_cb), column_l);
+ gtk_widget_set_sensitive(title_te, FALSE);
+ gtk_widget_show(title_te);
+
+ lb = gtk_label_new("Column format:");
+ gtk_misc_set_alignment(GTK_MISC(lb), 1.0, 0.5);
+ gtk_table_attach_defaults(GTK_TABLE(tb), lb, 0, 1, 1, 2);
+ gtk_widget_show(lb);
+
+ top_hb = gtk_hbox_new(FALSE, 5);
+ gtk_table_attach(GTK_TABLE(tb), top_hb, 1, 2, 1, 2, GTK_FILL,
+ GTK_SHRINK, 0, 0);
+ gtk_widget_show(top_hb);
+
+ fmt_m = gtk_option_menu_new();
+ menu = gtk_menu_new();
+ for (i = 0; i < NUM_COL_FMTS; i++) {
+ mitem = gtk_menu_item_new_with_label(col_format_desc(i));
+ gtk_menu_append(GTK_MENU(menu), mitem);
+ g_signal_connect(G_OBJECT(mitem), "activate",
+ G_CALLBACK(column_menu_changed_cb), (gpointer) i);
+ gtk_widget_show(mitem);
+ }
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(fmt_m), menu);
+ cur_fmt = 0;
+ gtk_option_menu_set_history(GTK_OPTION_MENU(fmt_m), cur_fmt);
+ gtk_widget_set_sensitive(fmt_m, FALSE);
+ gtk_box_pack_start(GTK_BOX(top_hb), fmt_m, FALSE, FALSE, 0);
+ gtk_widget_show(fmt_m);
+
+ return(main_vb);
+}
+
+/* For each selection, set the entry and option menu widgets to match
+ * the currently selected item. Set the up/down button sensitivity.
+ * Draw focus to the entry widget. */
+static void
+column_list_select_cb(GtkCList *clist,
+ gint row,
+ gint column _U_,
+ GdkEvent *event _U_,
+ gpointer user_data _U_) {
+ fmt_data *cfmt;
+ GList *clp;
+
+ clp = gtk_clist_get_row_data(clist, row);
+ g_assert(clp != NULL);
+ cfmt = (fmt_data *) clp->data;
+ cur_fmt = get_column_format_from_str(cfmt->fmt);
+ g_assert(cur_fmt != -1); /* It should always be valid */
+ cur_row = row;
+
+ gtk_entry_set_text(GTK_ENTRY(title_te), cfmt->title);
+ gtk_editable_select_region(GTK_EDITABLE(title_te), 0, -1);
+ gtk_widget_grab_focus(title_te);
+
+ gtk_option_menu_set_history(GTK_OPTION_MENU(fmt_m), cur_fmt);
+
+ gtk_widget_set_sensitive(del_bt, TRUE);
+ gtk_widget_set_sensitive(title_te, TRUE);
+ gtk_widget_set_sensitive(fmt_m, TRUE);
+ column_set_arrow_button_sensitivity(clp);
+}
+
+/* A row was deselected. Clear the text entry box and disable various
+ * widgets. */
+static void
+column_list_unselect_cb(GtkCList *clist _U_,
+ gint row _U_,
+ gint column _U_,
+ GdkEvent *event _U_,
+ gpointer user_data _U_) {
+
+ cur_row = -1;
+ gtk_editable_delete_text(GTK_EDITABLE(title_te), 0, -1);
+
+ gtk_widget_set_sensitive(del_bt, FALSE);
+ gtk_widget_set_sensitive(title_te, FALSE);
+ gtk_widget_set_sensitive(fmt_m, FALSE);
+ gtk_widget_set_sensitive(up_bt, FALSE);
+ gtk_widget_set_sensitive(dn_bt, FALSE);
+}
+
+/* To do: add input checking to each of these callbacks */
+
+static void
+column_list_new_cb(GtkWidget *w _U_, gpointer data _U_) {
+ fmt_data *cfmt;
+ gchar *title = "New Column", *col_ent[2];
+
+ cur_fmt = 0;
+ cfmt = (fmt_data *) g_malloc(sizeof(fmt_data));
+ cfmt->title = g_strdup(title);
+ cfmt->fmt = g_strdup(col_format_to_string(cur_fmt));
+ prefs.col_list = g_list_append(prefs.col_list, cfmt);
+
+ col_ent[0] = title;
+ col_ent[1] = col_format_desc(cur_fmt);
+ cur_row = gtk_clist_append(GTK_CLIST(column_l), col_ent);
+ gtk_clist_set_row_data(GTK_CLIST(column_l), cur_row,
+ g_list_last(prefs.col_list));
+
+ gtk_clist_select_row(GTK_CLIST(column_l), cur_row, 0);
+}
+
+static void
+column_list_delete_cb(GtkWidget *w _U_, gpointer data _U_) {
+ GList *clp;
+ fmt_data *cfmt;
+
+ g_assert(cur_row >= 0);
+ clp = gtk_clist_get_row_data(GTK_CLIST(column_l), cur_row);
+
+ cfmt = (fmt_data *) clp->data;
+ g_free(cfmt->title);
+ g_free(cfmt->fmt);
+ g_free(cfmt);
+ prefs.col_list = g_list_remove_link(prefs.col_list, clp);
+
+ gtk_clist_remove(GTK_CLIST(column_l), cur_row);
+}
+
+/* The user changed the column title entry box. */
+static void
+column_entry_changed_cb(GtkEditable *te, gpointer data) {
+ fmt_data *cfmt;
+ GList *clp;
+ GtkCList *cl = data;
+ gchar *title;
+
+ if (cur_row >= 0) {
+ title = gtk_editable_get_chars(te, 0, -1);
+ clp = gtk_clist_get_row_data(cl, cur_row);
+ cfmt = (fmt_data *) clp->data;
+
+ gtk_clist_set_text(cl, cur_row, 0, title);
+ g_free(cfmt->title);
+ cfmt->title = title;
+ }
+}
+
+/* The user changed the format menu. */
+static void
+column_menu_changed_cb(GtkWidget *w _U_, gpointer data) {
+ fmt_data *cfmt;
+ GList *clp;
+
+ if (cur_row >= 0) {
+ cur_fmt = (gint) data;
+ clp = gtk_clist_get_row_data(GTK_CLIST(column_l), cur_row);
+ cfmt = (fmt_data *) clp->data;
+
+ gtk_clist_set_text(GTK_CLIST(column_l), cur_row, 1,
+ col_format_desc(cur_fmt));
+ g_free(cfmt->fmt);
+ cfmt->fmt = g_strdup(col_format_to_string(cur_fmt));
+ }
+}
+
+static void
+column_arrow_cb(GtkWidget *w, gpointer data _U_) {
+ GList *clp;
+ fmt_data *cfmt;
+ gint inc = 1;
+
+ g_assert(cur_row >= 0);
+
+ if (w == up_bt)
+ inc = -1;
+
+ /* This would end up appending to the list. We shouldn't have to
+ * check for appending past the end of the list. */
+ g_assert((cur_row + inc) >= 0);
+
+ clp = gtk_clist_get_row_data(GTK_CLIST(column_l), cur_row);
+ cfmt = (fmt_data *) clp->data;
+ prefs.col_list = g_list_remove(prefs.col_list, cfmt);
+ prefs.col_list = g_list_insert(prefs.col_list, cfmt, cur_row + inc);
+
+ gtk_clist_row_move(GTK_CLIST(column_l), cur_row, cur_row + inc);
+ clp = g_list_find(prefs.col_list, cfmt);
+ cur_row += inc;
+ gtk_clist_set_row_data(GTK_CLIST(column_l), cur_row, clp);
+
+ column_set_arrow_button_sensitivity(clp);
+}
+
+void
+column_set_arrow_button_sensitivity(GList *clp) {
+ gint up_sens = FALSE, dn_sens = FALSE;
+
+ if (clp != g_list_first(prefs.col_list))
+ up_sens = TRUE;
+ if (clp != g_list_last(prefs.col_list))
+ dn_sens = TRUE;
+
+ gtk_widget_set_sensitive(up_bt, up_sens);
+ gtk_widget_set_sensitive(dn_bt, dn_sens);
+}
+
+void
+column_prefs_fetch(GtkWidget *w _U_) {
+}
+
+void
+column_prefs_apply(GtkWidget *w _U_) {
+}
+
+void
+column_prefs_destroy(GtkWidget *w) {
+
+ /* Let the list cb know we're about to destroy the widget tree, so it */
+ /* doesn't operate on widgets that don't exist. */
+ gtk_object_set_data(GTK_OBJECT(w), E_COL_CM_KEY, (gpointer)TRUE);
+ gtk_widget_destroy(GTK_WIDGET(w));
+}
diff --git a/gtk2/column_prefs.h b/gtk2/column_prefs.h
new file mode 100644
index 0000000000..5d6396106c
--- /dev/null
+++ b/gtk2/column_prefs.h
@@ -0,0 +1,29 @@
+/* gui_prefs.h
+ * Definitions for column preferences window
+ *
+ * $Id: column_prefs.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+GtkWidget *column_prefs_show(void);
+void column_prefs_fetch(GtkWidget *);
+void column_prefs_apply(GtkWidget *);
+void column_prefs_destroy(GtkWidget *);
diff --git a/gtk2/decode_as_dlg.c b/gtk2/decode_as_dlg.c
new file mode 100644
index 0000000000..06539f0984
--- /dev/null
+++ b/gtk2/decode_as_dlg.c
@@ -0,0 +1,1349 @@
+/* decode_as_dlg.c
+ *
+ * $Id: decode_as_dlg.c,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Routines to modify dissector tables on the fly.
+ *
+ * By David Hampton <dhampton@mac.com>
+ * Copyright 2001 David Hampton
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <errno.h>
+
+#include "decode_as_dlg.h"
+#include "dlg_utils.h"
+#include "globals.h"
+#include "simple_dialog.h"
+#include <epan/packet.h>
+#include "ipproto.h"
+#include "ui_util.h"
+#include <epan/epan_dissect.h>
+
+#undef DEBUG
+
+/**************************************************/
+/* Typedefs & Enums */
+/**************************************************/
+
+/*
+ * Enum used to track which radio button is currently selected in the
+ * dialog. These buttons are labeled "Decode" and "Do not decode".
+ */
+enum action_type {
+ /* The "Decode" button is currently selected. */
+ E_DECODE_YES,
+
+ /* The "Do not decode" button is currently selected. */
+ E_DECODE_NO
+};
+
+/*
+ * Enum used to track which transport layer port menu item is
+ * currently selected in the dialog. These items are labeled "source",
+ * "destination", and "source/destination".
+ */
+enum srcdst_type {
+ /* The "source port" menu item is currently selected. */
+ E_DECODE_SPORT,
+ /* The "destination port" menu item is currently selected. */
+ E_DECODE_DPORT,
+ /* The "source/destination port" menu item is currently selected. */
+ E_DECODE_BPORT
+};
+
+#define E_DECODE_MIN_HEIGHT 100
+#define E_NOTEBOOK "notebook"
+
+#define E_MENU_SRCDST "menu_src_dst"
+
+#define E_PAGE_ACTION "notebook_page_action"
+#define E_PAGE_LIST "notebook_page_list"
+#define E_PAGE_TABLE "notebook_page_table_name"
+#define E_PAGE_TITLE "notebook_page_title"
+#define E_PAGE_VALUE "notebook_page_value"
+
+/*
+ * list columns for a "Select" list.
+ * Note that most of these columns aren't displayed; they're attached
+ * to the row of the table as additional information.
+ */
+#define E_LIST_S_PROTO_NAME 0
+#define E_LIST_S_TABLE 1
+/* The following is for debugging in decode_add_to_list */
+#define E_LIST_S_MAX E_LIST_S_TABLE
+#define E_LIST_S_COLUMNS (E_LIST_S_MAX + 1)
+
+/*
+ * list columns for a "Display" list
+ */
+#define E_LIST_D_TABLE 0
+#define E_LIST_D_PORT 1
+#define E_LIST_D_INITIAL 2
+#define E_LIST_D_CURRENT 3
+#define E_LIST_D_MAX E_LIST_D_CURRENT
+#define E_LIST_D_COLUMNS (E_LIST_D_MAX + 1)
+
+/**************************************************/
+/* File Global Variables */
+/**************************************************/
+
+/*
+ * Keep a static pointer to the current "Decode As" window. This is
+ * kept so that if somebody tries to do "Tools:Decode As" while
+ * there's already a "Decode As" window up, we just pop up the
+ * existing one, rather than creating a new one.
+ */
+static GtkWidget *decode_w = NULL;
+
+/*
+ * A static pointer to the current "Decode As:Show" window. This is
+ * kept so that if somebody tries to do clock the "Show Current"
+ * button or slect the "Display:User Specified Decodes" menu item
+ * while there's already a "Decode As:Show" window up, we just pop up
+ * the existing one, rather than creating a new one.
+ */
+static GtkWidget *decode_show_w = NULL;
+
+/*
+ * A list of the dialog items that only have meaning when the user has
+ * selected the "Decode" radio button. When the "Do not decode"
+ * button is selected these items should be dimmed.
+ */
+static GSList *decode_dimmable = NULL;
+
+/*
+ * Remember the "action" radio button that is currently selected in
+ * the dialog. This value is initialized when the dialog is created,
+ * modified in a callback routine, and read in the routine that
+ * handles a click in the "OK" button for the dialog.
+ */
+static enum action_type requested_action = -1;
+
+/**************************************************/
+/* Resett Changed Dissectors */
+/**************************************************/
+
+/*
+ * Data structure for tracking which dissector need to be reset. This
+ * structure is necessary as a hash table entry cannot be removed
+ * while a g_hash_table_foreach walk is in progress.
+ */
+struct dissector_delete_item {
+ /* The name of the dissector table */
+ const gchar *ddi_table_name;
+ /* The port number in the dissector table */
+ guint ddi_port;
+};
+
+/*
+ * A typedef for the data structure to track the original dissector
+ * used for any given port on any given protocol.
+ */
+typedef struct dissector_delete_item dissector_delete_item_t;
+
+/*
+ * A list of dissectors that need to be reset.
+ */
+GSList *dissector_reset_list = NULL;
+
+/*
+ * This routine creates one entry in the list of protocol dissector
+ * that need to be reset. It is called by the g_hash_table_foreach
+ * routine once for each changed entry in a dissector table.
+ * Unfortunately it cannot delete the entry immediately as this screws
+ * up the foreach function, so it builds a list of dissectors to be
+ * reset once the foreach routine finishes.
+ *
+ * @param table_name The table name in which this dissector is found.
+ *
+ * @param key A pointer to the key for this entry in the dissector
+ * hash table. This is generally the numeric selector of the
+ * protocol, i.e. the ethernet type code, IP port number, TCP port
+ * number, etc.
+ *
+ * @param value A pointer to the value for this entry in the dissector
+ * hash table. This is an opaque pointer that can only be handed back
+ * to routine in the file packet.c - but it's unused.
+ *
+ * @param user_data Unused.
+ */
+static void
+decode_build_reset_list (gchar *table_name, gpointer key,
+ gpointer value _U_, gpointer user_data _U_)
+{
+ dissector_delete_item_t *item;
+
+ item = g_malloc(sizeof(dissector_delete_item_t));
+ item->ddi_table_name = table_name;
+ item->ddi_port = GPOINTER_TO_UINT(key);
+ dissector_reset_list = g_slist_prepend(dissector_reset_list, item);
+}
+
+
+/**************************************************/
+/* Show Changed Dissectors */
+/**************************************************/
+
+/*
+ * This routine creates one entry in the list of protocol dissector
+ * that have been changed. It is called by the g_hash_foreach routine
+ * once for each changed entry in a dissector table.
+ *
+ * @param table_name The table name in which this dissector is found.
+ *
+ * @param key A pointer to the key for this entry in the dissector
+ * hash table. This is generally the numeric selector of the
+ * protocol, i.e. the ethernet type code, IP port number, TCP port
+ * number, etc.
+ *
+ * @param value A pointer to the value for this entry in the dissector
+ * hash table. This is an opaque pointer that can only be handed back
+ * to routine in the file packet.c
+ *
+ * @param user_data A pointer to the ListStore in which this information
+ * should be stored.
+ */
+static void
+decode_build_show_list (gchar *table_name, gpointer key,
+ gpointer value, gpointer user_data)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ dissector_handle_t current, initial;
+ gchar *current_proto_name, *initial_proto_name, *text[E_LIST_D_COLUMNS];
+ gchar string1[20];
+
+ g_assert(user_data);
+ g_assert(value);
+
+ store = (GtkListStore *)user_data;
+ current = dtbl_entry_get_handle(value);
+ if (current == NULL)
+ current_proto_name = "(none)";
+ else
+ current_proto_name = dissector_handle_get_short_name(current);
+ initial = dtbl_entry_get_initial_handle(value);
+ if (initial == NULL)
+ initial_proto_name = "(none)";
+ else
+ initial_proto_name = dissector_handle_get_short_name(initial);
+
+ text[E_LIST_D_TABLE] = get_dissector_table_ui_name(table_name);
+ switch (get_dissector_table_base(table_name)) {
+
+ case BASE_DEC:
+ sprintf(string1, "%u", GPOINTER_TO_UINT(key));
+ break;
+
+ case BASE_HEX:
+ switch (get_dissector_table_type(table_name)) {
+
+ case FT_UINT8:
+ sprintf(string1, "0x%02x", GPOINTER_TO_UINT(key));
+ break;
+
+ case FT_UINT16:
+ sprintf(string1, "0x%04x", GPOINTER_TO_UINT(key));
+ break;
+
+ case FT_UINT24:
+ sprintf(string1, "0x%06x", GPOINTER_TO_UINT(key));
+ break;
+
+ case FT_UINT32:
+ sprintf(string1, "0x%08x", GPOINTER_TO_UINT(key));
+ break;
+
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ break;
+
+ case BASE_OCT:
+ sprintf(string1, "%#o", GPOINTER_TO_UINT(key));
+ break;
+ }
+ text[E_LIST_D_PORT] = string1;
+ text[E_LIST_D_INITIAL] = initial_proto_name;
+ text[E_LIST_D_CURRENT] = current_proto_name;
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, E_LIST_D_TABLE, text[E_LIST_D_TABLE],
+ E_LIST_D_PORT, text[E_LIST_D_PORT],
+ E_LIST_D_INITIAL, text[E_LIST_D_INITIAL],
+ E_LIST_D_CURRENT, text[E_LIST_D_CURRENT], -1);
+}
+
+
+/*
+ * This routine is called when the user clicks the "OK" button in
+ * the "Decode As:Show..." dialog window. This routine destroys the
+ * dialog box and performs other housekeeping functions.
+ *
+ * @param GtkWidget * A pointer to the "OK" button.
+ *
+ * @param gpointer A pointer to the dialog window.
+ */
+static void
+decode_show_ok_cb (GtkWidget *ok_bt _U_, gpointer parent_w)
+{
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+}
+
+
+/*
+ * This routine is called when the user clicks the "Reset" button in
+ * the "Decode As:Show..." dialog window. This routine resets all the
+ * dissector values and then destroys the dialog box and performs
+ * other housekeeping functions.
+ *
+ * @param GtkWidget * A pointer to the "Reset" button.
+ *
+ * @param gpointer A pointer to the dialog window.
+ */
+static void
+decode_show_reset_cb (GtkWidget *reset_bt _U_, gpointer parent_w)
+{
+ dissector_delete_item_t *item;
+ GSList *tmp;
+
+ dissector_all_tables_foreach_changed(decode_build_reset_list, NULL);
+
+ for (tmp = dissector_reset_list; tmp; tmp = g_slist_next(tmp)) {
+ item = tmp->data;
+ dissector_reset(item->ddi_table_name, item->ddi_port);
+ g_free(item);
+ }
+ g_slist_free(dissector_reset_list);
+ dissector_reset_list = NULL;
+
+ redissect_packets(&cfile);
+
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+}
+
+
+/*
+ * This routine is called when the user clicks the "Close" button in
+ * the "Decode As:Show..." dialog window. This routine simply calls the
+ * cancel routine as if the user had clicked the cancel button instead
+ * of the close button.
+ *
+ * @param GtkWidget * A pointer to the dialog box.
+ *
+ * @param gpointer Unknown
+ */
+static gboolean
+decode_show_delete_cb (GtkWidget *decode_w _U_, gpointer dummy _U_)
+{
+ decode_show_ok_cb(NULL, decode_show_w);
+ return FALSE;
+}
+
+
+/*
+ * This routine is called at the destruction of the "Decode As:Show"
+ * dialog box. It clears the pointer maintained by this file, so that
+ * the next time the user clicks the "Decode As:Show" button a new
+ * dialog box will be created.
+ *
+ * @param GtkWidget * A pointer to the dialog box.
+ *
+ * @param gpointer Unknown
+ */
+static void
+decode_show_destroy_cb (GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have a "Decode As:Show" dialog box. */
+ decode_show_w = NULL;
+}
+
+
+/*
+ * This routine creates the "Decode As:Show" dialog box. This dialog box
+ * shows the user which protocols have had their dissectors changed.
+ *
+ * @param w Unknown
+ * @param data Unknown
+ */
+void
+decode_show_cb (GtkWidget * w _U_, gpointer data _U_)
+{
+ GtkWidget *main_vb, *bbox, *ok_bt, *button, *scrolled_window;
+ gchar *titles[E_LIST_D_COLUMNS] = {
+ "Table", "Port", "Initial", "Current"
+ };
+ gint column;
+ GtkListStore *store;
+ GtkTreeView *list;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *tc;
+ GtkTreeIter iter;
+
+ if (decode_show_w != NULL) {
+ /* There's already a "Decode As" dialog box; reactivate it. */
+ reactivate_window(decode_show_w);
+ return;
+ }
+
+ decode_show_w = dlg_window_new("Ethereal: Decode As: Show");
+ g_signal_connect(G_OBJECT(decode_show_w), "delete_event",
+ G_CALLBACK(decode_show_delete_cb), NULL);
+ g_signal_connect(G_OBJECT(decode_show_w), "destroy",
+ G_CALLBACK(decode_show_destroy_cb), NULL);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 2);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(decode_show_w), main_vb);
+
+ {
+ /* Initialize list */
+ store = gtk_list_store_new(E_LIST_D_COLUMNS, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+ list = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)));
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list), TRUE);
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list), FALSE);
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection(list),
+ GTK_SELECTION_MULTIPLE);
+
+ for (column = 0; column < E_LIST_D_COLUMNS; column++) {
+ renderer = gtk_cell_renderer_text_new();
+ tc = gtk_tree_view_column_new_with_attributes(titles[column],
+ renderer, "text",
+ column, NULL);
+ gtk_tree_view_column_set_sizing(tc, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(list, tc);
+ }
+
+ /* Add data */
+ dissector_all_tables_foreach_changed(decode_build_show_list, store);
+ g_object_unref(G_OBJECT(store));
+ /* gtk_clist_sort(clist); */
+
+ /* Put clist into a scrolled window */
+ scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(scrolled_window),
+ GTK_WIDGET(list));
+ gtk_box_pack_start(GTK_BOX(main_vb), scrolled_window, TRUE, TRUE, 0);
+ /* Provide a minimum of a couple of rows worth of data */
+ gtk_widget_set_size_request(scrolled_window, -1, E_DECODE_MIN_HEIGHT);
+ }
+
+ /* Button row: OK and reset buttons */
+ 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_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 10);
+
+ button = gtk_button_new_with_label("Reset Changes");
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(decode_show_reset_cb),
+ GTK_OBJECT(decode_show_w));
+ GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+ gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+ gtk_widget_set_sensitive(button,
+ gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter));
+
+ ok_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ g_signal_connect(G_OBJECT(ok_bt), "clicked",
+ G_CALLBACK(decode_show_ok_cb), GTK_OBJECT(decode_show_w));
+ GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start(GTK_BOX(bbox), ok_bt, FALSE, FALSE, 0);
+ gtk_widget_grab_default(ok_bt);
+ dlg_set_cancel(decode_show_w, ok_bt);
+
+ gtk_widget_show_all(decode_show_w);
+}
+
+
+/**************************************************/
+/* Modify the dissector routines */
+/**************************************************/
+
+/*
+ * Modify a single dissector. This routine first takes care of
+ * updating the internal table of original protocol/port/dissector
+ * combinations by adding a new entry (or removing an existing entry
+ * if the value is being set back to its default). This routine then
+ * performs the actual modification to the packet dissector tables.
+ *
+ * @param s Pointer to a string buffer. This buffer is used to build
+ * up a message indicating which ports have had their dissector
+ * changed. This output will be displayed all at once after all
+ * dissectors have been modified.
+ *
+ * @param table_name The table name in which the dissector should be
+ * modified.
+ *
+ * @param selector An enum value indication which selector value
+ * (i.e. IP protocol number, TCP port number, etc.)is to be changed.
+ *
+ * @param list The List in which all the selection information can
+ * be found.
+ *
+ * @return gchar * Pointer to the next free location in the string
+ * buffer.
+ */
+static void
+decode_change_one_dissector(gchar *table_name, gint selector, GtkTreeView *list)
+{
+ dissector_handle_t handle;
+ gchar *abbrev;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ selection = gtk_tree_view_get_selection(list);
+ if (gtk_tree_selection_get_selected(selection, &model, &iter) == FALSE)
+ {
+ abbrev = "(NULL)";
+ handle = NULL;
+ } else {
+ gtk_tree_model_get(model, &iter, E_LIST_S_PROTO_NAME, &abbrev,
+ E_LIST_S_TABLE+1, &handle, -1);
+ }
+
+ if (strcmp(abbrev, "(default)") == 0) {
+ dissector_reset(table_name, selector);
+ } else {
+ dissector_change(table_name, selector, handle);
+ }
+}
+
+
+
+/**************************************************/
+/* Action routines for the "Decode As..." dialog */
+/* - called when the OK button pressed */
+/* - one per notebook page */
+/**************************************************/
+
+
+#ifdef DEBUG
+/*
+ * Print debugging information about list selection. Extract all
+ * information from the list entry that was selected and print it to
+ * a dialog window.
+ *
+ * @param clist The clist to dump.
+ *
+ * @param leadin A string to print at the start of each line.
+ */
+static void
+decode_debug (GtkCList *clist, gchar *leadin)
+{
+ gchar *string, *text[E_LIST_S_COLUMNS];
+ dissector_handle_t handle;
+ gint row;
+
+ string = g_malloc(1024);
+ if (clist->selection) {
+ row = GPOINTER_TO_INT(clist->selection->data);
+ gtk_clist_get_text(clist, row, E_LIST_S_PROTO_NAME,
+ &text[E_LIST_S_PROTO_NAME]);
+ gtk_clist_get_text(clist, row, E_LIST_S_TABLE,
+ &text[E_LIST_S_TABLE]);
+ handle = gtk_clist_get_row_data(clist, row);
+ sprintf(string, "%s clist row %d: <put handle here>, name %s, table %s",
+ leadin, row, text[E_LIST_S_PROTO_NAME],
+ text[E_LIST_S_TABLE]);
+ } else {
+ sprintf(string, "%s clist row (none), aka do not decode", leadin);
+ }
+ simple_dialog(ESD_TYPE_INFO, NULL, string);
+ g_free(string);
+}
+#endif
+
+
+/*
+ * This routine is called when the user clicks the "OK" button in the
+ * "Decode As..." dialog window and a 'simple' page is foremost.
+ * This routine takes care of making any changes requested to the
+ * dissector tables. This routine is currently used for IP and
+ * Ethertypes. Any 'single change' notebook page can use this
+ * routine.
+ *
+ * @param notebook_pg A pointer to the "network" notebook page.
+ */
+static void
+decode_simple (GtkWidget *notebook_pg)
+{
+ GtkTreeView *list;
+#ifdef DEBUG
+ gchar *string;
+#endif
+ gchar *table_name;
+ gint value;
+
+ list = GTK_TREE_VIEW(gtk_object_get_data(GTK_OBJECT(notebook_pg),
+ E_PAGE_LIST));
+ if (requested_action == E_DECODE_NO)
+ gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(list));
+
+#ifdef DEBUG
+ string = gtk_object_get_data(GTK_OBJECT(notebook_pg), E_PAGE_TITLE);
+ decode_debug(list, string);
+#endif
+
+ table_name = gtk_object_get_data(GTK_OBJECT(notebook_pg), E_PAGE_TABLE);
+ value = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(notebook_pg),
+ E_PAGE_VALUE));
+ decode_change_one_dissector(table_name, value, list);
+}
+
+
+/*
+ * This routine is called when the user clicks the "OK" button in the
+ * "Decode As..." dialog window and the transport page is foremost.
+ * This routine takes care of making any changes requested to the TCP
+ * or UDP dissector tables.
+ *
+ * @param notebook_pg A pointer to the "transport" notebook page.
+ */
+static void
+decode_transport (GtkObject *notebook_pg)
+{
+ GtkWidget *menu, *menuitem;
+ GtkTreeView *list;
+ gchar *table_name;
+ gint requested_srcdst;
+
+ list = GTK_TREE_VIEW(gtk_object_get_data(notebook_pg, E_PAGE_LIST));
+ if (requested_action == E_DECODE_NO)
+ gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(list));
+
+ menu = gtk_object_get_data(notebook_pg, E_MENU_SRCDST);
+ menuitem = gtk_menu_get_active(GTK_MENU(menu));
+ requested_srcdst = GPOINTER_TO_INT(gtk_object_get_user_data(GTK_OBJECT(menuitem)));
+
+#ifdef DEBUG
+ string = gtk_object_get_data(GTK_OBJECT(notebook_pg), E_PAGE_TITLE);
+ decode_debug(list, string);
+#endif
+
+ table_name = gtk_object_get_data(GTK_OBJECT(notebook_pg), E_PAGE_TABLE);
+ if (requested_srcdst != E_DECODE_DPORT)
+ decode_change_one_dissector(table_name, cfile.edt->pi.srcport, list);
+ if (requested_srcdst != E_DECODE_SPORT)
+ decode_change_one_dissector(table_name, cfile.edt->pi.destport, list);
+}
+
+/**************************************************/
+/* Signals from the "Decode As..." dialog */
+/**************************************************/
+
+/*
+ * This routine is called when the user clicks the "OK" button in the
+ * "Decode As..." dialog window. This routine calls various helper
+ * routines to set/clear dissector values as requested by the user.
+ * These routines accumulate information on what actions they have
+ * taken, and this summary information is printed by this routine.
+ * This routine then destroys the dialog box and performs other
+ * housekeeping functions.
+ *
+ * @param ok_bt A pointer to the "OK" button.
+ *
+ * @param parent_w A pointer to the dialog window.
+ */
+static void
+decode_ok_cb (GtkWidget *ok_bt _U_, gpointer parent_w)
+{
+ GtkWidget *notebook, *notebook_pg;
+ void (* func)(GtkWidget *);
+ gint page_num;
+
+ /* Call the right routine for the page that was currently in front. */
+ notebook = gtk_object_get_data(GTK_OBJECT(parent_w), E_NOTEBOOK);
+ page_num = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
+ notebook_pg = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page_num);
+
+ func = gtk_object_get_data(GTK_OBJECT(notebook_pg), E_PAGE_ACTION);
+ func(notebook_pg);
+
+ /* Now destroy the "Decode As" dialog. */
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+ g_slist_free(decode_dimmable);
+ decode_dimmable = NULL;
+
+ redissect_packets(&cfile);
+}
+
+
+/*
+ * This routine is called when the user clicks the "Cancel" button in
+ * the "Decode As..." dialog window. This routine then destroys the
+ * dialog box and performs other housekeeping functions.
+ *
+ * @param cancel_bt A pointer to the "Cancel" button.
+ *
+ * @param parent_w A pointer to the dialog window.
+ */
+static void
+decode_cancel_cb (GtkWidget *cancel_bt _U_, gpointer parent_w)
+{
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+ g_slist_free(decode_dimmable);
+ decode_dimmable = NULL;
+}
+
+
+/*
+ * This routine is called when the user clicks the "Close" button in
+ * the "Decode As..." dialog window. This routine simply calls the
+ * cancel routine as if the user had clicked the cancel button instead
+ * of the close button.
+ *
+ * @param decode_w A pointer to the dialog box.
+ *
+ * @param dummy Unknown
+ */
+static gboolean
+decode_delete_cb (GtkWidget *decode_w, gpointer dummy _U_)
+{
+ decode_cancel_cb(NULL, decode_w);
+ return FALSE;
+}
+
+
+/*
+ * This routine is called at the destruction of the "Decode As..."
+ * dialog box. It clears the pointer maintained by this file, so that
+ * the next time the user selects the "Decode As..." menu item a new
+ * dialog box will be created.
+ *
+ * @param decode_w A pointer to the dialog box.
+ *
+ * @param user_data Unknown
+ *
+ * @return void
+ */
+static void
+decode_destroy_cb (GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have a "Decode As" dialog box. */
+ decode_w = NULL;
+}
+
+
+/**************************************************/
+/* Dialog setup - radio buttons */
+/**************************************************/
+
+/*
+ * Update the requested action field of the dialog. This routine is
+ * called by GTK when either of the two radio buttons in the dialog is
+ * clicked.
+ *
+ * @param w The radio button that was clicked.
+ *
+ * @param data The enum value assigned to this radio button. This
+ * will be either E_DECODE_YES or E_DECODE_NO
+ */
+static void
+decode_update_action (GtkWidget *w _U_, gpointer data)
+{
+ GSList *tmp;
+ gboolean enable;
+
+ requested_action = GPOINTER_TO_INT(data);
+ enable = (requested_action == E_DECODE_YES);
+ for (tmp = decode_dimmable; tmp; tmp = g_slist_next(tmp)) {
+ gtk_widget_set_sensitive(tmp->data, enable);
+ }
+}
+
+/*
+ * This routine is called to create the "Decode" and "Do not decode"
+ * radio buttons. These buttons are installed into a vbox, and set up
+ * as a format group.
+ *
+ * @return GtkWidget * A pointer to the vbox containing the buttons
+ */
+static GtkWidget *
+decode_add_yes_no (void)
+{
+ GtkWidget *format_vb, *radio_button;
+ GSList *format_grp;
+
+ format_vb = gtk_vbox_new(FALSE, 2);
+
+ radio_button = gtk_radio_button_new_with_label(NULL, "Decode");
+ format_grp = gtk_radio_button_group(GTK_RADIO_BUTTON(radio_button));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_button), TRUE);
+ g_signal_connect(G_OBJECT(radio_button), "clicked",
+ G_CALLBACK(decode_update_action),
+ GINT_TO_POINTER(E_DECODE_YES));
+ gtk_box_pack_start(GTK_BOX(format_vb), radio_button, TRUE, TRUE, 0);
+
+ radio_button = gtk_radio_button_new_with_label(format_grp, "Do not decode");
+ format_grp = gtk_radio_button_group(GTK_RADIO_BUTTON(radio_button));
+ g_signal_connect(G_OBJECT(radio_button), "clicked",
+ G_CALLBACK(decode_update_action),
+ GINT_TO_POINTER(E_DECODE_NO));
+ gtk_box_pack_start(GTK_BOX(format_vb), radio_button, TRUE, TRUE, 0);
+
+ return(format_vb);
+}
+
+/**************************************************/
+/* Dialog setup - simple menus */
+/**************************************************/
+
+/*
+ * This routine is called to pack an option menu into an aligment, so
+ * that it doesn't expand vertically to fill up the space available to
+ * it.
+ *
+ * @param optmenu A pointer to the option menu to be so packed.
+ *
+ * @return GtkWidget * A pointer to the newly created alignment.
+ */
+static GtkWidget *
+decode_add_pack_menu (GtkWidget *optmenu)
+{
+ GtkWidget *alignment;
+
+ alignment = gtk_alignment_new(0.0, 0.5, 0.0, 0.0);
+ gtk_container_add(GTK_CONTAINER(alignment), optmenu);
+
+ return(alignment);
+}
+
+
+/*
+ * This routine is called to add the transport port selection menu to
+ * the dialog box. This is a three choice menu: source, destination
+ * and both. The default choice for the menu is set to the source
+ * port number of the currently selected packet.
+ *
+ * @param page A pointer notebook page that will contain all
+ * widgets created by this routine.
+ *
+ * @return GtkWidget * A pointer to the newly created alignment into
+ * which we've packed the newly created option menu.
+ */
+static GtkWidget *
+decode_add_srcdst_menu (GtkWidget *page)
+{
+ GtkWidget *optmenu, *menu, *menuitem, *alignment;
+ gchar tmp[100];
+
+ optmenu = gtk_option_menu_new();
+ menu = gtk_menu_new();
+ sprintf(tmp, "source (%u)", cfile.edt->pi.srcport);
+ menuitem = gtk_menu_item_new_with_label(tmp);
+ gtk_object_set_user_data(GTK_OBJECT(menuitem),
+ GINT_TO_POINTER(E_DECODE_SPORT));
+ gtk_menu_append(GTK_MENU(menu), menuitem);
+ gtk_widget_show(menuitem); /* gtk_widget_show_all() doesn't show this */
+
+ sprintf(tmp, "destination (%u)", cfile.edt->pi.destport);
+ menuitem = gtk_menu_item_new_with_label(tmp);
+ gtk_object_set_user_data(GTK_OBJECT(menuitem),
+ GINT_TO_POINTER(E_DECODE_DPORT));
+ gtk_menu_append(GTK_MENU(menu), menuitem);
+ gtk_widget_show(menuitem); /* gtk_widget_show_all() doesn't show this */
+
+ menuitem = gtk_menu_item_new_with_label("both");
+ gtk_object_set_user_data(GTK_OBJECT(menuitem),
+ GINT_TO_POINTER(E_DECODE_BPORT));
+ gtk_menu_append(GTK_MENU(menu), menuitem);
+ gtk_widget_show(menuitem); /* gtk_widget_show_all() doesn't show this */
+
+ gtk_object_set_data(GTK_OBJECT(page), E_MENU_SRCDST, menu);
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
+
+ alignment = decode_add_pack_menu(optmenu);
+
+ return(alignment);
+}
+
+/**************************************************/
+/* Dialog setup - list based menus */
+/**************************************************/
+
+struct handle_lookup_info {
+ dissector_handle_t handle;
+ gboolean found;
+};
+
+static gboolean
+lookup_handle(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
+ gpointer data)
+{
+ dissector_handle_t handle;
+ struct handle_lookup_info *hli = (struct handle_lookup_info *)data;
+
+ gtk_tree_model_get(model, iter, E_LIST_S_TABLE+1, &handle, -1);
+ if (hli->handle == handle) {
+ hli->found = TRUE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * This routine creates one entry in the list of protocol dissector
+ * that can be used. It is called by the dissector_table_foreach_handle
+ * routine once for each entry in a dissector table's list of handles
+ * for dissectors that could be used in that table. It guarantees unique
+ * entries by iterating over the list of entries build up to this point,
+ * looking for a duplicate name. If there is no duplicate, then this
+ * entry is added to the list of possible dissectors.
+ *
+ * @param table_name The name of the dissector table currently
+ * being walked.
+ *
+ * @param value The dissector handle for this entry. This is an opaque
+ * pointer that can only be handed back to routines in the file packet.c
+ *
+ * @param user_data A data block passed into each instance of this
+ * routine. It contains information from the caller of the foreach
+ * routine, specifying information about the dissector table and where
+ * to store any information generated by this routine.
+ */
+static void
+decode_add_to_list(gchar *table_name, gpointer value, gpointer user_data)
+{
+ GtkTreeView *list;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ gchar *proto_name;
+ gchar *text[E_LIST_S_COLUMNS];
+ dissector_handle_t handle;
+ struct handle_lookup_info hli;
+
+ g_assert(user_data);
+ g_assert(value);
+
+ list = (GtkTreeView *)user_data;
+ handle = value;
+ proto_name = dissector_handle_get_short_name(handle);
+
+ hli.handle = handle;
+ hli.found = FALSE;
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(list));
+ gtk_tree_model_foreach(GTK_TREE_MODEL(store), lookup_handle, &hli);
+ if (hli.found) {
+ /*
+ * We already have an entry for this handle.
+ * XXX - will this ever happen?
+ */
+ return;
+ }
+
+ text[E_LIST_S_PROTO_NAME] = proto_name;
+ text[E_LIST_S_TABLE] = table_name;
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ E_LIST_S_PROTO_NAME, text[E_LIST_S_PROTO_NAME],
+ E_LIST_S_TABLE, text[E_LIST_S_TABLE],
+ E_LIST_S_TABLE+1, handle, -1);
+}
+
+
+/*
+ * This routine starts the creation of a List on a notebook page. It
+ * creates both a scrolled window and a list, adds the list to the
+ * window, and attaches the list as a data object on the page.
+ *
+ * @param page A pointer to the notebook page being created.
+ *
+ * @param list_p Will be filled in with the address of a newly
+ * created List.
+ *
+ * @param scrolled_win_p Will be filled in with the address of a newly
+ * created GtkScrolledWindow.
+ */
+static void
+decode_list_menu_start (GtkWidget *page, GtkTreeView **list_p,
+ GtkWidget **scrolled_win_p)
+{
+ GtkTreeView *list;
+ GtkWidget *window;
+ GtkListStore *store;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *tc;
+
+ store = gtk_list_store_new(E_LIST_S_COLUMNS+1, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_POINTER);
+ list = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)));
+ *list_p = list;
+ gtk_tree_view_set_headers_clickable(list, FALSE);
+#ifndef DEBUG
+ gtk_tree_view_set_headers_visible(list, FALSE);
+#endif
+ renderer = gtk_cell_renderer_text_new();
+ tc = gtk_tree_view_column_new_with_attributes("Short Name", renderer,
+ "text", E_LIST_S_PROTO_NAME,
+ NULL);
+ gtk_tree_view_column_set_sizing(tc, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(list, tc);
+ gtk_object_set_data(GTK_OBJECT(page), E_PAGE_LIST, list);
+
+ *scrolled_win_p = window = gtk_scrolled_window_new(NULL, NULL);
+ /* Provide a minimum of a couple of rows worth of data */
+ gtk_widget_set_size_request(window, -1, E_DECODE_MIN_HEIGHT);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(window),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(list));
+}
+
+/*
+ * This routine finishes the creation of a List on a notebook page.
+ * It adds the default entry, sets the default entry as the
+ * highlighted entry, and sorts the List.
+ *
+ * @param list A pointer the the List to finish.
+ */
+static void
+decode_list_menu_finish (GtkTreeView *list)
+{
+ gchar *text[E_LIST_S_COLUMNS];
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(list));
+ text[E_LIST_S_PROTO_NAME] = "(default)";
+ text[E_LIST_S_TABLE] = "(none)";
+ gtk_list_store_prepend(store, &iter);
+ gtk_list_store_set(store, &iter,
+ E_LIST_S_PROTO_NAME, text[E_LIST_S_PROTO_NAME],
+ E_LIST_S_TABLE, text[E_LIST_S_TABLE],
+ E_LIST_S_TABLE+1, NULL, -1);
+
+ gtk_tree_selection_select_iter(gtk_tree_view_get_selection(list), &iter);
+ /* gtk_clist_sort(clist); */
+}
+
+/*
+ * This routine is called to add the dissector selection list to a
+ * notebook page. This scrolled list contains an entry labeled
+ * "default", and an entry for each protocol that has had a dissector
+ * registered. The default choice for the list is set to the
+ * "default" choice, which will return the protocol/port selections to
+ * their original dissector(s).
+ *
+ * @param page A pointer to the notebook page currently being created.
+ *
+ * @param table_name The name of the dissector table to use to build
+ * this (list) menu.
+ *
+ * @return GtkWidget * A pointer to the newly created list within a
+ * scrolled window.
+ */
+static GtkWidget *
+decode_add_simple_menu (GtkWidget *page, gchar *table_name)
+{
+ GtkWidget *scrolled_window;
+ GtkTreeView *list;
+
+ decode_list_menu_start(page, &list, &scrolled_window);
+ dissector_table_foreach_handle(table_name, decode_add_to_list, list);
+ decode_list_menu_finish(list);
+ return(scrolled_window);
+}
+
+/**************************************************/
+/* Dialog setup */
+/**************************************************/
+
+/*
+ * This routine creates a sample notebook page in the dialog box.
+ * This notebook page provides a prompt specifying what is being
+ * changed and its current value (e.g. "IP Protocol number (17)"), and
+ * a list specifying all the available choices. The list of choices
+ * is conditionally enabled, based upon the setting of the
+ * "decode"/"do not decode" radio buttons.
+ *
+ * @param prompt The prompt for this notebook page
+ *
+ * @param title A title for this page to use when debugging.
+ *
+ * @param table_name The name of the dissector table to use to
+ * build this page.
+ *
+ * @param value The protocol/port value that is to be changed.
+ *
+ * @return GtkWidget * A pointer to the notebook page created by this
+ * routine.
+ */
+static GtkWidget *
+decode_add_simple_page (gchar *prompt, gchar *title, gchar *table_name,
+ gint value)
+{
+ GtkWidget *page, *label, *scrolled_window;
+
+ page = gtk_hbox_new(FALSE, 5);
+ gtk_object_set_data(GTK_OBJECT(page), E_PAGE_ACTION, decode_simple);
+ gtk_object_set_data(GTK_OBJECT(page), E_PAGE_TABLE, table_name);
+ gtk_object_set_data(GTK_OBJECT(page), E_PAGE_TITLE, title);
+ gtk_object_set_data(GTK_OBJECT(page), E_PAGE_VALUE, GINT_TO_POINTER(value));
+
+ /* Always enabled */
+ label = gtk_label_new(prompt);
+ gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
+
+ /* Conditionally enabled - only when decoding packets */
+ label = gtk_label_new("as");
+ gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
+ decode_dimmable = g_slist_prepend(decode_dimmable, label);
+ scrolled_window = decode_add_simple_menu(page, table_name);
+ gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
+ decode_dimmable = g_slist_prepend(decode_dimmable, scrolled_window);
+
+ return(page);
+}
+
+
+/*
+ * This routine creates the TCP or UDP notebook page in the dialog box.
+ * All items created by this routine are packed into a single
+ * horizontal box. First is a label indicating whether the port(s) for
+ * which the user can set the dissection is a TCP port or a UDP port.
+ * Second is a menu allowing the user to select whether the source port,
+ * destination port, or both ports will have dissectors added for them.
+ * Last is a (conditionally enabled) popup menu listing all possible
+ * dissectors that can be used to decode the packets, and the choice
+ * or returning to the default dissector for these ports.
+ *
+ * The defaults for these items are the transport layer protocol of
+ * the currently selected packet, the source port of the currently
+ * selected packet, and the "default dissector".
+ *
+ * @param prompt The prompt for this notebook page
+ *
+ * @param table_name The name of the dissector table to use to
+ * build this page.
+ *
+ * @return GtkWidget * A pointer to the notebook page created by
+ * this routine.
+ */
+static GtkWidget *
+decode_add_tcpudp_page (gchar *prompt, gchar *table_name)
+{
+ GtkWidget *page, *label, *scrolled_window, *optmenu;
+
+ page = gtk_hbox_new(FALSE, 5);
+ gtk_object_set_data(GTK_OBJECT(page), E_PAGE_ACTION, decode_transport);
+ gtk_object_set_data(GTK_OBJECT(page), E_PAGE_TABLE, table_name);
+ gtk_object_set_data(GTK_OBJECT(page), E_PAGE_TITLE, "Transport");
+
+ /* Always enabled */
+ label = gtk_label_new(prompt);
+ gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
+ optmenu = decode_add_srcdst_menu(page);
+ gtk_box_pack_start(GTK_BOX(page), optmenu, TRUE, TRUE, 0);
+ label = gtk_label_new("port(s)");
+ gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
+
+ /* Conditionally enabled - only when decoding packets */
+ label = gtk_label_new("as");
+ gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
+ decode_dimmable = g_slist_prepend(decode_dimmable, label);
+ scrolled_window = decode_add_simple_menu(page, table_name);
+ gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
+ decode_dimmable = g_slist_prepend(decode_dimmable, scrolled_window);
+
+ return(page);
+}
+
+/*
+ * This routine indicates whether we'd actually have any pages in the
+ * notebook in a "Decode As" dialog box; if there wouldn't be, we
+ * inactivate the menu item for "Decode As".
+ */
+gboolean
+decode_as_ok(void)
+{
+ return cfile.edt->pi.ethertype || cfile.edt->pi.ipproto ||
+ cfile.edt->pi.ptype == PT_TCP || cfile.edt->pi.ptype == PT_UDP;
+}
+
+
+/*
+ * This routine creates the bulk of the "Decode As" dialog box. All
+ * items created by this routine are packed as pages into a notebook.
+ * There will be a page for each protocol layer that can be change.
+ *
+ * @param GtkWidget * A pointer to the widget in which the notebook
+ * should be installed.
+ */
+static void
+decode_add_notebook (GtkWidget *format_hb)
+{
+ GtkWidget *notebook, *page, *label;
+ gchar buffer[40];
+
+ /* Start a nootbook for flipping between sets of changes */
+ notebook = gtk_notebook_new();
+ gtk_container_add(GTK_CONTAINER(format_hb), notebook);
+ gtk_object_set_data(GTK_OBJECT(decode_w), E_NOTEBOOK, notebook);
+
+ /* Add link level selection page */
+ if (cfile.edt->pi.ethertype) {
+ sprintf(buffer, "Ethertype 0x%04x", cfile.edt->pi.ethertype);
+ page = decode_add_simple_page(buffer, "Link", "ethertype", cfile.edt->pi.ethertype);
+ label = gtk_label_new("Link");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
+ }
+
+ /* Add network selection page */
+ if (cfile.edt->pi.ipproto) {
+ /*
+ * The network-layer protocol is IP.
+ */
+ sprintf(buffer, "IP protocol %u", cfile.edt->pi.ipproto);
+ page = decode_add_simple_page(buffer, "Network", "ip.proto", cfile.edt->pi.ipproto);
+ gtk_object_set_data(GTK_OBJECT(page), E_PAGE_ACTION, decode_simple);
+ label = gtk_label_new("Network");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
+ }
+
+ /* Add transport selection page */
+ switch (cfile.edt->pi.ptype) {
+
+ case PT_TCP:
+ page = decode_add_tcpudp_page("TCP", "tcp.port");
+ break;
+
+ case PT_UDP:
+ page = decode_add_tcpudp_page("UDP", "udp.port");
+ break;
+
+ default:
+ page = NULL;
+ break;
+ }
+ if (page != NULL) {
+ label = gtk_label_new("Transport");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
+ }
+
+ /* Select the last added page (selects first by default) */
+ /* Notebook must be visible for set_page to work. */
+ gtk_widget_show_all(notebook);
+ gtk_notebook_set_page(GTK_NOTEBOOK(notebook), -1);
+}
+
+
+/*
+ * This routine creates the "Decode As" dialog box. This dialog box
+ * asks the user which protocol to use for decoding the currently
+ * selected packet. This will affect the last packet that we called a
+ * dissection routine on belongs (this might be the most recently
+ * selected packet, or it might be the last packet in the file).
+ *
+ * This routine uses an auxiliary function to create the bulk of the
+ * dialog box, and then hand crafts the button box at the bottom of
+ * the dialog.
+ *
+ * @param w Unknown
+ * @param data Unknown
+ */
+void
+decode_as_cb (GtkWidget * w _U_, gpointer data _U_)
+{
+ GtkWidget *main_vb, *format_hb, *bbox, *ok_bt, *cancel_bt, *button;
+ GtkWidget *button_vb;
+
+ if (decode_w != NULL) {
+ /* There's already a "Decode As" dialog box; reactivate it. */
+ reactivate_window(decode_w);
+ return;
+ }
+
+ requested_action = E_DECODE_YES;
+ decode_w = dlg_window_new("Ethereal: Decode As");
+ g_signal_connect(G_OBJECT(decode_w), "delete_event",
+ G_CALLBACK(decode_delete_cb), NULL);
+ g_signal_connect(G_OBJECT(decode_w), "destroy",
+ G_CALLBACK(decode_destroy_cb), NULL);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 2);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(decode_w), main_vb);
+
+ /* First row - Buttons and Notebook */
+ {
+ format_hb = gtk_hbox_new(FALSE, 5);
+ gtk_box_pack_start(GTK_BOX(main_vb), format_hb, TRUE, TRUE, 10);
+
+ button_vb = decode_add_yes_no();
+ gtk_box_pack_start(GTK_BOX(format_hb), button_vb, TRUE, TRUE, 10);
+
+ decode_add_notebook(format_hb);
+ }
+
+ /* Button row: OK and cancel buttons */
+ 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_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 10);
+
+ ok_bt = gtk_button_new_from_stock(GTK_STOCK_OK);
+ g_signal_connect(G_OBJECT(ok_bt), "clicked", G_CALLBACK(decode_ok_cb),
+ GTK_OBJECT(decode_w));
+ GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start(GTK_BOX(bbox), ok_bt, FALSE, FALSE, 0);
+ gtk_widget_grab_default(ok_bt);
+
+ button = gtk_button_new_with_label("Show Current");
+ g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(decode_show_cb),
+ GTK_OBJECT(decode_w));
+ GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+ gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+
+ cancel_bt = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+ g_signal_connect(G_OBJECT(cancel_bt), "clicked",
+ G_CALLBACK(decode_cancel_cb), GTK_OBJECT(decode_w));
+ GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start(GTK_BOX(bbox), cancel_bt, FALSE, FALSE, 0);
+
+ /*
+ * 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(decode_w, cancel_bt);
+
+ gtk_widget_show_all(decode_w);
+}
+
+
+/*
+ * Local Variables:
+ * mode:c
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/gtk2/decode_as_dlg.h b/gtk2/decode_as_dlg.h
new file mode 100644
index 0000000000..85c8db7268
--- /dev/null
+++ b/gtk2/decode_as_dlg.h
@@ -0,0 +1,33 @@
+/* decode_as_dlg.c
+ *
+ * $Id: decode_as_dlg.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Routines to modify dissector tables on the fly.
+ *
+ * By David Hampton <dhampton@mac.com>
+ * Copyright 2001 David Hampton
+ *
+ * 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.
+ *
+ */
+
+#ifndef __DECODE_AS_DLG_H__
+#define __DECODE_AS_DLG_H__
+
+void decode_as_cb(GtkWidget *, gpointer);
+void decode_show_cb(GtkWidget *, gpointer);
+gboolean decode_as_ok(void);
+
+#endif
diff --git a/gtk2/dfilter_expr_dlg.c b/gtk2/dfilter_expr_dlg.c
new file mode 100644
index 0000000000..218c7319fa
--- /dev/null
+++ b/gtk2/dfilter_expr_dlg.c
@@ -0,0 +1,1207 @@
+/* dfilter_expr_dlg.c
+ *
+ * Allow the user to construct a subexpression of a display filter
+ * expression, testing a particular field; display the tree of fields
+ * and the relations and values with which it can be compared.
+ *
+ * Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com> and
+ * Guy Harris <guy@alum.mit.edu>
+ *
+ * $Id: dfilter_expr_dlg.c,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 2000 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.
+ */
+
+/* Todo -
+ * may want to check the enable field to decide if protocol should be in tree
+ * improve speed of dialog box creation
+ * - I believe this is slow because of tree widget creation.
+ * 1) could improve the widget
+ * 2) keep a copy in memory after the first time.
+ * user can pop multiple tree dialogs by pressing the "Tree" button multiple
+ * time. not a good thing.
+ * Sort the protocols and children
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef NEED_SNPRINTF_H
+# include "snprintf.h"
+#endif
+
+#include "globals.h"
+#include "main.h"
+#include "ui_util.h"
+#include "simple_dialog.h"
+#include "dlg_utils.h"
+#include "proto_dlg.h"
+#include "filter_prefs.h"
+#include "dfilter_expr_dlg.h"
+
+#define E_DFILTER_EXPR_TREE_KEY "dfilter_expr_tree"
+#define E_DFILTER_EXPR_CURRENT_VAR_KEY "dfilter_expr_current_var"
+#define E_DFILTER_EXPR_RELATION_LABEL_KEY "dfilter_expr_relation_label"
+#define E_DFILTER_EXPR_RELATION_LIST_KEY "dfilter_expr_relation_list"
+#define E_DFILTER_EXPR_RANGE_LABEL_KEY "dfilter_expr_range_label"
+#define E_DFILTER_EXPR_RANGE_ENTRY_KEY "dfilter_expr_range_entry"
+#define E_DFILTER_EXPR_VALUE_LABEL_KEY "dfilter_expr_value_label"
+#define E_DFILTER_EXPR_VALUE_ENTRY_KEY "dfilter_expr_value_entry"
+#define E_DFILTER_EXPR_VALUE_LIST_KEY "dfilter_expr_value_list"
+#define E_DFILTER_EXPR_VALUE_LIST_SW_KEY "dfilter_expr_value_list_sw"
+#define E_DFILTER_EXPR_ACCEPT_BT_KEY "dfilter_expr_accept_bt"
+#define E_DFILTER_EXPR_VALUE_KEY "dfilter_expr_value"
+
+typedef struct protocol_data {
+ char *abbrev;
+ int hfinfo_index;
+} protocol_data_t;
+
+static void show_relations(GtkWidget *relation_label, GtkWidget *relation_list,
+ ftenum_t ftype);
+static gboolean relation_is_presence_test(const char *string);
+static void add_relation_list(GtkWidget *relation_list, char *relation);
+static void build_boolean_values(GtkWidget *value_list_scrolled_win,
+ GtkWidget *value_list, const true_false_string *values);
+static void build_enum_values(GtkWidget *value_list_scrolled_win,
+ GtkWidget *value_list, const value_string *values);
+static void add_value_list_item(GtkWidget *value_list, gchar *string,
+ gpointer data);
+static void display_value_fields(header_field_info *hfinfo,
+ gboolean is_comparison, GtkWidget *value_label, GtkWidget *value_entry,
+ GtkWidget *value_list, GtkWidget *value_list_scrolled_win,
+ GtkWidget *range_label, GtkWidget *range_entry);
+
+/*
+ * Note that this is called every time the user clicks on an item,
+ * whether it is already selected or not.
+ */
+static void
+field_select_row_cb(GtkTreeSelection *sel, gpointer user_data)
+{
+ GtkWidget *window = gtk_widget_get_toplevel(user_data);
+ GtkWidget *relation_label = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_RELATION_LABEL_KEY);
+ GtkWidget *relation_list = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_RELATION_LIST_KEY);
+ GtkWidget *range_label = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_RANGE_LABEL_KEY);
+ GtkWidget *range_entry = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_RANGE_ENTRY_KEY);
+ GtkWidget *value_label = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_LABEL_KEY);
+ GtkWidget *value_entry = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_ENTRY_KEY);
+ GtkWidget *value_list = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_LIST_KEY);
+ GtkWidget *value_list_scrolled_win = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_LIST_SW_KEY);
+ GtkWidget *accept_bt = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_ACCEPT_BT_KEY);
+ header_field_info *hfinfo, *cur_hfinfo;
+ const char *value_type;
+ char value_label_string[1024+1]; /* XXX - should be large enough */
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ gtk_tree_selection_get_selected(sel, &model, &iter);
+ gtk_tree_model_get(model, &iter, 1, &hfinfo, -1);
+
+ /*
+ * What was the item that was last selected?
+ */
+ cur_hfinfo = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_CURRENT_VAR_KEY);
+ if (cur_hfinfo == hfinfo) {
+ /*
+ * It's still selected; no need to change anything.
+ */
+ return;
+ }
+
+ /*
+ * Mark it as currently selected.
+ */
+ gtk_object_set_data(GTK_OBJECT(window), E_DFILTER_EXPR_CURRENT_VAR_KEY,
+ hfinfo);
+
+ show_relations(relation_label, relation_list, hfinfo->type);
+
+ /*
+ * Set the label for the value to indicate what type of value
+ * it is.
+ */
+ value_type = ftype_pretty_name(hfinfo->type);
+ if (value_type != NULL) {
+ /*
+ * Indicate what type of value it is.
+ */
+ snprintf(value_label_string, sizeof value_label_string,
+ "Value (%s)", value_type);
+ gtk_label_set_text(GTK_LABEL(value_label), value_label_string);
+ }
+
+ /*
+ * Clear the entry widget for the value, as whatever
+ * was there before doesn't apply.
+ */
+ gtk_entry_set_text(GTK_ENTRY(value_entry), "");
+
+ switch (hfinfo->type) {
+
+ case FT_BOOLEAN:
+ /*
+ * The list of values should be the strings for "true"
+ * and "false"; show them in the value list.
+ */
+ build_boolean_values(value_list_scrolled_win, value_list,
+ hfinfo->strings);
+ 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:
+ /*
+ * If this has a value_string table associated with it,
+ * fill up the list of values, otherwise clear the list
+ * of values.
+ */
+ if (hfinfo->strings != NULL) {
+ build_enum_values(value_list_scrolled_win, value_list,
+ hfinfo->strings);
+ } else
+ gtk_list_clear_items(GTK_LIST(value_list), 0, -1);
+ break;
+
+ default:
+ /*
+ * Clear the list of values.
+ */
+ gtk_list_clear_items(GTK_LIST(value_list), 0, -1);
+ break;
+ }
+
+ /*
+ * Display various items for the value, as appropriate.
+ * The relation we start out with is never a comparison.
+ */
+ display_value_fields(hfinfo, FALSE, value_label, value_entry,
+ value_list, value_list_scrolled_win, range_label, range_entry);
+
+ /*
+ * XXX - in browse mode, there always has to be something
+ * selected, so this should always be sensitive.
+ */
+ gtk_widget_set_sensitive(accept_bt, TRUE);
+}
+
+static void
+show_relations(GtkWidget *relation_label, GtkWidget *relation_list,
+ ftenum_t ftype)
+{
+ /*
+ * Clear out the currently displayed list of relations.
+ */
+ gtk_list_clear_items(GTK_LIST(relation_list), 0, -1);
+
+ /*
+ * Add the supported relations.
+ */
+ add_relation_list(relation_list, "is present");
+ if (ftype_can_eq(ftype) ||
+ (ftype_can_slice(ftype) && ftype_can_eq(FT_BYTES)))
+ add_relation_list(relation_list, "==");
+ if (ftype_can_ne(ftype) ||
+ (ftype_can_slice(ftype) && ftype_can_ne(FT_BYTES)))
+ add_relation_list(relation_list, "!=");
+ if (ftype_can_gt(ftype) ||
+ (ftype_can_slice(ftype) && ftype_can_gt(FT_BYTES)))
+ add_relation_list(relation_list, ">");
+ if (ftype_can_lt(ftype) ||
+ (ftype_can_slice(ftype) && ftype_can_lt(FT_BYTES)))
+ add_relation_list(relation_list, "<");
+ if (ftype_can_ge(ftype) ||
+ (ftype_can_slice(ftype) && ftype_can_ge(FT_BYTES)))
+ add_relation_list(relation_list, ">=");
+ if (ftype_can_le(ftype) ||
+ (ftype_can_slice(ftype) && ftype_can_le(FT_BYTES)))
+ add_relation_list(relation_list, "<=");
+
+ /*
+ * And show the list.
+ */
+ gtk_widget_show(relation_label);
+ gtk_widget_show(relation_list);
+}
+
+/*
+ * Given a string that represents a test to be made on a field, returns
+ * TRUE if it tests for the field's presence, FALSE otherwise.
+ */
+static gboolean
+relation_is_presence_test(const char *string)
+{
+ return (strcmp(string, "is present") == 0);
+}
+
+static void
+add_relation_list(GtkWidget *relation_list, char *relation)
+{
+ GtkWidget *label, *item;
+
+ label = gtk_label_new(relation);
+ item = gtk_list_item_new();
+
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+ gtk_container_add(GTK_CONTAINER(item), label);
+ gtk_widget_show(label);
+ gtk_container_add(GTK_CONTAINER(relation_list), item);
+ gtk_widget_show(item);
+}
+
+static void
+relation_list_sel_cb(GtkList *relation_list, GtkWidget *child _U_,
+ gpointer user_data _U_)
+{
+ GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(relation_list));
+ GtkWidget *range_label = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_RANGE_LABEL_KEY);
+ GtkWidget *range_entry = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_RANGE_ENTRY_KEY);
+ GtkWidget *value_label = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_LABEL_KEY);
+ GtkWidget *value_entry = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_ENTRY_KEY);
+ GtkWidget *value_list = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_LIST_KEY);
+ GtkWidget *value_list_scrolled_win = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_LIST_SW_KEY);
+ header_field_info *hfinfo = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_CURRENT_VAR_KEY);
+ GList *sl;
+ GtkWidget *item, *item_label;
+ gchar *item_str;
+
+ /*
+ * What's the relation?
+ */
+ sl = GTK_LIST(relation_list)->selection;
+ item = GTK_WIDGET(sl->data);
+ item_label = GTK_BIN(item)->child;
+ gtk_label_get(GTK_LABEL(item_label), &item_str);
+
+ /*
+ * Update the display of various items for the value, as appropriate.
+ */
+ display_value_fields(hfinfo,
+ !relation_is_presence_test(item_str),
+ value_label, value_entry, value_list, value_list_scrolled_win,
+ range_label, range_entry);
+}
+
+static void
+build_boolean_values(GtkWidget *value_list_scrolled_win, GtkWidget *value_list,
+ const true_false_string *values)
+{
+ static const true_false_string true_false = { "True", "False" };
+
+ /*
+ * Clear out the items for the list, and put in the names
+ * from the value_string list.
+ */
+ gtk_list_clear_items(GTK_LIST(value_list), 0, -1);
+
+ /*
+ * Put the list in single mode, so we don't get any selection
+ * events while we're building it (i.e., so we don't get any
+ * on a list item BEFORE WE GET TO SET THE DATA FOR THE LIST
+ * ITEM SO THAT THE HANDLER CAN HANDLE IT).
+ */
+ gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_SINGLE);
+
+ /*
+ * Build the list.
+ */
+ if (values == NULL)
+ values = &true_false;
+ add_value_list_item(value_list, values->true_string, (gpointer)values);
+ add_value_list_item(value_list, values->false_string, NULL);
+
+ /*
+ * OK, we're done, so we can finally put it in browse mode.
+ * Select the first item, so that the user doesn't have to, under
+ * the assumption that they're most likely to test if something
+ * is true, not false.
+ */
+ gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_BROWSE);
+ gtk_list_select_item(GTK_LIST(value_list), 0);
+
+ gtk_widget_show_all(value_list_scrolled_win);
+}
+
+static void
+build_enum_values(GtkWidget *value_list_scrolled_win _U_, GtkWidget *value_list,
+ const value_string *values)
+{
+ /*
+ * Clear out the items for the list, and put in the names
+ * from the value_string list.
+ */
+ gtk_list_clear_items(GTK_LIST(value_list), 0, -1);
+
+ /*
+ * Put the list in single mode, so we don't get any selection
+ * events while we're building it (i.e., so we don't get any
+ * on a list item BEFORE WE GET TO SET THE DATA FOR THE LIST
+ * ITEM SO THAT THE HANDLER CAN HANDLE IT).
+ */
+ gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_SINGLE);
+
+ /*
+ * Build the list.
+ */
+ while (values->strptr != NULL) {
+ add_value_list_item(value_list, values->strptr,
+ (gpointer)values);
+ values++;
+ }
+
+ /*
+ * OK, we're done, so we can finally put it in browse mode.
+ */
+ gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_BROWSE);
+}
+
+static void
+add_value_list_item(GtkWidget *value_list, gchar *string, gpointer data)
+{
+ GtkWidget *label, *item;
+
+ label = gtk_label_new(string);
+ item = gtk_list_item_new();
+
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+ gtk_container_add(GTK_CONTAINER(item), label);
+ gtk_widget_show(label);
+ gtk_container_add(GTK_CONTAINER(value_list), item);
+ gtk_object_set_data(GTK_OBJECT(item), E_DFILTER_EXPR_VALUE_KEY, data);
+ gtk_widget_show(item);
+}
+
+/*
+ * Show or hide the various values fields as appropriate for the field
+ * and currently-selected relation.
+ */
+static void
+display_value_fields(header_field_info *hfinfo, gboolean is_comparison,
+ GtkWidget *value_label, GtkWidget *value_entry, GtkWidget *value_list _U_,
+ GtkWidget *value_list_scrolled_win, GtkWidget *range_label,
+ GtkWidget *range_entry)
+{
+ gboolean show_value_label = FALSE;
+
+ /*
+ * Either:
+ *
+ * this is an FT_NONE variable, in which case you can
+ * only check whether it's present or absent in the
+ * protocol tree
+ *
+ * or
+ *
+ * this is a Boolean variable, in which case you
+ * can't specify a value to compare with, you can
+ * only specify whether to test for the Boolean
+ * being true or to test for it being false
+ *
+ * or
+ *
+ * this isn't a Boolean variable, in which case you
+ * can test for its presence in the protocol tree,
+ * and the default relation is such a test, in
+ * which case you don't compare with a value
+ *
+ * so we hide the value entry.
+ */
+ if (is_comparison) {
+ /*
+ * The relation is a comparison; display the entry for
+ * the value with which to compare.
+ */
+ gtk_widget_show(value_entry);
+
+ /*
+ * We're showing the entry; show the label as well.
+ */
+ show_value_label = TRUE;
+ } else {
+ /*
+ * The relation isn't a comparison; there's no value with
+ * which to compare, so don't show the entry for it.
+ */
+ gtk_widget_hide(value_entry);
+ }
+
+ switch (hfinfo->type) {
+
+ case FT_BOOLEAN:
+ if (is_comparison) {
+ /*
+ * The relation is a comparison, so we're showing
+ * an entry for the value with which to compare;
+ * show the list of names for values as well.
+ * (The list of values contains the strings for
+ * "true" and "false".)
+ */
+ gtk_widget_show_all(value_list_scrolled_win);
+
+ /*
+ * We're showing the value list; show the label as
+ * well.
+ */
+ show_value_label = TRUE;
+ } else {
+ /*
+ * It's not a comparison, so we're not showing
+ * the entry for the value; don't show the
+ * list of names for values, either.
+ */
+ gtk_widget_hide_all(value_list_scrolled_win);
+ }
+ 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:
+ if (hfinfo->strings != NULL) {
+ /*
+ * We have a list of values to show.
+ */
+ if (is_comparison) {
+ /*
+ * The relation is a comparison, so we're
+ * showing an entry for the value with
+ * which to compare; show the list of
+ * names for values as well.
+ */
+ gtk_widget_show_all(value_list_scrolled_win);
+
+ /*
+ * We're showing the entry; show the label
+ * as well.
+ */
+ show_value_label = TRUE;
+ } else {
+ /*
+ * It's not a comparison, so we're not showing
+ * the entry for the value; don't show the
+ * list of names for values, either.
+ */
+ gtk_widget_hide_all(value_list_scrolled_win);
+ }
+ } else {
+ /*
+ * There is no list of names for values, so don't
+ * show it.
+ */
+ gtk_widget_hide_all(value_list_scrolled_win);
+ }
+ break;
+
+ default:
+ /*
+ * There is no list of names for values; hide the list.
+ */
+ gtk_widget_hide_all(value_list_scrolled_win);
+ break;
+ }
+
+ if (show_value_label)
+ gtk_widget_show(value_label);
+ else
+ gtk_widget_hide(value_label);
+
+ /*
+ * Is this a comparison, and are ranges supported by this type?
+ * If both are true, show the range stuff, otherwise hide it.
+ */
+ if (is_comparison && ftype_can_slice(hfinfo->type)) {
+ gtk_widget_show(range_label);
+ gtk_widget_show(range_entry);
+ } else {
+ gtk_widget_hide(range_label);
+ gtk_widget_hide(range_entry);
+ }
+}
+
+static void
+value_list_sel_cb(GtkList *value_list, GtkWidget *child,
+ gpointer value_entry_arg)
+{
+ GtkWidget *value_entry = value_entry_arg;
+ GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(value_list));
+ header_field_info *hfinfo = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_CURRENT_VAR_KEY);
+ const value_string *value;
+ char value_string[11+1]; /* long enough for 32-bit octal value */
+
+ value = gtk_object_get_data(GTK_OBJECT(child),
+ E_DFILTER_EXPR_VALUE_KEY);
+
+ /*
+ * This should either be a numeric type or a Boolean type.
+ */
+ if (hfinfo->type == FT_BOOLEAN) {
+ /*
+ * Boolean type; if the value key for the selected item
+ * is non-null, it's the item for "true", otherwise it's
+ * the item for "false". Compare with 1 if we're
+ * testing for "true", and compare with 0 if we're
+ * testing for "false".
+ */
+ if (value != NULL)
+ strcpy(value_string, "1");
+ else
+ strcpy(value_string, "0");
+ } else {
+ /*
+ * Numeric type; get the value corresponding to the
+ * selected item, and display it in the base for this
+ * field.
+ */
+ switch (hfinfo->display) {
+
+ case BASE_DEC:
+ case BASE_BIN: /* binary - treated as decimal, for now */
+ switch (hfinfo->type) {
+
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT32:
+ snprintf(value_string, sizeof value_string,
+ "%u", value->value);
+ break;
+
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT32:
+ snprintf(value_string, sizeof value_string,
+ "%d", value->value);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+ break;
+
+ case BASE_HEX:
+ snprintf(value_string, sizeof value_string, "0x%x",
+ value->value);
+ break;
+
+ case BASE_OCT:
+ snprintf(value_string, sizeof value_string, "%#o",
+ value->value);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+ }
+ gtk_entry_set_text(GTK_ENTRY(value_entry), value_string);
+}
+
+static void
+dfilter_report_bad_value(char *format, ...)
+{
+ char error_msg_buf[1024];
+ va_list args;
+
+ va_start(args, format);
+ vsnprintf(error_msg_buf, sizeof error_msg_buf, format, args);
+ va_end(args);
+
+ simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
+ "%s", error_msg_buf);
+}
+
+static void
+dfilter_expr_dlg_accept_cb(GtkWidget *w, gpointer filter_te_arg)
+{
+ GtkWidget *filter_te = filter_te_arg;
+ GtkWidget *window = gtk_widget_get_toplevel(w);
+ GtkWidget *relation_list = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_RELATION_LIST_KEY);
+ GtkWidget *range_entry = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_RANGE_ENTRY_KEY);
+ GtkWidget *value_entry = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_ENTRY_KEY);
+ header_field_info *hfinfo;
+ GList *sl;
+ GtkWidget *item, *item_label;
+ gchar *item_str;
+ gchar *range_str, *stripped_range_str;
+ gchar *value_str, *stripped_value_str;
+ int pos;
+ gchar *chars;
+ ftenum_t ftype;
+ gboolean can_compare;
+ fvalue_t *fvalue;
+
+ /*
+ * Get the variable to be tested.
+ */
+ hfinfo = gtk_object_get_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_CURRENT_VAR_KEY);
+
+ /*
+ * Get the relation to use, if any.
+ */
+ if (GTK_WIDGET_VISIBLE(relation_list)) {
+ /*
+ * The list of relations is visible, so we can get a
+ * relation operator from it.
+ */
+ sl = GTK_LIST(relation_list)->selection;
+ item = GTK_WIDGET(sl->data);
+ item_label = GTK_BIN(item)->child;
+ gtk_label_get(GTK_LABEL(item_label), &item_str);
+ } else
+ item_str = NULL; /* no relation operator */
+
+ /*
+ * Get the range to use, if any.
+ */
+ if (GTK_WIDGET_VISIBLE(range_entry)) {
+ range_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(range_entry)));
+ stripped_range_str = g_strstrip(range_str);
+ if (strcmp(stripped_range_str, "") == 0) {
+ /*
+ * No range was specified.
+ */
+ g_free(range_str);
+ range_str = NULL;
+ stripped_range_str = NULL;
+ }
+
+ /*
+ * XXX - check it for validity?
+ */
+ } else {
+ range_str = NULL;
+ stripped_range_str = NULL;
+ }
+
+ /*
+ * If a range was specified, the type of the LHS of the
+ * comparison is FT_BYTES; otherwise, it's the type of the field.
+ */
+ if (range_str == NULL)
+ ftype = hfinfo->type;
+ else
+ ftype = FT_BYTES;
+
+ /*
+ * Make sure the relation is valid for the type in question.
+ * We may be offering relations that the type of the field
+ * can't support, because the field's type supports slicing,
+ * and the relation *is* supported on byte strings.
+ */
+ if (strcmp(item_str, "==") == 0)
+ can_compare = ftype_can_eq(ftype);
+ else if (strcmp(item_str, "!=") == 0)
+ can_compare = ftype_can_ne(ftype);
+ else if (strcmp(item_str, ">") == 0)
+ can_compare = ftype_can_gt(ftype);
+ else if (strcmp(item_str, "<") == 0)
+ can_compare = ftype_can_lt(ftype);
+ else if (strcmp(item_str, ">=") == 0)
+ can_compare = ftype_can_ge(ftype);
+ else if (strcmp(item_str, "<=") == 0)
+ can_compare = ftype_can_le(ftype);
+ else
+ can_compare = TRUE; /* not a comparison */
+ if (!can_compare) {
+ if (range_str == NULL) {
+ simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
+ "That field cannot be tested with \"%s\".",
+ item_str);
+ } else {
+ simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
+ "Ranges of that field cannot be tested with \"%s\".",
+ item_str);
+ }
+ if (range_str != NULL)
+ g_free(range_str);
+ return;
+ }
+
+ /*
+ * Get the value to use, if any.
+ */
+ if (GTK_WIDGET_VISIBLE(value_entry)) {
+ value_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(value_entry)));
+ stripped_value_str = g_strstrip(value_str);
+ if (strcmp(stripped_value_str, "") == 0) {
+ /*
+ * This field takes a value, but they didn't supply
+ * one.
+ */
+ simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
+ "That field must be compared with a value, "
+ "but you didn't specify a value with which to "
+ "compare it.");
+ if (range_str != NULL)
+ g_free(range_str);
+ g_free(value_str);
+ return;
+ }
+
+ /*
+ * Make sure the value is valid.
+ *
+ * If no range string was specified, it must be valid
+ * for the type of the field; if a range string was
+ * specified, must be valid for FT_BYTES.
+ */
+ fvalue = fvalue_from_string(ftype, stripped_value_str,
+ dfilter_report_bad_value);
+ if (fvalue == NULL) {
+ /*
+ * It's not valid.
+ *
+ * The dialog box was already popped up by
+ * "dfilter_report_bad_value()".
+ */
+ if (range_str != NULL)
+ g_free(range_str);
+ g_free(value_str);
+ return;
+ }
+ fvalue_free(fvalue);
+ } else {
+ value_str = NULL;
+ stripped_value_str = NULL;
+ }
+
+ /*
+ * Insert the expression at the current cursor position.
+ * If there's a non-whitespace character to the left of it,
+ * insert a blank first; if there's a non-whitespace character
+ * to the right of it, insert a blank after it.
+ */
+ pos = gtk_editable_get_position(GTK_EDITABLE(filter_te));
+ chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos, pos + 1);
+ if (strcmp(chars, "") != 0 && !isspace((unsigned char)chars[0]))
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
+ g_free(chars);
+
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), hfinfo->abbrev,
+ strlen(hfinfo->abbrev), &pos);
+ if (range_str != NULL) {
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), "[", 1, &pos);
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te),
+ stripped_range_str, strlen(stripped_range_str), &pos);
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), "]", 1, &pos);
+ g_free(range_str);
+ }
+ if (item_str != NULL && !relation_is_presence_test(item_str)) {
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), item_str,
+ strlen(item_str), &pos);
+ }
+ if (value_str != NULL) {
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
+ switch (hfinfo->type) {
+
+ case FT_STRING:
+ case FT_STRINGZ:
+ case FT_UINT_STRING:
+ /*
+ * Put quotes around the string.
+ */
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
+ 1, &pos);
+
+ default:
+ break;
+ }
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te),
+ stripped_value_str, strlen(stripped_value_str), &pos);
+ switch (hfinfo->type) {
+
+ case FT_STRING:
+ case FT_STRINGZ:
+ case FT_UINT_STRING:
+ /*
+ * Put quotes around the string.
+ */
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
+ 1, &pos);
+
+ default:
+ break;
+ }
+ g_free(value_str);
+ }
+ chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos + 1, pos + 2);
+ if (strcmp(chars, "") != 0 && !isspace((unsigned char)chars[0]))
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
+ g_free(chars);
+
+ /*
+ * Put the cursor after the expression we just entered into
+ * the text entry widget.
+ */
+ gtk_editable_set_position(GTK_EDITABLE(filter_te), pos);
+
+ /*
+ * We're done; destroy the dialog box (which is the top-level
+ * widget for the "Accept" button).
+ */
+ gtk_widget_destroy(window);
+}
+
+static void
+dfilter_expr_dlg_cancel_cb(GtkWidget *w _U_, gpointer parent_w)
+{
+ /*
+ * User pressed the cancel button; close the dialog box.
+ */
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+dfilter_expr_dlg_destroy_cb(GtkWidget *w, gpointer filter_te)
+{
+ /*
+ * The dialog box is being destroyed; disconnect from the
+ * "destroy" signal on the text entry box to which we're
+ * attached, as the handler for that signal is supposed
+ * to destroy us, but we're already gone.
+ */
+ g_signal_handlers_disconnect_by_func(GTK_OBJECT(filter_te),
+ dfilter_expr_dlg_cancel_cb, w);
+}
+
+void
+dfilter_expr_dlg_new(GtkWidget *filter_te)
+{
+ GtkWidget *window;
+ GtkWidget *main_vb;
+ GtkWidget *hb;
+ GtkWidget *col1_vb;
+ GtkWidget *tree_label, *tree, *tree_scrolled_win;
+ GtkWidget *col2_vb;
+ GtkWidget *relation_label, *relation_list;
+ GtkWidget *range_label, *range_entry;
+ GtkWidget *value_vb;
+ GtkWidget *value_label, *value_entry, *value_list_scrolled_win, *value_list;
+ GtkWidget *list_bb, *alignment, *accept_bt, *close_bt;
+ header_field_info *hfinfo;
+ int i, len;
+ GtkTreeStore *store;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeIter iter;
+ gint col_offset;
+
+ window = dlg_window_new("Ethereal: Filter Expression");
+ gtk_container_set_border_width(GTK_CONTAINER(window), 5);
+
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(window), main_vb);
+ gtk_widget_show(main_vb);
+
+ hb = gtk_hbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(hb), 5);
+ gtk_container_add(GTK_CONTAINER(main_vb), hb);
+ gtk_widget_show(hb);
+
+ col1_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(col1_vb), 5);
+ gtk_container_add(GTK_CONTAINER(hb), col1_vb);
+ gtk_widget_show(col1_vb);
+
+ tree_label = gtk_label_new("Field name");
+ gtk_misc_set_alignment(GTK_MISC(tree_label), 0.0, 0.0);
+ gtk_box_pack_start(GTK_BOX(col1_vb), tree_label, FALSE, FALSE, 0);
+ gtk_widget_show(tree_label);
+
+ tree_scrolled_win = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(tree_scrolled_win),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_size_request(tree_scrolled_win, 300, 400);
+ gtk_box_pack_start(GTK_BOX(col1_vb), tree_scrolled_win, FALSE, FALSE, 0);
+ gtk_widget_show(tree_scrolled_win);
+
+ store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
+ tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), FALSE);
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
+ renderer = gtk_cell_renderer_text_new();
+ col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree),
+ -1, "Name", renderer,
+ "text", 0, NULL);
+ column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree),
+ col_offset - 1);
+ gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
+ GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ g_signal_connect(G_OBJECT(selection), "changed",
+ G_CALLBACK(field_select_row_cb), tree);
+ gtk_container_add(GTK_CONTAINER(tree_scrolled_win), tree);
+
+ col2_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(col2_vb), 5);
+ gtk_container_add(GTK_CONTAINER(hb), col2_vb);
+ gtk_widget_show(col2_vb);
+
+ relation_label = gtk_label_new("Relation");
+ gtk_misc_set_alignment(GTK_MISC(relation_label), 0.0, 0.0);
+ gtk_box_pack_start(GTK_BOX(col2_vb), relation_label, FALSE, FALSE, 0);
+
+ relation_list = gtk_list_new();
+ gtk_box_pack_start(GTK_BOX(col2_vb), relation_list, TRUE, TRUE, 0);
+ gtk_list_set_selection_mode(GTK_LIST(relation_list),
+ GTK_SELECTION_BROWSE);
+
+ range_label = gtk_label_new("Range (offset:length)");
+ gtk_misc_set_alignment(GTK_MISC(range_label), 0.0, 0.0);
+ gtk_box_pack_start(GTK_BOX(col2_vb), range_label, FALSE, FALSE, 0);
+
+ range_entry = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(col2_vb), range_entry, FALSE, FALSE, 0);
+
+ /*
+ * OK, show the relation label and range stuff as it would be
+ * with everything turned on, so it'll request as much space
+ * as it'll ever need, so the dialog box and widgets start out
+ * with the right sizes.
+ *
+ * XXX - this doesn't work. It *doesn't* request as much space
+ * as it'll ever need.
+ *
+ * XXX - FT_UINT8 doesn't support ranges, so even if it did work,
+ * it wouldn't work right.
+ *
+ * XXX - this no longer affects the range stuff, as that's
+ * controlled both by the type and by the relational operator
+ * selected.
+ */
+ show_relations(relation_label, relation_list, FT_UINT8);
+
+ value_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(value_vb), 5);
+ gtk_container_add(GTK_CONTAINER(hb), value_vb);
+ gtk_widget_show(value_vb);
+
+ value_label = gtk_label_new("Value");
+ gtk_misc_set_alignment(GTK_MISC(value_label), 0.0, 0.0);
+ gtk_box_pack_start(GTK_BOX(value_vb), value_label, FALSE, FALSE, 0);
+ gtk_widget_show(value_label);
+
+ value_entry = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(value_vb), value_entry, FALSE, FALSE, 0);
+ gtk_widget_show(value_entry);
+
+ value_list_scrolled_win = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(value_list_scrolled_win),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start(GTK_BOX(value_vb), value_list_scrolled_win, TRUE,
+ TRUE, 0);
+ gtk_widget_show(value_list_scrolled_win);
+
+ value_list = gtk_list_new();
+ gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(value_list_scrolled_win),
+ value_list);
+ g_signal_connect(G_OBJECT(value_list), "select-child",
+ G_CALLBACK(value_list_sel_cb), value_entry);
+ gtk_list_set_selection_mode(GTK_LIST(value_list), GTK_SELECTION_SINGLE);
+ /* This remains hidden until an enumerated field is selected */
+
+ /*
+ * The value stuff may be hidden or shown depending on what
+ * relation was selected; connect to the "select-child" signal
+ * for the relation list, so we can make that happen.
+ */
+ g_signal_connect(G_OBJECT(relation_list), "select-child",
+ G_CALLBACK(relation_list_sel_cb), NULL);
+
+ /*
+ * Put the items in the CTree; we don't want to do that until
+ * we've constructed the value list and set the tree's
+ * E_DFILTER_EXPR_VALUE_LIST_KEY data to point to it, and
+ * constructed the "Accept" button and set the tree's
+ * E_DFILTER_EXPR_ACCEPT_BT_KEY data to point to it, so that
+ * when the list item is "helpfully" automatically selected for us
+ * we're ready to cope with the selection signal.
+ */
+
+ /* proto_array = g_array_new(FALSE, FALSE, sizeof(GtkTreeIter));
+ for (i = proto_get_first_protocol(&cookie); i != -1;
+ i = proto_get_next_protocol(&cookie)) {
+ hfinfo = proto_registrar_get_nth(i); */
+ /* Create a node for the protocol, and remember it for
+ later use. */
+ /* name = proto_get_protocol_short_name(i);
+ gtk_tree_store_append(store, &iter, NULL);
+ gtk_tree_store_set(store, &iter, 0, name, 1, hfinfo, -1);
+ g_array_append_val(proto_array, iter);
+ } */
+
+ len = proto_registrar_n();
+ for (i = 0; i < len; i++) {
+ GtkTreeIter child_iter;
+
+ hfinfo = proto_registrar_get_nth(i);
+
+ if (hfinfo->type == FT_PROTOCOL)
+ {
+ /* Create a node for the protocol */
+ gtk_tree_store_append(store, &iter, NULL);
+ gtk_tree_store_set(store, &iter, 0, hfinfo->abbrev, 1, hfinfo, -1);
+ continue;
+ }
+
+ /*
+ * If this field isn't at the head of the list of
+ * fields with this name, skip this field - all
+ * fields with the same name are really just versions
+ * of the same field stored in different bits, and
+ * should have the same type/radix/value list, and
+ * just differ in their bit masks. (If a field isn't
+ * a bitfield, but can be, say, 1 or 2 bytes long,
+ * it can just be made FT_UINT16, meaning the
+ * *maximum* length is 2 bytes, and be used
+ * for all lengths.)
+ */
+ if (hfinfo->same_name_prev != NULL)
+ continue;
+
+ /* Create a node for the item, and put it
+ under its parent protocol. */
+ gtk_tree_store_append(store, &child_iter, &iter);
+ gtk_tree_store_set(store, &child_iter, 0, hfinfo->name, 1, hfinfo, -1);
+ }
+ g_object_unref(G_OBJECT(store));
+
+ /* g_array_free(proto_array, TRUE); */
+
+ gtk_widget_show_all(tree);
+
+ list_bb = gtk_hbutton_box_new();
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(list_bb), GTK_BUTTONBOX_START);
+ gtk_button_box_set_spacing(GTK_BUTTON_BOX(list_bb), 5);
+ gtk_container_add(GTK_CONTAINER(main_vb), list_bb);
+ gtk_widget_show(list_bb);
+
+ accept_bt = gtk_button_new_from_stock(GTK_STOCK_OK);
+ gtk_widget_set_sensitive(accept_bt, FALSE);
+ g_signal_connect(G_OBJECT(accept_bt), "clicked",
+ G_CALLBACK(dfilter_expr_dlg_accept_cb), filter_te);
+ GTK_WIDGET_SET_FLAGS(accept_bt, GTK_CAN_DEFAULT);
+ alignment = gtk_alignment_new(0.0, 0.5, 1.0, 0.0);
+ gtk_container_add(GTK_CONTAINER(alignment), accept_bt);
+ gtk_box_pack_start(GTK_BOX(list_bb), alignment, TRUE, TRUE, 0);
+ gtk_widget_grab_default(accept_bt);
+ gtk_widget_show(accept_bt);
+ gtk_widget_show(alignment);
+
+ close_bt = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+ g_signal_connect(G_OBJECT(close_bt), "clicked",
+ G_CALLBACK(dfilter_expr_dlg_cancel_cb), window);
+ alignment = gtk_alignment_new(0.0, 0.5, 1.0, 0.0);
+ gtk_container_add(GTK_CONTAINER(alignment), close_bt);
+ gtk_box_pack_start(GTK_BOX(list_bb), alignment, TRUE, TRUE, 0);
+ gtk_widget_show(close_bt);
+ gtk_widget_show(alignment);
+
+ /* Catch the "activate" signal on the range and value text entries,
+ so that if the user types Return there, we act as if the "Accept"
+ button had been selected, as happens if Return is typed if some
+ widget that *doesn't* handle the Return key has the input focus. */
+ dlg_set_activate(range_entry, accept_bt);
+ dlg_set_activate(value_entry, accept_bt);
+
+ /*
+ * Catch the "key_press_event" signal in the window, so that we can
+ * catch the ESC key being pressed and act as if the "Close" button
+ * had been selected.
+ */
+ dlg_set_cancel(window, close_bt);
+
+ gtk_object_set_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_RELATION_LABEL_KEY, relation_label);
+ gtk_object_set_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_RELATION_LIST_KEY, relation_list);
+ gtk_object_set_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_RANGE_LABEL_KEY, range_label);
+ gtk_object_set_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_RANGE_ENTRY_KEY, range_entry);
+ gtk_object_set_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_LABEL_KEY, value_label);
+ gtk_object_set_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_ENTRY_KEY, value_entry);
+ gtk_object_set_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_LIST_KEY, value_list);
+ gtk_object_set_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_LIST_SW_KEY, value_list_scrolled_win);
+ gtk_object_set_data(GTK_OBJECT(window),
+ E_DFILTER_EXPR_ACCEPT_BT_KEY, accept_bt);
+
+ /*
+ * Catch the "destroy" signal on our top-level window, and,
+ * when it's destroyed, disconnect the signal we'll be
+ * connecting below.
+ */
+ g_signal_connect(G_OBJECT(window), "destroy",
+ G_CALLBACK(dfilter_expr_dlg_destroy_cb), filter_te);
+
+ /*
+ * Catch the "destroy" signal on the text entry widget to which
+ * we're attached; if it's destroyed, we should destroy ourselves
+ * as well.
+ */
+ g_signal_connect(G_OBJECT(filter_te), "destroy",
+ G_CALLBACK(dfilter_expr_dlg_cancel_cb), window);
+
+ gtk_widget_show(window);
+}
diff --git a/gtk2/dfilter_expr_dlg.h b/gtk2/dfilter_expr_dlg.h
new file mode 100644
index 0000000000..1ec4ba8edb
--- /dev/null
+++ b/gtk2/dfilter_expr_dlg.h
@@ -0,0 +1,31 @@
+/* dfilter_expr_dlg.h
+ * Definitions for dialog boxes for display filter expression construction
+ *
+ * $Id: dfilter_expr_dlg.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+#ifndef __DFILTER_EXPR_DLG_H__
+#define __DFILTER_EXPR_DLG_H__
+
+void dfilter_expr_dlg_new(GtkWidget *);
+
+#endif /* dfilter_expr_dlg.h */
diff --git a/gtk2/display_opts.c b/gtk2/display_opts.c
new file mode 100644
index 0000000000..c2d9aa3b43
--- /dev/null
+++ b/gtk2/display_opts.c
@@ -0,0 +1,337 @@
+/* display_opts.c
+ * Routines for packet display windows
+ *
+ * $Id: display_opts.c,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <time.h>
+
+#ifdef HAVE_SYS_SOCKIO_H
+# include <sys/sockio.h>
+#endif
+
+#include "globals.h"
+#include <epan/resolv.h>
+#include <epan/timestamp.h>
+#include <epan/packet.h>
+#include "file.h"
+#include "display_opts.h"
+#include "ui_util.h"
+#include "dlg_utils.h"
+
+extern capture_file cfile;
+
+/* Display callback data keys */
+#define E_DISPLAY_TIME_ABS_KEY "display_time_abs"
+#define E_DISPLAY_DATE_TIME_ABS_KEY "display_date_time_abs"
+#define E_DISPLAY_TIME_REL_KEY "display_time_rel"
+#define E_DISPLAY_TIME_DELTA_KEY "display_time_delta"
+#ifdef HAVE_LIBPCAP
+#define E_DISPLAY_AUTO_SCROLL_KEY "display_auto_scroll"
+#endif
+#define E_DISPLAY_M_NAME_RESOLUTION_KEY "display_mac_name_resolution"
+#define E_DISPLAY_N_NAME_RESOLUTION_KEY "display_network_name_resolution"
+#define E_DISPLAY_T_NAME_RESOLUTION_KEY "display_transport_name_resolution"
+
+static void display_opt_ok_cb(GtkWidget *, gpointer);
+static void display_opt_apply_cb(GtkWidget *, gpointer);
+static void get_display_options(GtkWidget *);
+static void update_display(void);
+static void display_opt_close_cb(GtkWidget *, gpointer);
+static void display_opt_destroy_cb(GtkWidget *, gpointer);
+
+/*
+ * Keep a static pointer to the current "Display Options" window, if any,
+ * so that if somebody tries to do "Display:Options" while there's already
+ * a "Display Options" window up, we just pop up the existing one, rather
+ * than creating a new one.
+ */
+static GtkWidget *display_opt_w;
+
+static ts_type initial_timestamp_type;
+static ts_type current_timestamp_type;
+
+void
+display_opt_cb(GtkWidget *w _U_, gpointer d _U_) {
+ GtkWidget *button, *main_vb, *bbox, *ok_bt, *apply_bt, *cancel_bt;
+ GtkAccelGroup *accel_group;
+
+ if (display_opt_w != NULL) {
+ /* There's already a "Display Options" dialog box; reactivate it. */
+ reactivate_window(display_opt_w);
+ return;
+ }
+
+ /* Save the timestamp type value as of when the dialog box was first popped
+ up, so that "Cancel" can put it back if we've changed it with "Apply". */
+ initial_timestamp_type = timestamp_type;
+
+ /* Save the current timestamp type so that we know whether it has changed;
+ we don't want to redisplay the time fields unless we've changed the way
+ they should be displayed (as redisplaying the time fields could be
+ expensive - we have to scan through all the packets and rebuild the
+ packet list).*/
+ current_timestamp_type = timestamp_type;
+
+ display_opt_w = dlg_window_new("Ethereal: Display Options");
+ g_signal_connect(G_OBJECT(display_opt_w), "destroy",
+ G_CALLBACK(display_opt_destroy_cb), NULL);
+
+ /* Accelerator group for the accelerators (or, as they're called in
+ Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
+ Ctrl+<key> is an accelerator). */
+ accel_group = gtk_accel_group_new();
+ gtk_window_add_accel_group(GTK_WINDOW(display_opt_w), accel_group);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(display_opt_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ button = dlg_radio_button_new_with_label_with_mnemonic(NULL, "_Time of day",
+ accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button),
+ (timestamp_type == ABSOLUTE));
+ gtk_object_set_data(GTK_OBJECT(display_opt_w), E_DISPLAY_TIME_ABS_KEY,
+ button);
+ gtk_box_pack_start(GTK_BOX(main_vb), button, TRUE, TRUE, 0);
+
+ gtk_widget_show(button);
+
+ button = dlg_radio_button_new_with_label_with_mnemonic(
+ gtk_radio_button_group(GTK_RADIO_BUTTON(button)),
+ "_Date and time of day", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button),
+ (timestamp_type == ABSOLUTE_WITH_DATE));
+ gtk_object_set_data(GTK_OBJECT(display_opt_w), E_DISPLAY_DATE_TIME_ABS_KEY,
+ button);
+ gtk_box_pack_start(GTK_BOX(main_vb), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ button = dlg_radio_button_new_with_label_with_mnemonic(
+ gtk_radio_button_group(GTK_RADIO_BUTTON(button)),
+ "Seconds since _beginning of capture", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button),
+ (timestamp_type == RELATIVE));
+ gtk_object_set_data(GTK_OBJECT(display_opt_w), E_DISPLAY_TIME_REL_KEY,
+ button);
+ gtk_box_pack_start(GTK_BOX(main_vb), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ button = dlg_radio_button_new_with_label_with_mnemonic(
+ gtk_radio_button_group(GTK_RADIO_BUTTON(button)),
+ "Seconds since _previous frame", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button),
+ (timestamp_type == DELTA));
+ gtk_object_set_data(GTK_OBJECT(display_opt_w), E_DISPLAY_TIME_DELTA_KEY,
+ button);
+ gtk_box_pack_start(GTK_BOX(main_vb), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+#ifdef HAVE_LIBPCAP
+ button = dlg_check_button_new_with_label_with_mnemonic(
+ "_Automatic scrolling in live capture", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), auto_scroll_live);
+ gtk_object_set_data(GTK_OBJECT(display_opt_w), E_DISPLAY_AUTO_SCROLL_KEY,
+ button);
+ gtk_box_pack_start(GTK_BOX(main_vb), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+#endif
+
+ button = dlg_check_button_new_with_label_with_mnemonic(
+ "Enable _MAC name resolution", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button),
+ g_resolv_flags & RESOLV_MAC);
+ gtk_object_set_data(GTK_OBJECT(display_opt_w), E_DISPLAY_M_NAME_RESOLUTION_KEY,
+ button);
+ gtk_box_pack_start(GTK_BOX(main_vb), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ button = dlg_check_button_new_with_label_with_mnemonic(
+ "Enable _network name resolution", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button),
+ g_resolv_flags & RESOLV_NETWORK);
+ gtk_object_set_data(GTK_OBJECT(display_opt_w), E_DISPLAY_N_NAME_RESOLUTION_KEY,
+ button);
+ gtk_box_pack_start(GTK_BOX(main_vb), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ button = dlg_check_button_new_with_label_with_mnemonic(
+ "Enable _transport name resolution", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button),
+ g_resolv_flags & RESOLV_TRANSPORT);
+ gtk_object_set_data(GTK_OBJECT(display_opt_w), E_DISPLAY_T_NAME_RESOLUTION_KEY,
+ button);
+ gtk_box_pack_start(GTK_BOX(main_vb), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ /* Button row: OK, Apply, and Cancel buttons */
+ 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(main_vb), bbox);
+ gtk_widget_show(bbox);
+
+ ok_bt = gtk_button_new_from_stock(GTK_STOCK_OK);
+ g_signal_connect(G_OBJECT(ok_bt), "clicked", G_CALLBACK(display_opt_ok_cb),
+ GTK_OBJECT(display_opt_w));
+ GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
+ gtk_widget_grab_default(ok_bt);
+ gtk_widget_show(ok_bt);
+
+ apply_bt = gtk_button_new_from_stock(GTK_STOCK_APPLY);
+ g_signal_connect(G_OBJECT(apply_bt), "clicked",
+ G_CALLBACK(display_opt_apply_cb), GTK_OBJECT(display_opt_w));
+ GTK_WIDGET_SET_FLAGS(apply_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (bbox), apply_bt, TRUE, TRUE, 0);
+ gtk_widget_show(apply_bt);
+
+ cancel_bt = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+ g_signal_connect(G_OBJECT(cancel_bt), "clicked",
+ G_CALLBACK(display_opt_close_cb), GTK_OBJECT(display_opt_w));
+ GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
+ gtk_widget_show(cancel_bt);
+
+ /* 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(display_opt_w, cancel_bt);
+
+ gtk_widget_show(display_opt_w);
+}
+
+static void
+display_opt_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w) {
+ get_display_options(GTK_WIDGET(parent_w));
+
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+
+ update_display();
+}
+
+static void
+display_opt_apply_cb(GtkWidget *ok_bt _U_, gpointer parent_w) {
+ get_display_options(GTK_WIDGET(parent_w));
+
+ update_display();
+}
+
+static void
+get_display_options(GtkWidget *parent_w)
+{
+ GtkWidget *button;
+
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_DISPLAY_TIME_ABS_KEY);
+ if (GTK_TOGGLE_BUTTON (button)->active)
+ timestamp_type = ABSOLUTE;
+
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_DISPLAY_DATE_TIME_ABS_KEY);
+ if (GTK_TOGGLE_BUTTON (button)->active)
+ timestamp_type = ABSOLUTE_WITH_DATE;
+
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_DISPLAY_TIME_REL_KEY);
+ if (GTK_TOGGLE_BUTTON (button)->active)
+ timestamp_type = RELATIVE;
+
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_DISPLAY_TIME_DELTA_KEY);
+ if (GTK_TOGGLE_BUTTON (button)->active)
+ timestamp_type = DELTA;
+
+#ifdef HAVE_LIBPCAP
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_DISPLAY_AUTO_SCROLL_KEY);
+ auto_scroll_live = (GTK_TOGGLE_BUTTON (button)->active);
+#endif
+
+ g_resolv_flags = RESOLV_NONE;
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_DISPLAY_M_NAME_RESOLUTION_KEY);
+ g_resolv_flags |= (GTK_TOGGLE_BUTTON (button)->active ? RESOLV_MAC : RESOLV_NONE);
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_DISPLAY_N_NAME_RESOLUTION_KEY);
+ g_resolv_flags |= (GTK_TOGGLE_BUTTON (button)->active ? RESOLV_NETWORK : RESOLV_NONE);
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_DISPLAY_T_NAME_RESOLUTION_KEY);
+ g_resolv_flags |= (GTK_TOGGLE_BUTTON (button)->active ? RESOLV_TRANSPORT : RESOLV_NONE);
+
+}
+
+static void
+update_display(void)
+{
+ if (timestamp_type != current_timestamp_type) {
+ /* Time stamp format changed; update the display.
+
+ XXX - redissecting the packets could actually be faster;
+ we have to find the row number for each frame, in order to
+ update the time stamp columns, and doing that is linear in
+ the row number, which means the whole process is N^2 in
+ the number of rows, whilst redissecting the packets is only
+ linear in the number of rows (assuming you're using our
+ CList code, or the GTK+ 1.2.8 CList code, or other CList
+ code which doesn't have to scan the entire list to find the
+ last element), even though the latter involves doing more work
+ per packet. */
+ current_timestamp_type = timestamp_type;
+ change_time_formats(&cfile);
+ }
+}
+
+static void
+display_opt_close_cb(GtkWidget *close_bt _U_, gpointer parent_w)
+{
+ /* Revert the timestamp type to the value it has when we started. */
+ timestamp_type = initial_timestamp_type;
+
+ /* Update the display if either of those changed. */
+ update_display();
+
+ gtk_grab_remove(GTK_WIDGET(parent_w));
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+display_opt_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have a "Display Options" dialog box. */
+ display_opt_w = NULL;
+}
diff --git a/gtk2/display_opts.h b/gtk2/display_opts.h
new file mode 100644
index 0000000000..7de400c283
--- /dev/null
+++ b/gtk2/display_opts.h
@@ -0,0 +1,31 @@
+/* display_opts.h
+ * Definitions for display option window
+ *
+ * $Id: display_opts.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+#ifndef __DISPLAY_OPTS_H__
+#define __DISPLAY_OPTS_H__
+
+void display_opt_cb(GtkWidget *, gpointer);
+
+#endif /* display_opts.h */
diff --git a/gtk2/dlg_utils.c b/gtk2/dlg_utils.c
new file mode 100644
index 0000000000..9d5bd815bd
--- /dev/null
+++ b/gtk2/dlg_utils.c
@@ -0,0 +1,167 @@
+/* dlg_utils.c
+ * Utilities to use when constructing dialogs
+ *
+ * $Id: dlg_utils.c,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "gtkglobals.h"
+#include "ui_util.h"
+
+static void
+dlg_activate (GtkWidget *widget, gpointer ok_button);
+
+static gint
+dlg_key_press (GtkWidget *widget, GdkEventKey *event, gpointer cancel_button);
+
+/* Create a dialog box window that belongs to Ethereal's main window. */
+GtkWidget *
+dlg_window_new(const gchar *title)
+{
+ GtkWidget *win;
+
+ win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_transient_for(GTK_WINDOW(win), GTK_WINDOW(top_level));
+ gtk_window_set_title(GTK_WINDOW(win), title);
+ g_signal_connect(G_OBJECT(win), "realize",
+ G_CALLBACK(window_icon_realize_cb), NULL);
+ return win;
+}
+
+/* Set the "activate" signal for a widget to call a routine to
+ activate the "OK" button for a dialog box.
+
+ XXX - there should be a way to specify that a GtkEntry widget
+ shouldn't itself handle the Return key, but should let it be
+ passed on to the parent, so that you don't have to do this
+ by hand for every GtkEntry widget in a dialog box, but, alas,
+ there isn't. (Does this problem exist for other widgets?
+ I.e., are there any others that seize the Return key? */
+void
+dlg_set_activate(GtkWidget *widget, GtkWidget *ok_button)
+{
+ g_signal_connect(G_OBJECT(widget), "activate",
+ G_CALLBACK(dlg_activate), ok_button);
+}
+
+static void
+dlg_activate (GtkWidget *widget _U_, gpointer ok_button)
+{
+ gtk_widget_activate(GTK_WIDGET(ok_button));
+}
+
+/* Set the "key_press_event" signal for a top-level dialog window to
+ call a routine to activate the "Cancel" button for a dialog box if
+ the key being pressed is the <Esc> key.
+
+ XXX - there should be a GTK+ widget that'll do that for you, and
+ let you specify a "Cancel" button. It should also not impose
+ a requirement that there be a separator in the dialog box, as
+ the GtkDialog widget does; the visual convention that there's
+ such a separator between the rest of the dialog boxes and buttons
+ such as "OK" and "Cancel" is, for better or worse, not universal
+ (not even in GTK+ - look at the GtkFileSelection dialog!). */
+void
+dlg_set_cancel(GtkWidget *widget, GtkWidget *cancel_button)
+{
+ g_signal_connect(G_OBJECT(widget), "key_press_event",
+ G_CALLBACK(dlg_key_press), cancel_button);
+}
+
+static gint
+dlg_key_press (GtkWidget *widget, GdkEventKey *event, gpointer cancel_button)
+{
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ if (event->keyval == GDK_Escape) {
+ gtk_widget_activate(GTK_WIDGET(cancel_button));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Sigh. GTK+ appears not to acknowledge that it should be possible
+ to attach mnemonics to anything other than menu items; provide
+ routines to create radio and check buttons with labels that
+ include mnemonics. */
+typedef struct {
+ GtkWidget *button;
+ GtkAccelGroup *accel_group;
+} fix_label_args_t;
+
+static void
+dlg_fix_label_callback(GtkWidget *label_widget, gpointer data)
+{
+ fix_label_args_t *args = data;
+ gchar *label;
+ guint accel_key;
+
+ gtk_label_get(GTK_LABEL(label_widget), &label);
+ accel_key = gtk_label_parse_uline(GTK_LABEL(label_widget), label);
+ if (accel_key != GDK_VoidSymbol) {
+ /* Yes, we have a mnemonic. */
+ gtk_widget_add_accelerator(args->button, "clicked", args->accel_group,
+ accel_key, 0, GTK_ACCEL_LOCKED);
+ gtk_widget_add_accelerator(args->button, "clicked", args->accel_group,
+ accel_key, GDK_MOD1_MASK, GTK_ACCEL_LOCKED);
+ }
+}
+
+static void
+dlg_fix_button_label(GtkWidget *button, GtkAccelGroup *accel_group)
+{
+ fix_label_args_t args;
+
+ args.button = button;
+ args.accel_group = accel_group;
+ gtk_container_foreach(GTK_CONTAINER(button), dlg_fix_label_callback, &args);
+}
+
+GtkWidget *
+dlg_radio_button_new_with_label_with_mnemonic(GSList *group,
+ const gchar *label, GtkAccelGroup *accel_group)
+{
+ GtkWidget *radio_button;
+
+ radio_button = gtk_radio_button_new_with_label (group, label);
+ dlg_fix_button_label(radio_button, accel_group);
+ return radio_button;
+}
+
+GtkWidget *
+dlg_check_button_new_with_label_with_mnemonic(const gchar *label,
+ GtkAccelGroup *accel_group)
+{
+ GtkWidget *check_button;
+
+ check_button = gtk_check_button_new_with_label (label);
+ dlg_fix_button_label(check_button, accel_group);
+ return check_button;
+}
diff --git a/gtk2/dlg_utils.h b/gtk2/dlg_utils.h
new file mode 100644
index 0000000000..3068f1e02e
--- /dev/null
+++ b/gtk2/dlg_utils.h
@@ -0,0 +1,46 @@
+/* dlg_utils.h
+ * Declarations of utilities to use when constructing dialogs
+ *
+ * $Id: dlg_utils.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+#ifndef __DLG_UTILS_H__
+#define __DLG_UTILS_H__
+
+/* Create a dialog box window that belongs to Ethereal's main window. */
+GtkWidget *dlg_window_new(const gchar *);
+
+/* Set the "activate" signal for a widget to call a routine to
+ activate the "OK" button for a dialog box. */
+void dlg_set_activate(GtkWidget *widget, GtkWidget *ok_button);
+
+/* Set the "key_press_event" signal for a top-level dialog window to
+ call a routine to activate the "Cancel" button for a dialog box if
+ the key being pressed is the <Esc> key. */
+void dlg_set_cancel(GtkWidget *widget, GtkWidget *cancel_button);
+
+GtkWidget *dlg_radio_button_new_with_label_with_mnemonic(GSList *group,
+ const gchar *label, GtkAccelGroup *accel_group);
+GtkWidget *dlg_check_button_new_with_label_with_mnemonic(const gchar *label,
+ GtkAccelGroup *accel_group);
+
+#endif
diff --git a/gtk2/file_dlg.c b/gtk2/file_dlg.c
new file mode 100644
index 0000000000..97b4ec8b20
--- /dev/null
+++ b/gtk2/file_dlg.c
@@ -0,0 +1,647 @@
+/* file_dlg.c
+ * Dialog boxes for handling files
+ *
+ * $Id: file_dlg.c,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_DIRECT_H
+#include <direct.h>
+#endif
+
+#include <string.h>
+
+#include <glib.h>
+
+#include <epan/filesystem.h>
+
+#include "globals.h"
+#include "gtkglobals.h"
+#include <epan/resolv.h>
+#include "keys.h"
+#include "filter_prefs.h"
+#include "ui_util.h"
+#include "simple_dialog.h"
+#include "menu.h"
+#include "file_dlg.h"
+#include "dlg_utils.h"
+#include "main.h"
+
+static void file_open_ok_cb(GtkWidget *w, GtkFileSelection *fs);
+static void file_open_destroy_cb(GtkWidget *win, gpointer user_data);
+static void select_file_type_cb(GtkWidget *w, gpointer data);
+static void file_save_as_ok_cb(GtkWidget *w, GtkFileSelection *fs);
+static void file_save_as_destroy_cb(GtkWidget *win, gpointer user_data);
+
+#define E_FILE_M_RESOLVE_KEY "file_dlg_mac_resolve_key"
+#define E_FILE_N_RESOLVE_KEY "file_dlg_network_resolve_key"
+#define E_FILE_T_RESOLVE_KEY "file_dlg_transport_resolve_key"
+
+/*
+ * Keep a static pointer to the current "Open Capture File" window, if
+ * any, so that if somebody tries to do "File:Open" while there's already
+ * an "Open Capture File" window up, we just pop up the existing one,
+ * rather than creating a new one.
+ */
+static GtkWidget *file_open_w;
+
+/* Open a file */
+void
+file_open_cmd_cb(GtkWidget *w, gpointer data _U_)
+{
+ GtkWidget *main_vb, *filter_hbox, *filter_bt, *filter_te,
+ *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
+ GtkAccelGroup *accel_group;
+ /* No Apply button, and "OK" just sets our text widget, it doesn't
+ activate it (i.e., it doesn't cause us to try to open the file). */
+ static construct_args_t args = {
+ "Ethereal: Read Filter",
+ FALSE,
+ FALSE
+ };
+
+ if (file_open_w != NULL) {
+ /* There's already an "Open Capture File" dialog box; reactivate it. */
+ reactivate_window(file_open_w);
+ return;
+ }
+
+ file_open_w = gtk_file_selection_new ("Ethereal: Open Capture File");
+ g_signal_connect(G_OBJECT(file_open_w), "destroy",
+ G_CALLBACK(file_open_destroy_cb), NULL);
+
+ /* Accelerator group for the accelerators (or, as they're called in
+ Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
+ Ctrl+<key> is an accelerator). */
+ accel_group = gtk_accel_group_new();
+ gtk_window_add_accel_group(GTK_WINDOW(file_open_w), accel_group);
+
+ /* If we've opened a file, start out by showing the files in the directory
+ in which that file resided. */
+ if (last_open_dir)
+ gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_open_w), last_open_dir);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(file_open_w)->action_area),
+ main_vb, FALSE, FALSE, 0);
+ gtk_widget_show(main_vb);
+
+ filter_hbox = gtk_hbox_new(FALSE, 1);
+ gtk_container_border_width(GTK_CONTAINER(filter_hbox), 0);
+ gtk_box_pack_start(GTK_BOX(main_vb), filter_hbox, FALSE, FALSE, 0);
+ gtk_widget_show(filter_hbox);
+
+ filter_bt = gtk_button_new_with_label("Filter:");
+ g_signal_connect(G_OBJECT(filter_bt), "clicked",
+ G_CALLBACK(display_filter_construct_cb), &args);
+ gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
+ gtk_widget_show(filter_bt);
+
+ filter_te = gtk_entry_new();
+ gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
+ gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
+ gtk_widget_show(filter_te);
+
+ gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_open_w)->ok_button),
+ E_RFILTER_TE_KEY, filter_te);
+
+ m_resolv_cb = dlg_check_button_new_with_label_with_mnemonic(
+ "Enable _MAC name resolution", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(m_resolv_cb),
+ g_resolv_flags & RESOLV_MAC);
+ gtk_box_pack_start(GTK_BOX(main_vb), m_resolv_cb, FALSE, FALSE, 0);
+ gtk_widget_show(m_resolv_cb);
+ gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_open_w)->ok_button),
+ E_FILE_M_RESOLVE_KEY, m_resolv_cb);
+
+ n_resolv_cb = dlg_check_button_new_with_label_with_mnemonic(
+ "Enable _network name resolution", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(n_resolv_cb),
+ g_resolv_flags & RESOLV_NETWORK);
+ gtk_box_pack_start(GTK_BOX(main_vb), n_resolv_cb, FALSE, FALSE, 0);
+ gtk_widget_show(n_resolv_cb);
+ gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_open_w)->ok_button),
+ E_FILE_N_RESOLVE_KEY, n_resolv_cb);
+
+ t_resolv_cb = dlg_check_button_new_with_label_with_mnemonic(
+ "Enable _transport name resolution", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(t_resolv_cb),
+ g_resolv_flags & RESOLV_TRANSPORT);
+ gtk_box_pack_start(GTK_BOX(main_vb), t_resolv_cb, FALSE, FALSE, 0);
+ gtk_widget_show(t_resolv_cb);
+ gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_open_w)->ok_button),
+ E_FILE_T_RESOLVE_KEY, t_resolv_cb);
+
+ /* Connect the ok_button to file_open_ok_cb function and pass along a
+ pointer to the file selection box widget */
+ g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(file_open_w)->ok_button),
+ "clicked", G_CALLBACK(file_open_ok_cb), file_open_w);
+
+ gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_open_w)->ok_button),
+ E_DFILTER_TE_KEY, gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY));
+
+ /* Connect the cancel_button to destroy the widget */
+ gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(file_open_w)->cancel_button),
+ "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT(file_open_w));
+
+ /* 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(file_open_w, GTK_FILE_SELECTION(file_open_w)->cancel_button);
+
+ gtk_widget_show(file_open_w);
+}
+
+static void
+file_open_ok_cb(GtkWidget *w, GtkFileSelection *fs) {
+ gchar *cf_name, *s;
+ GtkWidget *filter_te, *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
+ dfilter_t *rfcode = NULL;
+ int err;
+ const gchar *rfilter;
+
+ cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
+ filter_te = gtk_object_get_data(GTK_OBJECT(w), E_RFILTER_TE_KEY);
+ rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
+ if (!dfilter_compile(rfilter, &rfcode)) {
+ simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
+ return;
+ }
+
+ /* Perhaps the user specified a directory instead of a file.
+ Check whether they did. */
+ if (test_for_directory(cf_name) == EISDIR) {
+ /* It's a directory - set the file selection box to display that
+ directory, don't try to open the directory as a capture file. */
+ set_last_open_dir(cf_name);
+ gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
+ return;
+ }
+
+ /* Try to open the capture file. */
+ if ((err = open_cap_file(cf_name, FALSE, &cfile)) != 0) {
+ /* We couldn't open it; don't dismiss the open dialog box,
+ just leave it around so that the user can, after they
+ dismiss the alert box popped up for the open error,
+ try again. */
+ if (rfcode != NULL)
+ dfilter_free(rfcode);
+ g_free(cf_name);
+ return;
+ }
+
+ /* Attach the new read filter to "cf" ("open_cap_file()" succeeded, so
+ it closed the previous capture file, and thus destroyed any
+ previous read filter attached to "cf"). */
+ cfile.rfcode = rfcode;
+
+ /* Set the global resolving variable */
+ g_resolv_flags = 0;
+ m_resolv_cb = gtk_object_get_data(GTK_OBJECT(w), E_FILE_M_RESOLVE_KEY);
+ g_resolv_flags |= GTK_TOGGLE_BUTTON (m_resolv_cb)->active ? RESOLV_MAC : RESOLV_NONE;
+ n_resolv_cb = gtk_object_get_data(GTK_OBJECT(w), E_FILE_N_RESOLVE_KEY);
+ g_resolv_flags |= GTK_TOGGLE_BUTTON (n_resolv_cb)->active ? RESOLV_NETWORK : RESOLV_NONE;
+ t_resolv_cb = gtk_object_get_data(GTK_OBJECT(w), E_FILE_T_RESOLVE_KEY);
+ g_resolv_flags |= GTK_TOGGLE_BUTTON (t_resolv_cb)->active ? RESOLV_TRANSPORT : RESOLV_NONE;
+
+ /* We've crossed the Rubicon; get rid of the file selection box. */
+ gtk_widget_hide(GTK_WIDGET (fs));
+ gtk_widget_destroy(GTK_WIDGET (fs));
+
+ switch (read_cap_file(&cfile, &err)) {
+
+ case READ_SUCCESS:
+ case READ_ERROR:
+ /* Just because we got an error, that doesn't mean we were unable
+ to read any of the file; we handle what we could get from the
+ file. */
+ break;
+
+ case READ_ABORTED:
+ /* The user bailed out of re-reading the capture file; the
+ capture file has been closed - just free the capture file name
+ string and return (without changing the last containing
+ directory). */
+ g_free(cf_name);
+ return;
+ }
+
+ /* Save the name of the containing directory specified in the path name,
+ if any; we can write over cf_name, which is a good thing, given that
+ "get_dirname()" does write over its argument. */
+ s = get_dirname(cf_name);
+ set_last_open_dir(s);
+
+ g_free(cf_name);
+}
+
+static void
+file_open_destroy_cb(GtkWidget *win, gpointer user_data _U_)
+{
+ GtkWidget *file_open_filter_w;
+
+ /* Is there a filter edit/selection dialog associated with this
+ Open Capture File dialog? */
+ file_open_filter_w = gtk_object_get_data(GTK_OBJECT(win), E_FILT_DIALOG_PTR_KEY);
+
+ if (file_open_filter_w != NULL) {
+ /* Yes. Destroy it. */
+ gtk_widget_destroy(file_open_filter_w);
+ }
+
+ /* Note that we no longer have a "Open Capture File" dialog box. */
+ file_open_w = NULL;
+}
+
+/* Close a file */
+void
+file_close_cmd_cb(GtkWidget *widget _U_, gpointer data _U_) {
+ close_cap_file(&cfile);
+}
+
+void
+file_save_cmd_cb(GtkWidget *w, gpointer data) {
+ /* If the file's already been saved, do nothing. */
+ if (cfile.user_saved)
+ return;
+
+ /* Do a "Save As". */
+ file_save_as_cmd_cb(w, data);
+}
+
+/* XXX - can we make these not be static? */
+static gboolean filtered;
+static gboolean marked;
+static int filetype;
+static GtkWidget *filter_cb;
+static GtkWidget *mark_cb;
+static GtkWidget *ft_om;
+
+static gboolean
+can_save_with_wiretap(int ft)
+{
+ /* To save a file with Wiretap, Wiretap has to handle that format,
+ and its code to handle that format must be able to write a file
+ with this file's encapsulation type. */
+ return wtap_dump_can_open(ft) && wtap_dump_can_write_encap(ft, cfile.lnk_t);
+}
+
+/* Generate a list of the file types we can save this file as.
+
+ "filetype" is the type it has now.
+
+ "encap" is the encapsulation for its packets (which could be
+ "unknown" or "per-packet").
+
+ "filtered" is TRUE if we're to save only the packets that passed
+ the display filter (in which case we have to save it using Wiretap)
+ and FALSE if we're to save the entire file (in which case, if we're
+ saving it in the type it has already, we can just copy it).
+
+ "marked" is TRUE if we have to save only the marked packets,
+ the same remark as "filtered" applies.
+*/
+static void
+set_file_type_list(GtkWidget *option_menu)
+{
+ GtkWidget *ft_menu, *ft_menu_item;
+ int ft;
+ guint index;
+ guint item_to_select;
+
+ /* Default to the first supported file type, if the file's current
+ type isn't supported. */
+ item_to_select = 0;
+
+ ft_menu = gtk_menu_new();
+
+ /* Check all file types. */
+ index = 0;
+ for (ft = 0; ft < WTAP_NUM_FILE_TYPES; ft++) {
+ if (filtered || marked || ft != cfile.cd_t) {
+ /* Filtered, marked or a different file type. We have to use Wiretap. */
+ if (!can_save_with_wiretap(ft))
+ continue; /* We can't. */
+ }
+
+ /* OK, we can write it out in this type. */
+ ft_menu_item = gtk_menu_item_new_with_label(wtap_file_type_string(ft));
+ if (ft == filetype) {
+ /* Default to the same format as the file, if it's supported. */
+ item_to_select = index;
+ }
+ g_signal_connect(G_OBJECT(ft_menu_item), "activate",
+ G_CALLBACK(select_file_type_cb), (gpointer)ft);
+ gtk_menu_append(GTK_MENU(ft_menu), ft_menu_item);
+ gtk_widget_show(ft_menu_item);
+ index++;
+ }
+
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), ft_menu);
+ gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu), item_to_select);
+}
+
+static void
+select_file_type_cb(GtkWidget *w _U_, gpointer data)
+{
+ int new_filetype = (int)data;
+
+ if (filetype != new_filetype) {
+ /* We can select only the filtered or marked packets to be saved if we can
+ use Wiretap to save the file. */
+ gtk_widget_set_sensitive(filter_cb, can_save_with_wiretap(new_filetype));
+ filetype = new_filetype;
+ file_set_save_marked_sensitive();
+ }
+}
+
+static void
+toggle_filtered_cb(GtkWidget *widget, gpointer data _U_)
+{
+ gboolean new_filtered;
+
+ new_filtered = GTK_TOGGLE_BUTTON (widget)->active;
+
+ if (filtered != new_filtered) {
+ /* They changed the state of the "filtered" button. */
+ filtered = new_filtered;
+ set_file_type_list(ft_om);
+ }
+}
+
+static void
+toggle_marked_cb(GtkWidget *widget, gpointer data _U_)
+{
+ gboolean new_marked;
+
+ new_marked = GTK_TOGGLE_BUTTON (widget)->active;
+
+ if (marked != new_marked) {
+ /* They changed the state of the "marked" button. */
+ marked = new_marked;
+ set_file_type_list(ft_om);
+ }
+}
+
+/*
+ * Keep a static pointer to the current "Save Capture File As" window, if
+ * any, so that if somebody tries to do "File:Save" or "File:Save As"
+ * while there's already a "Save Capture File As" window up, we just pop
+ * up the existing one, rather than creating a new one.
+ */
+static GtkWidget *file_save_as_w;
+
+void
+file_save_as_cmd_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ GtkWidget *ok_bt, *main_vb, *ft_hb, *ft_lb;
+
+ if (file_save_as_w != NULL) {
+ /* There's already an "Save Capture File As" dialog box; reactivate it. */
+ reactivate_window(file_save_as_w);
+ return;
+ }
+
+ /* Default to saving all packets, in the file's current format. */
+ filtered = FALSE;
+ marked = FALSE;
+ filetype = cfile.cd_t;
+
+ file_save_as_w = gtk_file_selection_new ("Ethereal: Save Capture File As");
+ g_signal_connect(G_OBJECT(file_save_as_w), "destroy",
+ G_CALLBACK(file_save_as_destroy_cb), NULL);
+
+ /* If we've opened a file, start out by showing the files in the directory
+ in which that file resided. */
+ if (last_open_dir)
+ gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_save_as_w), last_open_dir);
+
+ /* Connect the ok_button to file_save_as_ok_cb function and pass along a
+ pointer to the file selection box widget */
+ ok_bt = GTK_FILE_SELECTION (file_save_as_w)->ok_button;
+ g_signal_connect(G_OBJECT(ok_bt), "clicked",
+ G_CALLBACK(file_save_as_ok_cb), file_save_as_w);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(file_save_as_w)->action_area),
+ main_vb, FALSE, FALSE, 0);
+ gtk_widget_show(main_vb);
+
+ /*
+ * XXX - should this be sensitive only if the current display filter
+ * has rejected some packets, so that not all packets are currently
+ * being displayed, and if it has accepted some packets, so that some
+ * packets are currently being displayed?
+ *
+ * I'd say "no", as that complicates the UI code, and as one could,
+ * I guess, argue that the user may want to "save all the displayed
+ * packets" even if there aren't any, i.e. save an empty file.
+ */
+ filter_cb = gtk_check_button_new_with_label("Save only packets currently being displayed");
+ gtk_container_add(GTK_CONTAINER(main_vb), filter_cb);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(filter_cb), FALSE);
+ g_signal_connect(G_OBJECT(filter_cb), "toggled",
+ G_CALLBACK(toggle_filtered_cb), NULL);
+ gtk_widget_set_sensitive(filter_cb, can_save_with_wiretap(filetype));
+ gtk_widget_show(filter_cb);
+
+ /*
+ * The argument above could, I guess, be applied to the marked packets,
+ * except that you can't easily tell whether there are any marked
+ * packets, so I could imagine users doing "Save only marked packets"
+ * when there aren't any marked packets, not knowing that they'd
+ * failed to mark them, so I'm more inclined to have the "Save only
+ * marked packets" toggle button enabled only if there are marked
+ * packets to save.
+ */
+ mark_cb = gtk_check_button_new_with_label("Save only marked packets");
+ gtk_container_add(GTK_CONTAINER(main_vb), mark_cb);
+ marked = FALSE;
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(mark_cb), FALSE);
+ g_signal_connect(G_OBJECT(mark_cb), "toggled",
+ G_CALLBACK(toggle_marked_cb), NULL);
+ gtk_widget_show(mark_cb);
+
+ /* File type row */
+ ft_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
+ gtk_widget_show(ft_hb);
+
+ ft_lb = gtk_label_new("File type:");
+ gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
+ gtk_widget_show(ft_lb);
+
+ ft_om = gtk_option_menu_new();
+
+ /* Generate the list of file types we can save. */
+ set_file_type_list(ft_om);
+ gtk_box_pack_start(GTK_BOX(ft_hb), ft_om, FALSE, FALSE, 0);
+ gtk_widget_show(ft_om);
+
+ /*
+ * Set the sensitivity of the "Save only marked packets" toggle
+ * button
+ *
+ * This has to be done after we create the file type menu option,
+ * as the routine that sets it also sets that menu.
+ */
+ file_set_save_marked_sensitive();
+
+ /* Connect the cancel_button to destroy the widget */
+ gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(file_save_as_w)->cancel_button),
+ "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT(file_save_as_w));
+
+ /* 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(file_save_as_w, GTK_FILE_SELECTION(file_save_as_w)->cancel_button);
+
+ gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_save_as_w), "");
+
+ gtk_widget_show(file_save_as_w);
+}
+
+/*
+ * Set the "Save only marked packets" toggle button as appropriate for
+ * the current output file type and count of marked packets.
+ *
+ * Called when the "Save As..." dialog box is created and when either
+ * the file type or the marked count changes.
+ */
+void
+file_set_save_marked_sensitive(void)
+{
+ if (file_save_as_w == NULL) {
+ /* We don't currently have a "Save As..." dialog box up. */
+ return;
+ }
+
+ /* We can request that only the marked packets be saved only if we
+ can use Wiretap to save the file and if there *are* marked packets. */
+ if (can_save_with_wiretap(filetype) && cfile.marked_count != 0)
+ gtk_widget_set_sensitive(mark_cb, TRUE);
+ else {
+ /* Force the "Save only marked packets" toggle to "false", turn
+ off the flag it controls, and update the list of types we can
+ save the file as. */
+ marked = FALSE;
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(mark_cb), FALSE);
+ set_file_type_list(ft_om);
+ gtk_widget_set_sensitive(mark_cb, FALSE);
+ }
+}
+
+static void
+file_save_as_ok_cb(GtkWidget *w _U_, GtkFileSelection *fs) {
+ gchar *cf_name;
+
+ cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
+ gtk_widget_hide(GTK_WIDGET (fs));
+ gtk_widget_destroy(GTK_WIDGET (fs));
+
+ /* Write out the packets (all, or only the ones that are currently
+ displayed or marked) to the file with the specified name. */
+ save_cap_file(cf_name, &cfile, filtered, marked, filetype);
+
+ /* If "save_cap_file()" saved the file name we handed it, it saved
+ a copy, so we should free up our copy. */
+ g_free(cf_name);
+}
+
+static void
+file_save_as_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have a "Save Capture File As" dialog box. */
+ file_save_as_w = NULL;
+}
+
+/* Reload a file using the current read and display filters */
+void
+file_reload_cmd_cb(GtkWidget *w, gpointer data _U_) {
+ /*GtkWidget *filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);*/
+ GtkWidget *filter_te;
+ gchar *filename;
+ gboolean is_tempfile;
+ int err;
+
+ filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
+
+ if (cfile.dfilter)
+ g_free(cfile.dfilter);
+ cfile.dfilter = g_strdup(gtk_entry_get_text(GTK_ENTRY(filter_te)));
+
+ /* If the file could be opened, "open_cap_file()" calls "close_cap_file()"
+ to get rid of state for the old capture file before filling in state
+ for the new capture file. "close_cap_file()" will remove the file if
+ it's a temporary file; we don't want that to happen (for one thing,
+ it'd prevent subsequent reopens from working). Remember whether it's
+ a temporary file, mark it as not being a temporary file, and then
+ reopen it as the type of file it was.
+
+ Also, "close_cap_file()" will free "cfile.filename", so we must make
+ a copy of it first. */
+ filename = g_strdup(cfile.filename);
+ is_tempfile = cfile.is_tempfile;
+ cfile.is_tempfile = FALSE;
+ if (open_cap_file(filename, is_tempfile, &cfile) == 0) {
+ switch (read_cap_file(&cfile, &err)) {
+
+ case READ_SUCCESS:
+ case READ_ERROR:
+ /* Just because we got an error, that doesn't mean we were unable
+ to read any of the file; we handle what we could get from the
+ file. */
+ break;
+
+ case READ_ABORTED:
+ /* The user bailed out of re-reading the capture file; the
+ capture file has been closed - just free the capture file name
+ string and return (without changing the last containing
+ directory). */
+ g_free(filename);
+ return;
+ }
+ } else {
+ /* The open failed, so "cfile.is_tempfile" wasn't set to "is_tempfile".
+ Instead, the file was left open, so we should restore "cfile.is_tempfile"
+ ourselves.
+
+ XXX - change the menu? Presumably "open_cap_file()" will do that;
+ make sure it does! */
+ cfile.is_tempfile = is_tempfile;
+ }
+ /* "open_cap_file()" made a copy of the file name we handed it, so
+ we should free up our copy. */
+ g_free(filename);
+}
diff --git a/gtk2/file_dlg.h b/gtk2/file_dlg.h
new file mode 100644
index 0000000000..786e52ecb9
--- /dev/null
+++ b/gtk2/file_dlg.h
@@ -0,0 +1,42 @@
+/* file_dlg.h
+ * Definitions for dialog boxes for handling files
+ *
+ * $Id: file_dlg.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifndef __FILE_DLG_H__
+#define __FILE_DLG_H__
+
+void file_open_cmd_cb(GtkWidget *, gpointer);
+void file_save_cmd_cb(GtkWidget *, gpointer);
+void file_save_as_cmd_cb(GtkWidget *, gpointer);
+void file_close_cmd_cb(GtkWidget *, gpointer);
+void file_reload_cmd_cb(GtkWidget *, gpointer);
+
+/*
+ * Set the "Save only marked packets" toggle button as appropriate for
+ * the current output file type and count of marked packets.
+ * Called when the "Save As..." dialog box is created and when either
+ * the file type or the marked count changes.
+ */
+void file_set_save_marked_sensitive(void);
+
+#endif /* file_dlg.h */
diff --git a/gtk2/filter_prefs.c b/gtk2/filter_prefs.c
new file mode 100644
index 0000000000..55b171b67a
--- /dev/null
+++ b/gtk2/filter_prefs.c
@@ -0,0 +1,1178 @@
+/* filter_prefs.c
+ * Dialog boxes for filter editing
+ * (This used to be a notebook page under "Preferences", hence the
+ * "prefs" in the file name.)
+ *
+ * $Id: filter_prefs.c,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/filesystem.h>
+
+#include "filters.h"
+#include "gtk/main.h"
+#include "filter_prefs.h"
+#include "dlg_utils.h"
+#include "ui_util.h"
+#include "simple_dialog.h"
+#include "dfilter_expr_dlg.h"
+
+#define E_FILT_PARENT_FILTER_TE_KEY "filter_parent_filter_te"
+#define E_FILT_CONSTRUCT_ARGS_KEY "filter_construct_args"
+#define E_FILT_LIST_ITEM_MODEL_KEY "filter_list_item_model"
+#define E_FILT_LBL_KEY "filter_label"
+#define E_FILT_FILTER_L_KEY "filter_filter_l"
+#define E_FILT_CHG_BT_KEY "filter_chg_bt"
+#define E_FILT_COPY_BT_KEY "filter_copy_bt"
+#define E_FILT_DEL_BT_KEY "filter_del_bt"
+#define E_FILT_NAME_TE_KEY "filter_name_te"
+#define E_FILT_FILTER_TE_KEY "filter_filter_te"
+#define E_FILT_DBLFUNC_KEY "filter_dblfunc"
+#define E_FILT_DBLARG_KEY "filter_dblarg"
+#define E_FILT_DBLACTIVATE_KEY "filter_dblactivate"
+
+typedef struct _filter_cb_data {
+ GList *fl;
+ GtkWidget *win;
+} filter_cb_data;
+
+static GtkWidget *filter_dialog_new(GtkWidget *caller, GtkWidget *filter_te,
+ filter_list_type_t list, construct_args_t *construct_args);
+static void filter_dlg_dclick(GtkWidget *dummy, gpointer main_w_arg,
+ gpointer activate);
+static void filter_dlg_ok_cb(GtkWidget *ok_bt, gpointer dummy);
+static void filter_dlg_apply_cb(GtkWidget *apply_bt, gpointer dummy);
+static void filter_apply(GtkWidget *main_w, gboolean destroy);
+static void filter_dlg_save_cb(GtkWidget *save_bt, gpointer parent_w);
+static void filter_dlg_close_cb(GtkWidget *close_bt, gpointer parent_w);
+static void filter_dlg_destroy(GtkWidget *win, gpointer data);
+
+static gint filter_sel_list_button_cb(GtkWidget *, GdkEventButton *,
+ gpointer);
+static void filter_sel_list_cb(GtkWidget *, gpointer);
+static void filter_list_destroy_cb(GtkWidget *, gpointer);
+static void filter_new_bt_clicked_cb(GtkWidget *, gpointer);
+static void filter_chg_bt_clicked_cb(GtkWidget *, gpointer);
+static void filter_chg_bt_destroy_cb(GtkWidget *, gpointer);
+static void filter_copy_bt_clicked_cb(GtkWidget *, gpointer);
+static void filter_copy_bt_destroy_cb(GtkWidget *, gpointer);
+static void filter_del_bt_clicked_cb(GtkWidget *, gpointer);
+static void filter_del_bt_destroy_cb(GtkWidget *, gpointer);
+static void filter_expr_cb(GtkWidget *, gpointer);
+static void filter_name_te_destroy_cb(GtkWidget *, gpointer);
+static void filter_filter_te_destroy_cb(GtkWidget *, gpointer);
+
+#ifdef HAVE_LIBPCAP
+/* Create a filter dialog for constructing a capture filter.
+
+ This is to be used as a callback for a button next to a text entry box,
+ which, when clicked, pops up this dialog to allow you to construct a
+ display filter by browsing the list of saved filters (the dialog
+ for constructing expressions assumes display filter syntax, not
+ capture filter syntax). The "OK" button sets the text entry box to the
+ constructed filter and activates that text entry box (which should have
+ no effect in the main capture dialog); this dialog is then dismissed. */
+void
+capture_filter_construct_cb(GtkWidget *w, gpointer user_data _U_)
+{
+ GtkWidget *caller = gtk_widget_get_toplevel(w);
+ GtkWidget *filter_browse_w;
+ GtkWidget *parent_filter_te;
+ /* No Apply button, and "OK" just sets our text widget, it doesn't
+ activate it (i.e., it doesn't cause us to try to open the file). */
+ static construct_args_t args = {
+ "Ethereal: Capture Filter",
+ FALSE,
+ FALSE
+ };
+
+ /* Has a filter dialog box already been opened for that top-level
+ widget? */
+ filter_browse_w = gtk_object_get_data(GTK_OBJECT(caller),
+ E_FILT_DIALOG_PTR_KEY);
+
+ if (filter_browse_w != NULL) {
+ /* Yes. Just re-activate that dialog box. */
+ reactivate_window(filter_browse_w);
+ return;
+ }
+
+ /* No. Get the text entry attached to the button. */
+ parent_filter_te = gtk_object_get_data(GTK_OBJECT(w), E_FILT_TE_PTR_KEY);
+
+ /* Now create a new dialog, without an "Add Expression..." button. */
+ filter_browse_w = filter_dialog_new(caller, parent_filter_te,
+ CFILTER_LIST, &args);
+
+ /* Set the E_FILT_CALLER_PTR_KEY for the new dialog to point to
+ our caller. */
+ gtk_object_set_data(GTK_OBJECT(filter_browse_w), E_FILT_CALLER_PTR_KEY,
+ caller);
+
+ /* Set the E_FILT_DIALOG_PTR_KEY for the caller to point to us */
+ gtk_object_set_data(GTK_OBJECT(caller), E_FILT_DIALOG_PTR_KEY,
+ filter_browse_w);
+}
+#endif
+
+/* Create a filter dialog for constructing a display filter.
+
+ This is to be used as a callback for a button next to a text entry box,
+ which, when clicked, pops up this dialog to allow you to construct a
+ display filter by browsing the list of saved filters and/or by adding
+ test expressions constructed with another dialog. The "OK" button
+ sets the text entry box to the constructed filter and activates that
+ text entry box, causing the filter to be used; this dialog is then
+ dismissed.
+
+ If "wants_apply_button" is non-null, we add an "Apply" button that
+ acts like "OK" but doesn't dismiss this dialog. */
+void
+display_filter_construct_cb(GtkWidget *w, gpointer construct_args_ptr)
+{
+ construct_args_t *construct_args = construct_args_ptr;
+ GtkWidget *caller = gtk_widget_get_toplevel(w);
+ GtkWidget *filter_browse_w;
+ GtkWidget *parent_filter_te;
+
+ /* Has a filter dialog box already been opened for that top-level
+ widget? */
+ filter_browse_w = gtk_object_get_data(GTK_OBJECT(caller),
+ E_FILT_DIALOG_PTR_KEY);
+
+ if (filter_browse_w != NULL) {
+ /* Yes. Just re-activate that dialog box. */
+ reactivate_window(filter_browse_w);
+ return;
+ }
+
+ /* No. Get the text entry attached to the button. */
+ parent_filter_te = gtk_object_get_data(GTK_OBJECT(w), E_FILT_TE_PTR_KEY);
+
+ /* Now create a new dialog, possibly with an "Apply" button, and
+ definitely with an "Add Expression..." button. */
+ filter_browse_w = filter_dialog_new(caller, parent_filter_te,
+ DFILTER_LIST, construct_args);
+
+ /* Set the E_FILT_CALLER_PTR_KEY for the new dialog to point to
+ our caller. */
+ gtk_object_set_data(GTK_OBJECT(filter_browse_w), E_FILT_CALLER_PTR_KEY,
+ caller);
+
+ /* Set the E_FILT_DIALOG_PTR_KEY for the caller to point to us */
+ gtk_object_set_data(GTK_OBJECT(caller), E_FILT_DIALOG_PTR_KEY,
+ filter_browse_w);
+}
+
+#ifdef HAVE_LIBPCAP
+static GtkWidget *global_cfilter_w;
+
+/* Create a filter dialog for editing capture filters; this is to be used
+ as a callback for menu items, toolbars, etc.. */
+void
+cfilter_dialog_cb(GtkWidget *w _U_)
+{
+ /* No Apply button, and there's no text widget to set, much less
+ activate, on "OK". */
+ static construct_args_t args = {
+ "Ethereal: Edit Capture Filter List",
+ FALSE,
+ FALSE
+ };
+
+ /* Has a filter dialog box already been opened for editing
+ capture filters? */
+ if (global_cfilter_w != NULL) {
+ /* Yes. Just reactivate it. */
+ reactivate_window(global_cfilter_w);
+ return;
+ }
+
+ /*
+ * No. Create one; we didn't pop this up as a result of pressing
+ * a button next to some text entry field, so don't associate it
+ * with a text entry field.
+ */
+ global_cfilter_w = filter_dialog_new(NULL, NULL, CFILTER_LIST, &args);
+}
+#endif
+
+static GtkWidget *global_dfilter_w;
+
+/* Create a filter dialog for editing display filters; this is to be used
+ as a callback for menu items, toolbars, etc.. */
+void
+dfilter_dialog_cb(GtkWidget *w _U_)
+{
+ /* No Apply button, and there's no text widget to set, much less
+ activate, on "OK". */
+ static construct_args_t args = {
+ "Ethereal: Edit Display Filter List",
+ FALSE,
+ FALSE
+ };
+
+ /* Has a filter dialog box already been opened for editing
+ display filters? */
+ if (global_dfilter_w != NULL) {
+ /* Yes. Just reactivate it. */
+ reactivate_window(global_dfilter_w);
+ return;
+ }
+
+ /*
+ * No. Create one; we didn't pop this up as a result of pressing
+ * a button next to some text entry field, so don't associate it
+ * with a text entry field.
+ */
+ global_dfilter_w = filter_dialog_new(NULL, NULL, DFILTER_LIST, &args);
+}
+
+/* List of capture filter dialogs, so that if the list of filters changes
+ (the model, if you will), we can update all of their lists displaying
+ the filters (the views). */
+static GList *cfilter_dialogs;
+
+/* List of display filter dialogs, so that if the list of filters changes
+ (the model, if you will), we can update all of their lists displaying
+ the filters (the views). */
+static GList *dfilter_dialogs;
+
+static void
+remember_filter_dialog(GtkWidget *main_w, GList **filter_dialogs)
+{
+ *filter_dialogs = g_list_append(*filter_dialogs, main_w);
+}
+
+/* Remove a filter dialog from the specified list of filter_dialogs. */
+static void
+forget_filter_dialog(GtkWidget *main_w, filter_list_type_t list)
+{
+ switch (list) {
+
+ case CFILTER_LIST:
+ cfilter_dialogs = g_list_remove(cfilter_dialogs, main_w);
+ break;
+
+ case DFILTER_LIST:
+ dfilter_dialogs = g_list_remove(dfilter_dialogs, main_w);
+ break;
+
+ default:
+ g_assert_not_reached();
+ break;
+ }
+}
+
+/* Get the dialog list corresponding to a particular filter list. */
+static GList *
+get_filter_dialog_list(filter_list_type_t list)
+{
+ switch (list) {
+
+ case CFILTER_LIST:
+ return cfilter_dialogs;
+
+ case DFILTER_LIST:
+ return dfilter_dialogs;
+
+ default:
+ g_assert_not_reached();
+ return NULL;
+ }
+}
+
+static GtkWidget *
+filter_dialog_new(GtkWidget *caller _U_, GtkWidget *parent_filter_te,
+ filter_list_type_t list, construct_args_t *construct_args)
+{
+ GtkWidget *main_w, /* main window */
+ *main_vb, /* main container */
+ *bbox, /* button container */
+ *ok_bt, /* "OK" button */
+ *apply_bt, /* "Apply" button */
+ *save_bt, /* "Save" button */
+ *close_bt; /* "Cancel" button */
+ GtkWidget *filter_pg = NULL; /* filter settings box */
+ GtkWidget *top_hb,
+ *list_bb,
+ *new_bt,
+ *chg_bt,
+ *copy_bt,
+ *del_bt,
+ *filter_sc,
+ *filter_l,
+ *nl_item,
+ *nl_lb,
+ *middle_hb,
+ *name_lb,
+ *name_te,
+ *bottom_hb,
+ *filter_lb,
+ *filter_te,
+ *add_expression_bt;
+ GtkWidget *l_select = NULL;
+ GList *fl_entry;
+ filter_def *filt;
+ const gchar *filter_te_str = NULL;
+ GList **filter_dialogs;
+ static filter_list_type_t cfilter_list = CFILTER_LIST;
+ static filter_list_type_t dfilter_list = DFILTER_LIST;
+ filter_list_type_t *filter_list_p;
+
+ /* Get a pointer to a static variable holding the type of filter on
+ which we're working, so we can pass that pointer to callback
+ routines. */
+ switch (list) {
+
+ case CFILTER_LIST:
+ filter_dialogs = &cfilter_dialogs;
+ filter_list_p = &cfilter_list;
+ break;
+
+ case DFILTER_LIST:
+ filter_dialogs = &dfilter_dialogs;
+ filter_list_p = &dfilter_list;
+ break;
+
+ default:
+ g_assert_not_reached();
+ filter_dialogs = NULL;
+ filter_list_p = NULL;
+ break;
+ }
+
+ main_w = dlg_window_new(construct_args->title);
+ gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_CONSTRUCT_ARGS_KEY,
+ construct_args);
+
+ /* Call a handler when we're destroyed, so we can inform
+ our caller, if any, that we've been destroyed. */
+ g_signal_connect(G_OBJECT(main_w), "destroy",
+ G_CALLBACK(filter_dlg_destroy), filter_list_p);
+
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(main_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* Make sure everything is set up */
+ if (parent_filter_te)
+ filter_te_str = gtk_entry_get_text(GTK_ENTRY(parent_filter_te));
+
+ /* Container for each row of widgets */
+ filter_pg = gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(filter_pg), 5);
+ gtk_widget_show(filter_pg);
+
+ /* Top row: Filter list and buttons */
+ top_hb = gtk_hbox_new(FALSE, 5);
+ gtk_container_add(GTK_CONTAINER(filter_pg), top_hb);
+ gtk_widget_show(top_hb);
+
+ list_bb = gtk_vbutton_box_new();
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (list_bb), GTK_BUTTONBOX_START);
+ gtk_container_add(GTK_CONTAINER(top_hb), list_bb);
+ gtk_widget_show(list_bb);
+
+ new_bt = gtk_button_new_from_stock(GTK_STOCK_NEW);
+ g_signal_connect(G_OBJECT(new_bt), "clicked",
+ G_CALLBACK(filter_new_bt_clicked_cb), filter_list_p);
+ gtk_container_add(GTK_CONTAINER(list_bb), new_bt);
+ gtk_widget_show(new_bt);
+
+ chg_bt = gtk_button_new_with_label ("Change");
+ gtk_widget_set_sensitive(chg_bt, FALSE);
+ g_signal_connect(G_OBJECT(chg_bt), "clicked",
+ G_CALLBACK(filter_chg_bt_clicked_cb), filter_list_p);
+ gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_CHG_BT_KEY, chg_bt);
+ g_signal_connect(G_OBJECT(chg_bt), "destroy",
+ G_CALLBACK(filter_chg_bt_destroy_cb), NULL);
+ gtk_container_add(GTK_CONTAINER(list_bb), chg_bt);
+ gtk_widget_show(chg_bt);
+
+ copy_bt = gtk_button_new_from_stock(GTK_STOCK_COPY);
+ gtk_widget_set_sensitive(copy_bt, FALSE);
+ g_signal_connect(G_OBJECT(copy_bt), "clicked",
+ G_CALLBACK(filter_copy_bt_clicked_cb), filter_list_p);
+ gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_COPY_BT_KEY, copy_bt);
+ g_signal_connect(G_OBJECT(copy_bt), "destroy",
+ G_CALLBACK(filter_copy_bt_destroy_cb), NULL);
+ gtk_container_add(GTK_CONTAINER(list_bb), copy_bt);
+ gtk_widget_show(copy_bt);
+
+ del_bt = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+ gtk_widget_set_sensitive(del_bt, FALSE);
+ g_signal_connect(G_OBJECT(del_bt), "clicked",
+ G_CALLBACK(filter_del_bt_clicked_cb), filter_list_p);
+ gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_DEL_BT_KEY, del_bt);
+ g_signal_connect(G_OBJECT(del_bt), "destroy",
+ G_CALLBACK(filter_del_bt_destroy_cb), NULL);
+ gtk_container_add(GTK_CONTAINER(list_bb), del_bt);
+ gtk_widget_show(del_bt);
+
+ if (list == DFILTER_LIST) {
+ /* Create the "Add Expression..." button, to pop up a dialog
+ for constructing filter comparison expressions. */
+ add_expression_bt = gtk_button_new_with_label("Add Expression...");
+ g_signal_connect(G_OBJECT(add_expression_bt), "clicked",
+ G_CALLBACK(filter_expr_cb), main_w);
+ gtk_container_add(GTK_CONTAINER(list_bb), add_expression_bt);
+ gtk_widget_show(add_expression_bt);
+ }
+
+ filter_sc = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(filter_sc),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_size_request(filter_sc, 250, 150);
+ gtk_container_add(GTK_CONTAINER(top_hb), filter_sc);
+ gtk_widget_show(filter_sc);
+
+ filter_l = gtk_list_new();
+ gtk_list_set_selection_mode(GTK_LIST(filter_l), GTK_SELECTION_SINGLE);
+ g_signal_connect(G_OBJECT(filter_l), "selection_changed",
+ G_CALLBACK(filter_sel_list_cb), filter_pg);
+ gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_FILTER_L_KEY, filter_l);
+ g_signal_connect(G_OBJECT(filter_l), "destroy",
+ G_CALLBACK(filter_list_destroy_cb), NULL);
+ gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(filter_sc),
+ filter_l);
+ gtk_widget_show(filter_l);
+
+ gtk_object_set_data(GTK_OBJECT(filter_l), E_FILT_DBLFUNC_KEY, filter_dlg_dclick);
+ gtk_object_set_data(GTK_OBJECT(filter_l), E_FILT_DBLARG_KEY, main_w);
+ /* This is a Boolean, but we make it a non-null pointer for TRUE
+ and a null pointer for FALSE, as object data is a pointer. */
+ gtk_object_set_data(GTK_OBJECT(filter_l), E_FILT_DBLACTIVATE_KEY,
+ construct_args->activate_on_ok? "" : NULL);
+
+ fl_entry = get_filter_list_first(list);
+ while (fl_entry != NULL) {
+ filt = (filter_def *) fl_entry->data;
+ nl_lb = gtk_label_new(filt->name);
+ nl_item = gtk_list_item_new();
+
+ g_signal_connect(G_OBJECT(nl_item), "button_press_event",
+ G_CALLBACK(filter_sel_list_button_cb),
+ filter_l);
+
+ gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
+ gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
+ gtk_widget_show(nl_lb);
+ gtk_container_add(GTK_CONTAINER(filter_l), nl_item);
+ gtk_widget_show(nl_item);
+ gtk_object_set_data(GTK_OBJECT(nl_item), E_FILT_LBL_KEY, nl_lb);
+ gtk_object_set_data(GTK_OBJECT(nl_item), E_FILT_LIST_ITEM_MODEL_KEY,
+ fl_entry);
+
+ if (filter_te_str && filt->strval) {
+ if (strcmp(filter_te_str, filt->strval) == 0)
+ l_select = nl_item;
+ }
+
+ fl_entry = fl_entry->next;
+ }
+
+ /* Middle row: Filter name entry */
+ middle_hb = gtk_hbox_new(FALSE, 5);
+ gtk_container_add(GTK_CONTAINER(filter_pg), middle_hb);
+ gtk_widget_show(middle_hb);
+
+ name_lb = gtk_label_new("Filter name:");
+ gtk_box_pack_start(GTK_BOX(middle_hb), name_lb, FALSE, FALSE, 3);
+ gtk_widget_show(name_lb);
+
+ name_te = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(middle_hb), name_te, TRUE, TRUE, 3);
+ gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_NAME_TE_KEY, name_te);
+ g_signal_connect(G_OBJECT(name_te), "destroy",
+ G_CALLBACK(filter_name_te_destroy_cb), NULL);
+ gtk_widget_show(name_te);
+
+ /* Bottom row: Filter text entry */
+ bottom_hb = gtk_hbox_new(FALSE, 5);
+ gtk_container_add(GTK_CONTAINER(filter_pg), bottom_hb);
+ gtk_widget_show(bottom_hb);
+
+ filter_lb = gtk_label_new("Filter string:");
+ gtk_box_pack_start(GTK_BOX(bottom_hb), filter_lb, FALSE, FALSE, 3);
+ gtk_widget_show(filter_lb);
+
+ filter_te = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(bottom_hb), filter_te, TRUE, TRUE, 3);
+ gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_FILTER_TE_KEY, filter_te);
+
+ g_signal_connect(G_OBJECT(filter_te), "destroy",
+ G_CALLBACK(filter_filter_te_destroy_cb), NULL);
+ gtk_widget_show(filter_te);
+
+ if (l_select) {
+ gtk_list_select_child(GTK_LIST(filter_l), l_select);
+ } else if (filter_te_str && filter_te_str[0]) {
+ gtk_entry_set_text(GTK_ENTRY(name_te), "New filter");
+ gtk_entry_set_text(GTK_ENTRY(filter_te), filter_te_str);
+ }
+
+ gtk_box_pack_start(GTK_BOX(main_vb), filter_pg, TRUE, TRUE, 0);
+ gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY,
+ parent_filter_te);
+
+ 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(main_vb), bbox);
+ gtk_widget_show(bbox);
+
+ if (parent_filter_te != NULL) {
+ /*
+ * We have a filter text entry that we can fill in if
+ * the "OK" button is clicked, so put in an "OK" button.
+ */
+ ok_bt = gtk_button_new_from_stock(GTK_STOCK_OK);
+ g_signal_connect(G_OBJECT(ok_bt), "clicked",
+ G_CALLBACK(filter_dlg_ok_cb), NULL);
+ GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start(GTK_BOX(bbox), ok_bt, TRUE, TRUE, 0);
+ gtk_widget_grab_default(ok_bt);
+ gtk_widget_show(ok_bt);
+
+ /* Catch the "activate" signal on the filter name and filter
+ expression text entries, so that if the user types Return
+ there, we act as if the "OK" button had been selected, as
+ happens if Return is typed if some widget that *doesn't*
+ handle the Return key has the input focus. */
+ dlg_set_activate(name_te, ok_bt);
+ dlg_set_activate(filter_te, ok_bt);
+ }
+
+ if (construct_args->wants_apply_button) {
+ apply_bt = gtk_button_new_from_stock(GTK_STOCK_APPLY);
+ g_signal_connect(G_OBJECT(apply_bt), "clicked",
+ G_CALLBACK(filter_dlg_apply_cb), NULL);
+ GTK_WIDGET_SET_FLAGS(apply_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start(GTK_BOX(bbox), apply_bt, TRUE, TRUE, 0);
+ gtk_widget_show(apply_bt);
+ }
+
+ save_bt = gtk_button_new_from_stock(GTK_STOCK_SAVE);
+ g_signal_connect(G_OBJECT(save_bt), "clicked",
+ G_CALLBACK(filter_dlg_save_cb), filter_list_p);
+ GTK_WIDGET_SET_FLAGS(save_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start(GTK_BOX(bbox), save_bt, TRUE, TRUE, 0);
+ gtk_widget_show(save_bt);
+
+ close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ g_signal_connect(G_OBJECT(close_bt), "clicked",
+ G_CALLBACK(filter_dlg_close_cb), GTK_OBJECT(main_w));
+ GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start(GTK_BOX(bbox), close_bt, TRUE, TRUE, 0);
+ gtk_widget_show(close_bt);
+
+ /*
+ * Catch the "key_press_event" signal in the window, so that we can
+ * catch the ESC key being pressed and act as if the "Close" button
+ * had been selected.
+ */
+ dlg_set_cancel(main_w, close_bt);
+
+ remember_filter_dialog(main_w, filter_dialogs);
+
+ gtk_widget_show(main_w);
+
+ return main_w;
+}
+
+static void
+filter_dlg_dclick(GtkWidget *filter_l, gpointer main_w_arg, gpointer activate)
+{
+ GtkWidget *main_w = GTK_WIDGET(main_w_arg);
+ GtkWidget *parent_filter_te =
+ gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY);
+ GList *flp, *sl;
+ GtkObject *l_item;
+ filter_def *filt;
+
+ if (parent_filter_te != NULL) {
+ /*
+ * We have a text entry widget associated with this dialog
+ * box; is one of the filters in the list selected?
+ */
+ sl = GTK_LIST(filter_l)->selection;
+ if (sl != NULL) {
+ /*
+ * Yes. Is there a filter definition for that filter?
+ */
+ l_item = GTK_OBJECT(sl->data);
+ flp = (GList *) gtk_object_get_data(l_item, E_FILT_LIST_ITEM_MODEL_KEY);
+ if (flp) {
+ /*
+ * Yes - put it in the text entry widget.
+ */
+ filt = (filter_def *) flp->data;
+ gtk_entry_set_text(GTK_ENTRY(parent_filter_te),
+ filt->strval);
+
+ /*
+ * Are we supposed to cause the filter we
+ * put there to be applied?
+ */
+ if (activate != NULL) {
+ /*
+ * Yes - do so.
+ */
+ gtk_signal_emit_by_name(GTK_OBJECT(parent_filter_te),
+ "activate");
+ }
+ }
+ }
+ }
+
+ gtk_widget_destroy(main_w);
+}
+
+static void
+filter_dlg_ok_cb(GtkWidget *ok_bt, gpointer data _U_)
+{
+ /*
+ * Destroy the dialog box and apply the filter.
+ */
+ filter_apply(gtk_widget_get_toplevel(ok_bt), TRUE);
+}
+
+static void
+filter_dlg_apply_cb(GtkWidget *apply_bt, gpointer dummy _U_)
+{
+ /*
+ * Apply the filter, but don't destroy the dialog box.
+ */
+ filter_apply(gtk_widget_get_toplevel(apply_bt), FALSE);
+}
+
+static void
+filter_apply(GtkWidget *main_w, gboolean destroy)
+{
+ construct_args_t *construct_args =
+ gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_CONSTRUCT_ARGS_KEY);
+ GtkWidget *parent_filter_te =
+ gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY);
+ GtkWidget *filter_te;
+ const gchar *filter_string;
+
+ if (parent_filter_te != NULL) {
+ /*
+ * We have a text entry widget associated with this dialog
+ * box; put the filter in our text entry widget into that
+ * text entry widget.
+ */
+ filter_te = gtk_object_get_data(GTK_OBJECT(main_w),
+ E_FILT_FILTER_TE_KEY);
+ filter_string = gtk_entry_get_text(GTK_ENTRY(filter_te));
+ gtk_entry_set_text(GTK_ENTRY(parent_filter_te), filter_string);
+
+ }
+
+ if (destroy) {
+ /*
+ * Destroy the filter dialog box.
+ */
+ gtk_widget_destroy(main_w);
+ }
+
+ if (parent_filter_te != NULL) {
+ /*
+ * We have a text entry widget associated with this dialog
+ * box; activate that widget to cause the filter we put
+ * there to be applied if we're supposed to do so.
+ *
+ * We do this after dismissing the filter dialog box,
+ * as activating the widget the dialog box to which
+ * it belongs to be dismissed, and that may cause it
+ * to destroy our dialog box if the filter succeeds.
+ * This means that our subsequent attempt to destroy
+ * it will fail.
+ *
+ * We don't know whether it'll destroy our dialog box,
+ * so we can't rely on it to do so. Instead, we
+ * destroy it ourselves, which will clear the
+ * E_FILT_DIALOG_PTR_KEY pointer for their dialog box,
+ * meaning they won't think it has one and won't try
+ * to destroy it.
+ */
+ if (construct_args->activate_on_ok) {
+ gtk_signal_emit_by_name(GTK_OBJECT(parent_filter_te),
+ "activate");
+ }
+ }
+}
+
+static void
+filter_dlg_save_cb(GtkWidget *save_bt _U_, gpointer data)
+{
+ filter_list_type_t list = *(filter_list_type_t *)data;
+ char *pf_dir_path;
+ char *f_path;
+ int f_save_errno;
+ char *filter_type;
+
+ /* Create the directory that holds personal configuration files,
+ if necessary. */
+ if (create_persconffile_dir(&pf_dir_path) == -1) {
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "Can't create directory\n\"%s\"\nfor filter files: %s.",
+ pf_dir_path, strerror(errno));
+ g_free(pf_dir_path);
+ return;
+ }
+
+ save_filter_list(list, &f_path, &f_save_errno);
+ if (f_path != NULL) {
+ /* We had an error saving the filter. */
+ switch (list) {
+
+ case CFILTER_LIST:
+ filter_type = "capture";
+ break;
+
+ case DFILTER_LIST:
+ filter_type = "display";
+ break;
+
+ default:
+ g_assert_not_reached();
+ filter_type = NULL;
+ break;
+ }
+ simple_dialog(ESD_TYPE_CRIT, NULL,
+ "Could not save to your %s filter file\n\"%s\": %s.",
+ filter_type, f_path, strerror(f_save_errno));
+ g_free(f_path);
+ }
+}
+
+static void
+filter_dlg_close_cb(GtkWidget *close_bt _U_, gpointer parent_w)
+{
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+filter_dlg_destroy(GtkWidget *win, gpointer data)
+{
+ filter_list_type_t list = *(filter_list_type_t *)data;
+ GtkWidget *caller;
+
+ /* Get the widget that requested that we be popped up, if any.
+ (It should arrange to destroy us if it's destroyed, so
+ that we don't get a pointer to a non-existent window here.) */
+ caller = gtk_object_get_data(GTK_OBJECT(win), E_FILT_CALLER_PTR_KEY);
+
+ if (caller != NULL) {
+ /* Tell it we no longer exist. */
+ gtk_object_set_data(GTK_OBJECT(caller), E_FILT_DIALOG_PTR_KEY,
+ NULL);
+ } else {
+ /* This is an editing dialog popped up from, for example,
+ a menu item; note that we no longer have one. */
+ switch (list) {
+
+#ifdef HAVE_LIBPCAP
+ case CFILTER_LIST:
+ g_assert(win == global_cfilter_w);
+ global_cfilter_w = NULL;
+ break;
+#endif
+
+ case DFILTER_LIST:
+ g_assert(win == global_dfilter_w);
+ global_dfilter_w = NULL;
+ break;
+
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ }
+
+ /* Remove this from the list of filter dialog windows. */
+ forget_filter_dialog(win, list);
+
+ /* Now nuke this window. */
+ gtk_grab_remove(GTK_WIDGET(win));
+ gtk_widget_destroy(GTK_WIDGET(win));
+}
+
+static gint
+filter_sel_list_button_cb (GtkWidget *widget, GdkEventButton *event,
+ gpointer func_data)
+{
+ GtkWidget *parent = func_data;
+ void (* func)(GtkWidget *, gpointer, gpointer);
+ gpointer func_arg;
+ gpointer func_activate;
+
+ if (GTK_IS_LIST_ITEM(widget) && event->type == GDK_2BUTTON_PRESS) {
+ func = gtk_object_get_data(GTK_OBJECT(parent), E_FILT_DBLFUNC_KEY);
+ func_arg = gtk_object_get_data(GTK_OBJECT(parent), E_FILT_DBLARG_KEY);
+ func_activate = gtk_object_get_data(GTK_OBJECT(parent), E_FILT_DBLACTIVATE_KEY);
+
+ if (func)
+ (*func)(func_data, func_arg, func_activate);
+ }
+
+ return FALSE;
+}
+
+static void
+filter_sel_list_cb(GtkWidget *l, gpointer data _U_)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(l);
+ GtkWidget *name_te = gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_NAME_TE_KEY);
+ GtkWidget *filter_te = gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
+ GtkWidget *chg_bt = gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_CHG_BT_KEY);
+ GtkWidget *copy_bt = gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_COPY_BT_KEY);
+ GtkWidget *del_bt = gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_DEL_BT_KEY);
+ filter_def *filt;
+ gchar *name = "", *strval = "";
+ GList *sl, *flp;
+ GtkObject *l_item;
+ gint sensitivity = FALSE;
+
+ if (l)
+ sl = GTK_LIST(l)->selection;
+ else
+ sl = NULL;
+
+ if (sl) { /* Something was selected */
+ l_item = GTK_OBJECT(sl->data);
+ flp = (GList *) gtk_object_get_data(l_item, E_FILT_LIST_ITEM_MODEL_KEY);
+ if (flp) {
+ filt = (filter_def *) flp->data;
+ name = filt->name;
+ strval = filt->strval;
+ sensitivity = TRUE;
+ }
+ }
+
+ /*
+ * Did you know that this function is called when the window is destroyed?
+ * Funny, that.
+ * This means that we have to:
+ *
+ * attach to the top-level window data items containing pointers to
+ * the widgets we affect here;
+ *
+ * give each of those widgets their own destroy callbacks;
+ *
+ * clear that pointer when the widget is destroyed;
+ *
+ * don't do anything to the widget if the pointer we get back is
+ * null;
+ *
+ * so that if we're called after any of the widgets we'd affect are
+ * destroyed, we know that we shouldn't do anything to those widgets.
+ */
+ if (name_te != NULL)
+ gtk_entry_set_text(GTK_ENTRY(name_te), name);
+ if (filter_te != NULL)
+ gtk_entry_set_text(GTK_ENTRY(filter_te), strval);
+ if (chg_bt != NULL)
+ gtk_widget_set_sensitive(chg_bt, sensitivity);
+ if (copy_bt != NULL)
+ gtk_widget_set_sensitive(copy_bt, sensitivity);
+ if (del_bt != NULL)
+ gtk_widget_set_sensitive(del_bt, sensitivity);
+}
+
+static void
+filter_list_destroy_cb(GtkWidget *l, gpointer data _U_)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(l);
+
+ gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_FILTER_L_KEY, NULL);
+}
+
+/* To do: add input checking to each of these callbacks */
+
+/* Structure containing arguments to be passed to "new_filter_cb()".
+
+ "active_filter_l" is the list in the dialog box in which "New" or
+ "Copy" was clicked; in that dialog box, but not in any other dialog
+ box, we select the newly created list item.
+
+ "nflp" is the GList member in the model (filter list) for the new
+ filter. */
+typedef struct {
+ GtkWidget *active_filter_l;
+ GList *nflp;
+} new_filter_cb_args_t;
+
+static void
+new_filter_cb(gpointer data, gpointer user_data)
+{
+ GtkWidget *main_w = data;
+ GtkWidget *filter_l = gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_FILTER_L_KEY);
+ new_filter_cb_args_t *args = user_data;
+ filter_def *nfilt = args->nflp->data;
+ GtkWidget *nl_lb, *nl_item;
+
+ nl_lb = gtk_label_new(nfilt->name);
+ nl_item = gtk_list_item_new();
+ gtk_misc_set_alignment(GTK_MISC(nl_lb), 0.0, 0.5);
+ gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
+ gtk_widget_show(nl_lb);
+ gtk_container_add(GTK_CONTAINER(filter_l), nl_item);
+ gtk_widget_show(nl_item);
+ gtk_object_set_data(GTK_OBJECT(nl_item), E_FILT_LBL_KEY, nl_lb);
+ gtk_object_set_data(GTK_OBJECT(nl_item), E_FILT_LIST_ITEM_MODEL_KEY,
+ args->nflp);
+ if (filter_l == args->active_filter_l) {
+ /* Select the item. */
+ gtk_list_select_child(GTK_LIST(filter_l), nl_item);
+ }
+}
+
+static void
+filter_new_bt_clicked_cb(GtkWidget *w, gpointer data)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(w);
+ GtkWidget *name_te = gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_NAME_TE_KEY);
+ GtkWidget *filter_te = gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
+ GtkWidget *filter_l = gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_FILTER_L_KEY);
+ filter_list_type_t list = *(filter_list_type_t *)data;
+ GList *fl_entry;
+ const gchar *name, *strval;
+ new_filter_cb_args_t args;
+
+ name = gtk_entry_get_text(GTK_ENTRY(name_te));
+ strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
+
+ if (strlen(name) > 0 && strlen(strval) > 0) {
+ /* Add a new entry to the filter list. */
+ fl_entry = add_to_filter_list(list, name, strval);
+
+ /* Update all the filter list widgets, not just the one in
+ the dialog box in which we clicked on "Copy". */
+ args.active_filter_l = filter_l;
+ args.nflp = fl_entry;
+ g_list_foreach(get_filter_dialog_list(list), new_filter_cb, &args);
+ } else {
+ /* Give the user some basic directions on how to use the 'new' button */
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "You have left either the 'Filter name' or 'Filter string' field empty.\n"
+ "To add a new filter, enter a name for the filter, and enter the expression\n"
+ "for the filter or use the 'Add Expression...' button to construct an\n"
+ "expression, then click 'New'.");
+ }
+}
+
+static void
+chg_list_item_cb(GtkWidget *nl_item, gpointer data)
+{
+ GList *flp = data;
+ filter_def *filt = flp->data;
+ GtkLabel *nl_lb =
+ GTK_LABEL(gtk_object_get_data(GTK_OBJECT(nl_item), E_FILT_LBL_KEY));
+ GList *nl_model =
+ gtk_object_get_data(GTK_OBJECT(nl_item), E_FILT_LIST_ITEM_MODEL_KEY);
+
+ /* Is this the GtkList item corresponding to the filter list item in
+ question? */
+ if (flp == nl_model) {
+ /* Yes - change the label to correspond to the new name for the filter. */
+ gtk_label_set(nl_lb, filt->name);
+ }
+}
+
+static void
+chg_filter_cb(gpointer data, gpointer user_data)
+{
+ GtkWidget *main_w = data;
+ GtkWidget *filter_l = gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_FILTER_L_KEY);
+
+ gtk_container_foreach(GTK_CONTAINER(filter_l), chg_list_item_cb, user_data);
+}
+
+static void
+filter_chg_bt_clicked_cb(GtkWidget *w, gpointer data)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(w);
+ GtkWidget *name_te = gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_NAME_TE_KEY);
+ GtkWidget *filter_te = gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
+ GtkWidget *filter_l = gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_FILTER_L_KEY);
+ filter_def *filt;
+ const gchar *name, *strval;
+ GList *sl, *fl_entry;
+ GtkObject *l_item;
+ GtkLabel *nl_lb;
+ filter_list_type_t list = *(filter_list_type_t *)data;
+
+ sl = GTK_LIST(filter_l)->selection;
+ name = gtk_entry_get_text(GTK_ENTRY(name_te));
+ strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
+
+ if (sl) { /* Something was selected */
+ l_item = GTK_OBJECT(sl->data);
+ fl_entry = (GList *) gtk_object_get_data(l_item, E_FILT_LIST_ITEM_MODEL_KEY);
+ nl_lb = (GtkLabel *) gtk_object_get_data(l_item, E_FILT_LBL_KEY);
+ if (fl_entry != NULL && nl_lb != NULL) {
+ filt = (filter_def *) fl_entry->data;
+
+ if (strlen(name) > 0 && strlen(strval) > 0 && filt) {
+ g_free(filt->name);
+ g_free(filt->strval);
+ filt->name = g_strdup(name);
+ filt->strval = g_strdup(strval);
+
+ /* Update all the filter list widgets, not just the one in
+ the dialog box in which we clicked on "Copy". */
+ g_list_foreach(get_filter_dialog_list(list), chg_filter_cb, fl_entry);
+ }
+ }
+ }
+}
+
+static void
+filter_chg_bt_destroy_cb(GtkWidget *chg_bt, gpointer data _U_)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(chg_bt);
+
+ gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_CHG_BT_KEY, NULL);
+}
+
+static void
+filter_copy_bt_clicked_cb(GtkWidget *w, gpointer data)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(w);
+ GtkWidget *filter_l = gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_FILTER_L_KEY);
+ GList *sl, *fl_entry, *nfl_entry;
+ gchar *prefix = "Copy of ", *name;
+ GtkObject *l_item;
+ filter_def *filt;
+ filter_list_type_t list = *(filter_list_type_t *)data;
+ new_filter_cb_args_t args;
+
+ sl = GTK_LIST(filter_l)->selection;
+ if (sl) { /* Something was selected */
+ l_item = GTK_OBJECT(sl->data);
+ fl_entry = (GList *) gtk_object_get_data(l_item, E_FILT_LIST_ITEM_MODEL_KEY);
+ if (fl_entry != NULL) {
+ /* Add a new entry, copying the existing entry, to the filter list. */
+ filt = (filter_def *) fl_entry->data;
+ name = g_malloc(strlen(prefix) + strlen(filt->name) + 1);
+ sprintf(name, "%s%s", prefix, filt->name);
+ nfl_entry = add_to_filter_list(list, name, filt->strval);
+ g_free(name);
+
+ /* Update all the filter list widgets, not just the one in
+ the dialog box in which we clicked on "Copy". */
+ args.active_filter_l = filter_l;
+ args.nflp = nfl_entry;
+ g_list_foreach(get_filter_dialog_list(list), new_filter_cb, &args);
+ }
+ }
+}
+
+static void
+filter_copy_bt_destroy_cb(GtkWidget *copy_bt, gpointer data _U_)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(copy_bt);
+
+ gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_COPY_BT_KEY, NULL);
+}
+
+static void
+delete_filter_cb(gpointer data, gpointer user_data)
+{
+ GtkWidget *main_w = data;
+ GtkWidget *filter_l = gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_FILTER_L_KEY);
+ gint pos = *(gint *)user_data;
+
+ gtk_list_clear_items(GTK_LIST(filter_l), pos, pos + 1);
+}
+
+static void
+filter_del_bt_clicked_cb(GtkWidget *w, gpointer data)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(w);
+ GtkWidget *filter_l = gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_FILTER_L_KEY);
+ filter_list_type_t list = *(filter_list_type_t *)data;
+ GList *sl, *fl_entry;
+ GtkObject *l_item;
+ gint pos;
+
+ sl = GTK_LIST(filter_l)->selection;
+ if (sl) { /* Something was selected */
+ l_item = GTK_OBJECT(sl->data);
+ pos = gtk_list_child_position(GTK_LIST(filter_l),
+ GTK_WIDGET(l_item));
+ fl_entry = (GList *) gtk_object_get_data(l_item, E_FILT_LIST_ITEM_MODEL_KEY);
+ if (fl_entry != NULL) {
+ /* Remove the entry from the filter list. */
+ remove_from_filter_list(list, fl_entry);
+
+ /* Update all the filter list widgets, not just the one in
+ the dialog box in which we clicked on "Delete". */
+ g_list_foreach(get_filter_dialog_list(list), delete_filter_cb, &pos);
+ }
+ }
+}
+
+static void
+filter_del_bt_destroy_cb(GtkWidget *del_bt, gpointer data _U_)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(del_bt);
+
+ gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_DEL_BT_KEY, NULL);
+}
+
+static void
+filter_expr_cb(GtkWidget *w _U_, gpointer main_w_arg)
+{
+ GtkWidget *main_w = GTK_WIDGET(main_w_arg);
+ GtkWidget *filter_te;
+
+ filter_te = gtk_object_get_data(GTK_OBJECT(main_w),
+ E_FILT_FILTER_TE_KEY);
+ dfilter_expr_dlg_new(filter_te);
+}
+
+static void
+filter_name_te_destroy_cb(GtkWidget *name_te, gpointer data _U_)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(name_te);
+
+ gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_NAME_TE_KEY, NULL);
+}
+
+static void
+filter_filter_te_destroy_cb(GtkWidget *filter_te, gpointer data _U_)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(filter_te);
+
+ gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_FILTER_TE_KEY, NULL);
+}
diff --git a/gtk2/filter_prefs.h b/gtk2/filter_prefs.h
new file mode 100644
index 0000000000..4c39ea329a
--- /dev/null
+++ b/gtk2/filter_prefs.h
@@ -0,0 +1,51 @@
+/* filter_prefs.h
+ * Definitions for dialog boxes for filter editing
+ * (This used to be a notebook page under "Preferences", hence the
+ * "prefs" in the file name.)
+ *
+ * $Id: filter_prefs.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+#ifndef __FILTER_H__
+#define __FILTER_H__
+
+/*
+ * Structure giving properties of the filter editing dialog box to be
+ * created.
+ */
+typedef struct {
+ gchar *title; /* title of dialog box */
+ gboolean wants_apply_button; /* if it should have an Apply button */
+ gboolean activate_on_ok; /* if parent text widget should be
+ activated on "Ok" or "Apply" */
+} construct_args_t;
+
+void capture_filter_construct_cb(GtkWidget *w, gpointer user_data);
+void display_filter_construct_cb(GtkWidget *w, gpointer construct_args_ptr);
+void cfilter_dialog_cb(GtkWidget *w);
+void dfilter_dialog_cb(GtkWidget *w);
+
+#define E_FILT_TE_PTR_KEY "filter_te_ptr"
+#define E_FILT_CALLER_PTR_KEY "filter_caller_ptr"
+#define E_FILT_DIALOG_PTR_KEY "filter_dialog_ptr"
+
+#endif /* filter.h */
diff --git a/gtk2/find_dlg.c b/gtk2/find_dlg.c
new file mode 100644
index 0000000000..bed136a449
--- /dev/null
+++ b/gtk2/find_dlg.c
@@ -0,0 +1,282 @@
+/* find_dlg.c
+ * Routines for "find frame" window
+ *
+ * $Id: find_dlg.c,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib.h>
+
+#include <epan/proto.h>
+#include <epan/dfilter/dfilter.h>
+#include "globals.h"
+
+#include "ui_util.h"
+#include "find_dlg.h"
+#include "filter_prefs.h"
+#include "simple_dialog.h"
+#include "dlg_utils.h"
+
+/* Capture callback data keys */
+#define E_FIND_FILT_KEY "find_filter_te"
+#define E_FIND_BACKWARD_KEY "find_backward"
+
+static void
+find_frame_ok_cb(GtkWidget *ok_bt, gpointer parent_w);
+
+static void
+find_frame_close_cb(GtkWidget *close_bt, gpointer parent_w);
+
+static void
+find_frame_destroy_cb(GtkWidget *win, gpointer user_data);
+
+/*
+ * Keep a static pointer to the current "Find Frame" window, if any, so
+ * that if somebody tries to do "Find Frame" while there's already a
+ * "Find Frame" window up, we just pop up the existing one, rather than
+ * creating a new one.
+ */
+static GtkWidget *find_frame_w;
+
+void
+find_frame_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ GtkWidget *main_vb, *filter_hb, *filter_bt, *filter_te,
+ *direction_hb, *forward_rb, *backward_rb,
+ *bbox, *ok_bt, *cancel_bt;
+ GtkAccelGroup *accel_group;
+ /* No Apply button, but "OK" not only sets our text widget, it
+ activates it (i.e., it causes us to do the search). */
+ static construct_args_t args = {
+ "Ethereal: Search Filter",
+ FALSE,
+ TRUE
+ };
+
+ if (find_frame_w != NULL) {
+ /* There's already a "Find Frame" dialog box; reactivate it. */
+ reactivate_window(find_frame_w);
+ return;
+ }
+
+ find_frame_w = dlg_window_new("Ethereal: Find Frame");
+ g_signal_connect(G_OBJECT(find_frame_w), "destroy",
+ G_CALLBACK(find_frame_destroy_cb), NULL);
+
+ /* Accelerator group for the accelerators (or, as they're called in
+ Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
+ Ctrl+<key> is an accelerator). */
+ accel_group = gtk_accel_group_new();
+ gtk_window_add_accel_group(GTK_WINDOW(find_frame_w), accel_group);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(find_frame_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* Filter row */
+ filter_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), filter_hb);
+ gtk_widget_show(filter_hb);
+
+ filter_bt = gtk_button_new_with_label("Filter:");
+ g_signal_connect(G_OBJECT(filter_bt), "clicked",
+ G_CALLBACK(display_filter_construct_cb), &args);
+ gtk_box_pack_start(GTK_BOX(filter_hb), filter_bt, FALSE, TRUE, 0);
+ gtk_widget_show(filter_bt);
+
+ filter_te = gtk_entry_new();
+ if (cfile.sfilter) gtk_entry_set_text(GTK_ENTRY(filter_te), cfile.sfilter);
+ gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
+ gtk_box_pack_start(GTK_BOX(filter_hb), filter_te, TRUE, TRUE, 0);
+ gtk_widget_show(filter_te);
+
+ /* Misc row: Forward and reverse radio buttons */
+ direction_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), direction_hb);
+ gtk_widget_show(direction_hb);
+
+ forward_rb = dlg_radio_button_new_with_label_with_mnemonic(NULL, "_Forward",
+ accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(forward_rb), !cfile.sbackward);
+ gtk_box_pack_start(GTK_BOX(direction_hb), forward_rb, TRUE, TRUE, 0);
+ gtk_widget_show(forward_rb);
+
+ backward_rb = dlg_radio_button_new_with_label_with_mnemonic(
+ gtk_radio_button_group(GTK_RADIO_BUTTON(forward_rb)),
+ "_Backward", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(backward_rb), cfile.sbackward);
+ gtk_box_pack_start(GTK_BOX(direction_hb), backward_rb, TRUE, TRUE, 0);
+ gtk_widget_show(backward_rb);
+
+ /* Button row: OK and cancel buttons */
+ 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(main_vb), bbox);
+ gtk_widget_show(bbox);
+
+ ok_bt = gtk_button_new_from_stock(GTK_STOCK_OK);
+ g_signal_connect(G_OBJECT(ok_bt), "clicked",
+ G_CALLBACK(find_frame_ok_cb), GTK_OBJECT(find_frame_w));
+ GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
+ gtk_widget_grab_default(ok_bt);
+ gtk_widget_show(ok_bt);
+
+ cancel_bt = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+ g_signal_connect(G_OBJECT(cancel_bt), "clicked",
+ G_CALLBACK(find_frame_close_cb), GTK_OBJECT(find_frame_w));
+ GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
+ gtk_widget_show(cancel_bt);
+
+ /* Attach pointers to needed widgets to the capture prefs window/object */
+ gtk_object_set_data(GTK_OBJECT(find_frame_w), E_FIND_FILT_KEY, filter_te);
+ gtk_object_set_data(GTK_OBJECT(find_frame_w), E_FIND_BACKWARD_KEY, backward_rb);
+
+ /* Catch the "activate" signal on the frame number text entry, so that
+ if the user types Return there, we act as if the "OK" button
+ had been selected, as happens if Return is typed if some widget
+ that *doesn't* handle the Return key has the input focus. */
+ dlg_set_activate(filter_te, ok_bt);
+
+ /* 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(find_frame_w, cancel_bt);
+
+ /* Give the initial focus to the "Filter" entry box. */
+ gtk_widget_grab_focus(filter_te);
+
+ gtk_widget_show(find_frame_w);
+}
+
+static void
+find_frame_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w)
+{
+ GtkWidget *filter_te, *backward_rb;
+ const gchar *filter_text;
+ dfilter_t *sfcode;
+
+ filter_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_FIND_FILT_KEY);
+ backward_rb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_FIND_BACKWARD_KEY);
+
+ filter_text = gtk_entry_get_text(GTK_ENTRY(filter_te));
+
+ /*
+ * Try to compile the filter.
+ */
+ if (!dfilter_compile(filter_text, &sfcode)) {
+ /* The attempt failed; report an error. */
+ simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
+ return;
+ }
+
+ /* Was it empty? */
+ if (sfcode == NULL) {
+ /* Yes - complain. */
+ simple_dialog(ESD_TYPE_CRIT, NULL,
+ "You didn't specify a filter to use when searching for a frame.");
+ return;
+ }
+
+ /*
+ * Remember the filter.
+ */
+ if (cfile.sfilter)
+ g_free(cfile.sfilter);
+ cfile.sfilter = g_strdup(filter_text);
+
+ cfile.sbackward = GTK_TOGGLE_BUTTON (backward_rb)->active;
+
+ if (!find_packet(&cfile, sfcode)) {
+ /* We didn't find the packet. */
+ simple_dialog(ESD_TYPE_CRIT, NULL, "No packet matched that filter.");
+ return;
+ }
+
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+find_frame_close_cb(GtkWidget *close_bt _U_, gpointer parent_w)
+{
+ gtk_grab_remove(GTK_WIDGET(parent_w));
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+find_frame_destroy_cb(GtkWidget *win, gpointer user_data _U_)
+{
+ GtkWidget *find_frame_filter_w;
+
+ /* Is there a filter edit/selection dialog associated with this
+ Find Frame dialog? */
+ find_frame_filter_w = gtk_object_get_data(GTK_OBJECT(win), E_FILT_DIALOG_PTR_KEY);
+
+ if (find_frame_filter_w != NULL) {
+ /* Yes. Destroy it. */
+ gtk_widget_destroy(find_frame_filter_w);
+ }
+
+ /* Note that we no longer have a "Find Frame" dialog box. */
+ find_frame_w = NULL;
+}
+
+static void
+find_previous_next(GtkWidget *w, gpointer d, gboolean sens)
+{
+ dfilter_t *sfcode;
+
+ if (cfile.sfilter) {
+ if (!dfilter_compile(cfile.sfilter, &sfcode))
+ return;
+ if (sfcode == NULL)
+ return;
+ cfile.sbackward = sens;
+ find_packet(&cfile, sfcode);
+ } else
+ find_frame_cb(w, d);
+}
+
+void
+find_next_cb(GtkWidget *w , gpointer d)
+{
+ find_previous_next(w, d, FALSE);
+}
+
+void
+find_previous_cb(GtkWidget *w , gpointer d)
+{
+ find_previous_next(w, d, TRUE);
+}
diff --git a/gtk2/find_dlg.h b/gtk2/find_dlg.h
new file mode 100644
index 0000000000..fdc09c9149
--- /dev/null
+++ b/gtk2/find_dlg.h
@@ -0,0 +1,33 @@
+/* find_dlg.h
+ * Definitions for "find frame" window
+ *
+ * $Id: find_dlg.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+#ifndef __FIND_DLG_H__
+#define __FIND_DLG_H__
+
+void find_frame_cb(GtkWidget *, gpointer);
+void find_next_cb(GtkWidget *, gpointer);
+void find_previous_cb(GtkWidget *, gpointer);
+
+#endif /* find_dlg.h */
diff --git a/gtk2/follow_dlg.c b/gtk2/follow_dlg.c
new file mode 100644
index 0000000000..3a62e491ac
--- /dev/null
+++ b/gtk2/follow_dlg.c
@@ -0,0 +1,819 @@
+/* follow_dlg.c
+ *
+ * $Id: follow_dlg.c,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 2000 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <errno.h>
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_IO_H
+#include <io.h> /* open/close on win32 */
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef NEED_SNPRINTF_H
+# include "snprintf.h"
+#endif
+
+#include <ctype.h>
+
+#include <langinfo.h>
+#include <iconv.h>
+
+#include "color.h"
+#include "color_utils.h"
+#include "file.h"
+#include "follow_dlg.h"
+#include "follow.h"
+#include "dlg_utils.h"
+#include "keys.h"
+#include "globals.h"
+#include "gtkglobals.h"
+#include "main.h"
+#include "simple_dialog.h"
+#include "packet-ipv6.h"
+#include "prefs.h"
+#include <epan/resolv.h>
+#include "util.h"
+#include "ui_util.h"
+#include <epan/epan_dissect.h>
+
+/* Show Stream */
+typedef enum {
+ FROM_CLIENT,
+ FROM_SERVER,
+ BOTH_HOSTS
+} show_stream_t;
+
+/* Show Type */
+typedef enum {
+ SHOW_ASCII,
+ SHOW_EBCDIC,
+ SHOW_HEXDUMP
+} show_type_t;
+
+typedef struct {
+ show_stream_t show_stream;
+ show_type_t show_type;
+ char data_out_filename[128 + 1];
+ GtkWidget *text;
+ GtkWidget *ascii_bt;
+ GtkWidget *ebcdic_bt;
+ GtkWidget *hexdump_bt;
+ GtkWidget *follow_save_as_w;
+ gboolean is_ipv6;
+} follow_info_t;
+
+static void follow_destroy_cb(GtkWidget * win, gpointer data);
+static void follow_charset_toggle_cb(GtkWidget * w, gpointer parent_w);
+static void follow_load_text(follow_info_t *follow_info);
+static void follow_print_stream(GtkWidget * w, gpointer parent_w);
+static void follow_save_as_cmd_cb(GtkWidget * w, gpointer data);
+static void follow_save_as_ok_cb(GtkWidget * w, GtkFileSelection * fs);
+static void follow_save_as_destroy_cb(GtkWidget * win, gpointer user_data);
+static void follow_stream_om_both(GtkWidget * w, gpointer data);
+static void follow_stream_om_client(GtkWidget * w, gpointer data);
+static void follow_stream_om_server(GtkWidget * w, gpointer data);
+
+
+extern FILE *data_out_file;
+
+
+#define E_FOLLOW_INFO_KEY "follow_info_key"
+
+/* List of "follow_info_t" structures for all "Follow TCP Stream" windows,
+ so we can redraw them all if the colors or font changes. */
+static GList *follow_infos;
+
+/* Add a "follow_info_t" structure to the list. */
+static void
+remember_follow_info(follow_info_t *follow_info)
+{
+ follow_infos = g_list_append(follow_infos, follow_info);
+}
+
+/* Remove a "follow_info_t" structure from the list. */
+static void
+forget_follow_info(follow_info_t *follow_info)
+{
+ follow_infos = g_list_remove(follow_infos, follow_info);
+}
+
+static void
+follow_redraw(gpointer data, gpointer user_data _U_)
+{
+ follow_load_text((follow_info_t *)data);
+}
+
+/* Redraw the text in all "Follow TCP Stream" windows. */
+void
+follow_redraw_all(void)
+{
+ g_list_foreach(follow_infos, follow_redraw, NULL);
+}
+
+/* Follow the TCP stream, if any, to which the last packet that we called
+ a dissection routine on belongs (this might be the most recently
+ selected packet, or it might be the last packet in the file). */
+void
+follow_stream_cb(GtkWidget * w, gpointer data _U_)
+{
+ GtkWidget *streamwindow, *vbox, *txt_scrollw, *text, *filter_te;
+ GtkWidget *hbox, *button, *radio_bt;
+ GtkWidget *stream_om, *stream_menu, *stream_mi;
+ int tmp_fd;
+ gchar *follow_filter;
+ const char *hostname0, *hostname1;
+ char *port0, *port1;
+ char string[128];
+ follow_tcp_stats_t stats;
+ follow_info_t *follow_info;
+
+ /* we got tcp so we can follow */
+ if (cfile.edt->pi.ipproto != 6) {
+ simple_dialog(ESD_TYPE_CRIT, NULL,
+ "Error following stream. Please make\n"
+ "sure you have a TCP packet selected.");
+ return;
+ }
+
+ follow_info = g_new0(follow_info_t, 1);
+
+ /* Create a temporary file into which to dump the reassembled data
+ from the TCP stream, and set "data_out_file" to refer to it, so
+ that the TCP code will write to it.
+
+ XXX - it might be nicer to just have the TCP code directly
+ append stuff to the text widget for the TCP stream window,
+ if we can arrange that said window not pop up until we're
+ done. */
+ tmp_fd = create_tempfile(follow_info->data_out_filename,
+ sizeof follow_info->data_out_filename, "follow");
+
+ if (tmp_fd == -1) {
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "Could not create temporary file %s: %s",
+ follow_info->data_out_filename, strerror(errno));
+ g_free(follow_info);
+ return;
+ }
+
+ data_out_file = fdopen(tmp_fd, "wb");
+ if (data_out_file == NULL) {
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "Could not create temporary file %s: %s",
+ follow_info->data_out_filename, strerror(errno));
+ close(tmp_fd);
+ unlink(follow_info->data_out_filename);
+ g_free(follow_info);
+ return;
+ }
+
+ /* Create a new filter that matches all packets in the TCP stream,
+ and set the display filter entry accordingly */
+ reset_tcp_reassembly();
+ follow_filter = build_follow_filter(&cfile.edt->pi);
+
+ /* Set the display filter entry accordingly */
+ filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
+ gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
+
+ /* Run the display filter so it goes in effect. */
+ filter_packets(&cfile, follow_filter);
+
+ /* Free the filter string, as we're done with it. */
+ g_free(follow_filter);
+
+ /* The data_out_file should now be full of the streams information */
+ fclose(data_out_file);
+
+ /* The data_out_filename file now has all the text that was in the session */
+ streamwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_name(streamwindow, "TCP stream window");
+
+ g_signal_connect(G_OBJECT(streamwindow), "destroy",
+ G_CALLBACK(follow_destroy_cb), NULL);
+
+ g_signal_connect(G_OBJECT(streamwindow), "realize",
+ G_CALLBACK(window_icon_realize_cb), NULL);
+ if (incomplete_tcp_stream) {
+ gtk_window_set_title(GTK_WINDOW(streamwindow),
+ "Contents of TCP stream (incomplete)");
+ } else {
+ gtk_window_set_title(GTK_WINDOW(streamwindow),
+ "Contents of TCP stream");
+ }
+ gtk_widget_set_size_request(GTK_WIDGET(streamwindow), -1,
+ DEF_HEIGHT);
+ gtk_container_border_width(GTK_CONTAINER(streamwindow), 2);
+
+ /* setup the container */
+ vbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
+
+ /* create a scrolled window for the text */
+ txt_scrollw = scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), txt_scrollw, TRUE, TRUE, 0);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ /* create a text box */
+ text = gtk_text_view_new();
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
+ gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
+ follow_info->text = text;
+
+ /* Create hbox */
+ hbox = gtk_hbox_new(FALSE, 1);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+
+ /* Stream to show */
+ follow_tcp_stats(&stats);
+
+ if (stats.is_ipv6) {
+ struct e_in6_addr ipaddr;
+ memcpy(&ipaddr, stats.ip_address[0], 16);
+ hostname0 = get_hostname6(&ipaddr);
+ memcpy(&ipaddr, stats.ip_address[0], 16);
+ hostname1 = get_hostname6(&ipaddr);
+ } else {
+ guint32 ipaddr;
+ memcpy(&ipaddr, stats.ip_address[0], 4);
+ hostname0 = get_hostname(ipaddr);
+ memcpy(&ipaddr, stats.ip_address[1], 4);
+ hostname1 = get_hostname(ipaddr);
+ }
+
+ port0 = get_tcp_port(stats.tcp_port[0]);
+ port1 = get_tcp_port(stats.tcp_port[1]);
+
+ follow_info->is_ipv6 = stats.is_ipv6;
+
+ stream_om = gtk_option_menu_new();
+ stream_menu = gtk_menu_new();
+
+ /* Both Hosts */
+ snprintf(string, sizeof(string),
+ "Entire conversation (%u bytes)",
+ stats.bytes_written[0] + stats.bytes_written[1]);
+ stream_mi = gtk_menu_item_new_with_label(string);
+ g_signal_connect(G_OBJECT(stream_mi), "activate",
+ G_CALLBACK(follow_stream_om_both), follow_info);
+ gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
+ gtk_widget_show(stream_mi);
+ follow_info->show_stream = BOTH_HOSTS;
+
+ /* Host 0 --> Host 1 */
+ snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
+ hostname0, port0, hostname1, port1,
+ stats.bytes_written[0]);
+ stream_mi = gtk_menu_item_new_with_label(string);
+ g_signal_connect(G_OBJECT(stream_mi), "activate",
+ G_CALLBACK(follow_stream_om_client), follow_info);
+ gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
+ gtk_widget_show(stream_mi);
+
+ /* Host 1 --> Host 0 */
+ snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
+ hostname1, port1, hostname0, port0,
+ stats.bytes_written[1]);
+ stream_mi = gtk_menu_item_new_with_label(string);
+ g_signal_connect(G_OBJECT(stream_mi), "activate",
+ G_CALLBACK(follow_stream_om_server), follow_info);
+ gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
+ gtk_widget_show(stream_mi);
+
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(stream_om), stream_menu);
+ /* Set history to 0th item, i.e., the first item. */
+ gtk_option_menu_set_history(GTK_OPTION_MENU(stream_om), 0);
+ gtk_box_pack_start(GTK_BOX(hbox), stream_om, FALSE, FALSE, 0);
+
+ /* ASCII radio button */
+ radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), TRUE);
+ gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
+ g_signal_connect(G_OBJECT(radio_bt), "toggled",
+ G_CALLBACK(follow_charset_toggle_cb),
+ follow_info);
+ follow_info->ascii_bt = radio_bt;
+ follow_info->show_type = SHOW_ASCII;
+
+ /* EBCDIC radio button */
+ radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
+ (GTK_RADIO_BUTTON(radio_bt)),
+ "EBCDIC");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
+ gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
+ g_signal_connect(G_OBJECT(radio_bt), "toggled",
+ G_CALLBACK(follow_charset_toggle_cb),
+ follow_info);
+ follow_info->ebcdic_bt = radio_bt;
+
+ /* HEX DUMP radio button */
+ radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
+ (GTK_RADIO_BUTTON(radio_bt)),
+ "Hex Dump");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
+ gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
+ g_signal_connect(G_OBJECT(radio_bt), "toggled",
+ G_CALLBACK(follow_charset_toggle_cb),
+ follow_info);
+ follow_info->hexdump_bt = radio_bt;
+
+ /* Create Close Button */
+ button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT(streamwindow));
+ gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+ /* 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(streamwindow, button);
+
+ /* Create Save As Button */
+ button = gtk_button_new_from_stock(GTK_STOCK_SAVE_AS);
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(follow_save_as_cmd_cb),
+ follow_info);
+ gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+ /* Create Print Button */
+ button = gtk_button_new_from_stock(GTK_STOCK_PRINT);
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(follow_print_stream), follow_info);
+ gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+
+ /* Tuck away the follow_info object into the window */
+ gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_INFO_KEY,
+ follow_info);
+
+ follow_load_text(follow_info);
+ remember_follow_info(follow_info);
+
+ data_out_file = NULL;
+
+ /* Make sure this widget gets destroyed if we quit the main loop,
+ so that if we exit, we clean up any temporary files we have
+ for "Follow TCP Stream" windows. */
+ gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
+ gtk_widget_show_all(streamwindow);
+}
+
+/* The destroy call back has the responsibility of
+ * unlinking the temporary file */
+static void
+follow_destroy_cb(GtkWidget *w, gpointer data _U_)
+{
+ follow_info_t *follow_info;
+
+ follow_info = gtk_object_get_data(GTK_OBJECT(w), E_FOLLOW_INFO_KEY);
+ unlink(follow_info->data_out_filename);
+ gtk_widget_destroy(w);
+ forget_follow_info(follow_info);
+ g_free(follow_info);
+}
+
+/* XXX - can I emulate follow_charset_toggle_cb() instead of having
+ * 3 different functions here? */
+static void
+follow_stream_om_both(GtkWidget *w _U_, gpointer data)
+{
+ follow_info_t *follow_info = data;
+ follow_info->show_stream = BOTH_HOSTS;
+ follow_load_text(follow_info);
+}
+
+static void
+follow_stream_om_client(GtkWidget *w _U_, gpointer data)
+{
+ follow_info_t *follow_info = data;
+ follow_info->show_stream = FROM_CLIENT;
+ follow_load_text(follow_info);
+}
+
+static void
+follow_stream_om_server(GtkWidget *w _U_, gpointer data)
+{
+ follow_info_t *follow_info = data;
+ follow_info->show_stream = FROM_SERVER;
+ follow_load_text(follow_info);
+}
+
+
+/* Handles the ASCII/EBCDIC toggling */
+static void
+follow_charset_toggle_cb(GtkWidget * w _U_, gpointer data)
+{
+ follow_info_t *follow_info = data;
+
+ if (GTK_TOGGLE_BUTTON(follow_info->ebcdic_bt)->active)
+ follow_info->show_type = SHOW_EBCDIC;
+ else if (GTK_TOGGLE_BUTTON(follow_info->hexdump_bt)->active)
+ follow_info->show_type = SHOW_HEXDUMP;
+ else if (GTK_TOGGLE_BUTTON(follow_info->ascii_bt)->active)
+ follow_info->show_type = SHOW_ASCII;
+ else
+ g_assert_not_reached();
+
+ follow_load_text(follow_info);
+}
+
+#define FLT_BUF_SIZE 1024
+static void
+follow_read_stream(follow_info_t *follow_info,
+ void (*print_line) (char *, int, gboolean, void *),
+ void *arg)
+{
+ tcp_stream_chunk sc;
+ int bcount, iplen;
+ guint8 client_addr[MAX_IPADDR_LEN];
+ guint16 client_port = 0;
+ gboolean is_server;
+ guint16 current_pos, global_client_pos = 0, global_server_pos = 0;
+ guint16 *global_pos;
+ gboolean skip;
+
+ iplen = (follow_info->is_ipv6) ? 16 : 4;
+
+ data_out_file = fopen(follow_info->data_out_filename, "rb");
+ if (data_out_file) {
+ char buffer[FLT_BUF_SIZE];
+ int nchars;
+ while (fread(&sc, 1, sizeof(sc), data_out_file)) {
+ if (client_port == 0) {
+ memcpy(client_addr, sc.src_addr, iplen);
+ client_port = sc.src_port;
+ }
+ skip = FALSE;
+ if (memcmp(client_addr, sc.src_addr, iplen) == 0 &&
+ client_port == sc.src_port) {
+ is_server = FALSE;
+ global_pos = &global_client_pos;
+ if (follow_info->show_stream == FROM_SERVER) {
+ skip = TRUE;
+ }
+ }
+ else {
+ is_server = TRUE;
+ global_pos = &global_server_pos;
+ if (follow_info->show_stream == FROM_CLIENT) {
+ skip = TRUE;
+ }
+ }
+
+ while (sc.dlen > 0) {
+ bcount = (sc.dlen < FLT_BUF_SIZE) ? sc.dlen : FLT_BUF_SIZE;
+ nchars = fread(buffer, 1, bcount, data_out_file);
+ if (nchars == 0)
+ break;
+ sc.dlen -= bcount;
+ if (!skip) {
+ switch (follow_info->show_type) {
+ case SHOW_EBCDIC:
+ /* If our native arch is ASCII, call: */
+ EBCDIC_to_ASCII(buffer, nchars);
+ (*print_line) (buffer, nchars, is_server, arg);
+ break;
+ case SHOW_ASCII:
+ /* If our native arch is EBCDIC, call:
+ * ASCII_TO_EBCDIC(buffer, nchars);
+ */
+ (*print_line) (buffer, nchars, is_server, arg);
+ break;
+ case SHOW_HEXDUMP:
+ current_pos = 0;
+ while (current_pos < nchars) {
+ gchar hexbuf[256];
+ gchar hexchars[] = "0123456789abcdef";
+ int i, cur;
+ /* is_server indentation : put 63 spaces at the begenning
+ * of the string */
+ sprintf(hexbuf, (is_server &&
+ follow_info->show_stream == BOTH_HOSTS) ?
+ " "
+ " %08X " :
+ "%08X ", *global_pos);
+ cur = strlen(hexbuf);
+ for (i = 0; i < 16 && current_pos + i < nchars;
+ i++) {
+ hexbuf[cur++] =
+ hexchars[(buffer[current_pos + i] & 0xf0)
+ >> 4];
+ hexbuf[cur++] =
+ hexchars[buffer[current_pos + i] & 0x0f];
+ if (i == 7) {
+ hexbuf[cur++] = ' ';
+ hexbuf[cur++] = ' ';
+ } else if (i != 15)
+ hexbuf[cur++] = ' ';
+ }
+ /* Fill it up if column isn't complete */
+ if (i < 16) {
+ int j;
+
+ for (j = i; j < 16; j++) {
+ if (j == 7) hexbuf[cur++] = ' ';
+ hexbuf[cur++] = ' ';
+ hexbuf[cur++] = ' ';
+ hexbuf[cur++] = ' ';
+ }
+ } else
+ hexbuf[cur++] = ' ';
+
+ /* Now dump bytes as text */
+ for (i = 0; i < 16 && current_pos + i < nchars;
+ i++) {
+ hexbuf[cur++] =
+ (isprint((guchar)buffer[current_pos + i]) ?
+ buffer[current_pos + i] : '.' );
+ if (i == 7) {
+ hexbuf[cur++] = ' ';
+ }
+ }
+ current_pos += i;
+ (*global_pos) += i;
+ hexbuf[cur++] = '\n';
+ hexbuf[cur] = 0;
+ (*print_line) (hexbuf, strlen(hexbuf), is_server, arg);
+ }
+ break;
+ }
+ }
+ }
+ }
+ if (ferror(data_out_file)) {
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "Error reading temporary file %s: %s", follow_info->data_out_filename,
+ strerror(errno));
+ }
+ fclose(data_out_file);
+ data_out_file = NULL;
+ } else {
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "Could not open temporary file %s: %s", follow_info->data_out_filename,
+ strerror(errno));
+ }
+}
+
+/*
+ * XXX - for text printing, we probably want to wrap lines at 80 characters;
+ * for PostScript printing, we probably want to wrap them at the appropriate
+ * width, and perhaps put some kind of dingbat (to use the technical term)
+ * to indicate a wrapped line, along the lines of what's done when displaying
+ * this in a window, as per Warren Young's suggestion.
+ *
+ * For now, we support only text printing.
+ */
+static void
+follow_print_text(char *buffer, int nchars, gboolean is_server _U_, void *arg)
+{
+ FILE *fh = arg;
+
+ fwrite(buffer, nchars, 1, fh);
+}
+
+static void
+follow_print_stream(GtkWidget * w _U_, gpointer data)
+{
+ FILE *fh;
+ gboolean to_file;
+ char *print_dest;
+ follow_info_t *follow_info = data;
+
+ switch (prefs.pr_dest) {
+ case PR_DEST_CMD:
+ print_dest = prefs.pr_cmd;
+ to_file = FALSE;
+ break;
+
+ case PR_DEST_FILE:
+ print_dest = prefs.pr_file;
+ to_file = TRUE;
+ break;
+ default: /* "Can't happen" */
+ simple_dialog(ESD_TYPE_CRIT, NULL,
+ "Couldn't figure out where to send the print "
+ "job. Check your preferences.");
+ return;
+ }
+
+ fh = open_print_dest(to_file, print_dest);
+ if (fh == NULL) {
+ switch (to_file) {
+ case FALSE:
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "Couldn't run print command %s.", prefs.pr_cmd);
+ break;
+
+ case TRUE:
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ file_write_error_message(errno), prefs.pr_file);
+ break;
+ }
+ return;
+ }
+
+ print_preamble(fh, PR_FMT_TEXT);
+ follow_read_stream(follow_info, follow_print_text, fh);
+ print_finale(fh, PR_FMT_TEXT);
+ close_print_dest(to_file, fh);
+}
+
+static void
+follow_add_to_gtk_text(char *buffer, int nchars, gboolean is_server,
+ void *arg)
+{
+ GtkWidget *text = arg;
+ GdkColor fg, bg;
+ GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
+ GtkTextIter iter;
+ GtkTextTag *tag;
+ gsize outbytes;
+ gchar *convbuf;
+
+#ifdef _WIN32
+ /* While our isprint() hack is in place, we
+ * have to use convert some chars to '.' in order
+ * to be able to see the data we *should* see
+ * in the GtkText widget.
+ */
+ int i;
+
+ for (i = 0; i < nchars; i++) {
+ if (buffer[i] == 0x0a || buffer[i] == 0x0d) {
+ continue;
+ }
+ else if (! isprint(buffer[i])) {
+ buffer[i] = '.';
+ }
+ }
+#else
+ int i;
+
+ for (i = 0; i < nchars; i++) {
+ if (buffer[i] == '\n')
+ continue;
+ if (! isprint(buffer[i])) {
+ buffer[i] = '.';
+ }
+ }
+#endif
+
+ if (is_server) {
+ color_t_to_gdkcolor(&fg, &prefs.st_server_fg);
+ color_t_to_gdkcolor(&bg, &prefs.st_server_bg);
+ } else {
+ color_t_to_gdkcolor(&fg, &prefs.st_client_fg);
+ color_t_to_gdkcolor(&bg, &prefs.st_client_bg);
+ }
+ gtk_text_buffer_get_end_iter(buf, &iter);
+ tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk", &fg,
+ "background-gdk", &bg, "font-desc",
+ m_r_font, NULL);
+ convbuf = g_locale_to_utf8(buffer, nchars, NULL, &outbytes, NULL);
+ gtk_text_buffer_insert_with_tags(buf, &iter, convbuf, outbytes, tag,
+ NULL);
+ g_free(convbuf);
+}
+
+static void
+follow_load_text(follow_info_t *follow_info)
+{
+ GtkTextBuffer *buf;
+
+ buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
+
+ /* Delete any info already in text box */
+ gtk_text_buffer_set_text(buf, "", -1);
+
+ follow_read_stream(follow_info, follow_add_to_gtk_text, follow_info->text);
+}
+
+
+/*
+ * Keep a static pointer to the current "Save TCP Follow Stream As" window, if
+ * any, so that if somebody tries to do "Save"
+ * while there's already a "Save TCP Follow Stream" window up, we just pop
+ * up the existing one, rather than creating a new one.
+ */
+static void
+follow_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
+{
+ GtkWidget *ok_bt, *new_win;
+ follow_info_t *follow_info = data;
+
+ if (follow_info->follow_save_as_w != NULL) {
+ /* There's already a dialog box; reactivate it. */
+ reactivate_window(follow_info->follow_save_as_w);
+ return;
+ }
+
+ new_win = gtk_file_selection_new("Ethereal: Save TCP Follow Stream As");
+ follow_info->follow_save_as_w = new_win;
+ g_signal_connect(G_OBJECT(new_win), "destroy",
+ G_CALLBACK(follow_save_as_destroy_cb), follow_info);
+
+ /* Tuck away the follow_info object into the window */
+ gtk_object_set_data(GTK_OBJECT(new_win), E_FOLLOW_INFO_KEY,
+ follow_info);
+
+ /* If we've opened a file, start out by showing the files in the directory
+ in which that file resided. */
+ if (last_open_dir)
+ gtk_file_selection_complete(GTK_FILE_SELECTION(new_win),
+ last_open_dir);
+
+ /* Connect the ok_button to file_save_as_ok_cb function and pass along a
+ pointer to the file selection box widget */
+ ok_bt = GTK_FILE_SELECTION(new_win)->ok_button;
+ g_signal_connect(G_OBJECT(ok_bt), "clicked",
+ G_CALLBACK(follow_save_as_ok_cb), new_win);
+
+ /* Connect the cancel_button to destroy the widget */
+ gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(new_win)->cancel_button),
+ "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT(new_win));
+
+ /* 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(new_win,
+ GTK_FILE_SELECTION(new_win)->cancel_button);
+
+ gtk_file_selection_set_filename(GTK_FILE_SELECTION(new_win), "");
+ gtk_widget_show_all(new_win);
+}
+
+
+static void
+follow_save_as_ok_cb(GtkWidget * w _U_, GtkFileSelection * fs)
+{
+ gchar *to_name;
+ follow_info_t *follow_info;
+ FILE *fh;
+
+ to_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
+
+ gtk_widget_hide(GTK_WIDGET(fs));
+ follow_info = gtk_object_get_data(GTK_OBJECT(fs), E_FOLLOW_INFO_KEY);
+ gtk_widget_destroy(GTK_WIDGET(fs));
+
+ fh = fopen(to_name, "wb");
+ if (fh == NULL) {
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ file_write_error_message(errno), to_name);
+ return;
+ }
+
+ follow_read_stream(follow_info, follow_print_text, fh);
+ fclose(fh);
+ g_free(to_name);
+}
+
+static void
+follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
+{
+ follow_info_t *follow_info = data;
+
+ /* Note that we no longer have a dialog box. */
+ follow_info->follow_save_as_w = NULL;
+}
diff --git a/gtk2/follow_dlg.h b/gtk2/follow_dlg.h
new file mode 100644
index 0000000000..cb42d8915b
--- /dev/null
+++ b/gtk2/follow_dlg.h
@@ -0,0 +1,33 @@
+/* follow_dlg.c
+ *
+ * $Id: follow_dlg.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.org>
+ * Copyright 2000 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.
+ *
+ */
+
+#ifndef __FOLLOW_DLG_H__
+#define __FOLLOW_DLG_H__
+
+void follow_stream_cb( GtkWidget *, gpointer);
+
+/* Redraw the text in all "Follow TCP Stream" windows. */
+void follow_redraw_all(void);
+
+#endif
diff --git a/gtk2/goto_dlg.c b/gtk2/goto_dlg.c
new file mode 100644
index 0000000000..46bf658e7d
--- /dev/null
+++ b/gtk2/goto_dlg.c
@@ -0,0 +1,169 @@
+/* goto_dlg.c
+ * Routines for "go to frame" window
+ *
+ * $Id: goto_dlg.c,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <stdlib.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib.h>
+
+#include <epan/proto.h>
+#include "globals.h"
+
+#include "goto_dlg.h"
+#include "simple_dialog.h"
+#include "dlg_utils.h"
+
+/* Capture callback data keys */
+#define E_GOTO_FNUMBER_KEY "goto_fnumber_te"
+
+static void
+goto_frame_ok_cb(GtkWidget *ok_bt, gpointer parent_w);
+
+static void
+goto_frame_close_cb(GtkWidget *close_bt, gpointer parent_w);
+
+void
+goto_frame_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ GtkWidget *goto_frame_w, *main_vb, *fnumber_hb, *fnumber_lb, *fnumber_te,
+ *bbox, *ok_bt, *cancel_bt;
+
+ goto_frame_w = dlg_window_new("Ethereal: Go To Frame");
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(goto_frame_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* Frame number row */
+ fnumber_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), fnumber_hb);
+ gtk_widget_show(fnumber_hb);
+
+ fnumber_lb = gtk_label_new("Frame number:");
+ gtk_box_pack_start(GTK_BOX(fnumber_hb), fnumber_lb, FALSE, FALSE, 0);
+ gtk_widget_show(fnumber_lb);
+
+ fnumber_te = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(fnumber_hb), fnumber_te, FALSE, FALSE, 0);
+ gtk_widget_show(fnumber_te);
+
+ /* Button row: OK and cancel buttons */
+ 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(main_vb), bbox);
+ gtk_widget_show(bbox);
+
+ ok_bt = gtk_button_new_from_stock(GTK_STOCK_OK);
+ g_signal_connect(G_OBJECT(ok_bt), "clicked",
+ G_CALLBACK(goto_frame_ok_cb), GTK_OBJECT(goto_frame_w));
+ GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
+ gtk_widget_grab_default(ok_bt);
+ gtk_widget_show(ok_bt);
+
+ cancel_bt = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+ g_signal_connect(G_OBJECT(cancel_bt), "clicked",
+ G_CALLBACK(goto_frame_close_cb), GTK_OBJECT(goto_frame_w));
+ GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
+ gtk_widget_show(cancel_bt);
+
+ /* Attach pointers to needed widgets to the capture prefs window/object */
+ gtk_object_set_data(GTK_OBJECT(goto_frame_w), E_GOTO_FNUMBER_KEY, fnumber_te);
+
+ /* Catch the "activate" signal on the frame number text entry, so that
+ if the user types Return there, we act as if the "OK" button
+ had been selected, as happens if Return is typed if some widget
+ that *doesn't* handle the Return key has the input focus. */
+ dlg_set_activate(fnumber_te, ok_bt);
+
+ /* 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(goto_frame_w, cancel_bt);
+
+ /* Give the initial focus to the "Frame number" entry box. */
+ gtk_widget_grab_focus(fnumber_te);
+
+ gtk_widget_show(goto_frame_w);
+}
+
+static void
+goto_frame_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w)
+{
+ GtkWidget *fnumber_te;
+ const gchar *fnumber_text;
+ guint fnumber;
+ char *p;
+
+ fnumber_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_GOTO_FNUMBER_KEY);
+
+ fnumber_text = gtk_entry_get_text(GTK_ENTRY(fnumber_te));
+ fnumber = strtoul(fnumber_text, &p, 10);
+ if (p == fnumber_text || *p != '\0') {
+ /* Illegal number.
+ XXX - what about negative numbers (which "strtoul()" allows)?
+ Can we hack up signal handlers for the widget to make it
+ reject attempts to type in characters other than digits? */
+ simple_dialog(ESD_TYPE_CRIT, NULL,
+ "The frame number you entered isn't a valid number.");
+ return;
+ }
+
+ switch (goto_frame(&cfile, fnumber)) {
+
+ case NO_SUCH_FRAME:
+ simple_dialog(ESD_TYPE_CRIT, NULL, "There is no frame with that frame number.");
+ return;
+
+ case FRAME_NOT_DISPLAYED:
+ /* XXX - add it to the display filter? */
+ simple_dialog(ESD_TYPE_CRIT, NULL, "The frame with that frame number is not currently being displayed.");
+ return;
+
+ case FOUND_FRAME:
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+ break;
+ }
+}
+
+static void
+goto_frame_close_cb(GtkWidget *close_bt _U_, gpointer parent_w)
+{
+ gtk_grab_remove(GTK_WIDGET(parent_w));
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+}
diff --git a/gtk2/goto_dlg.h b/gtk2/goto_dlg.h
new file mode 100644
index 0000000000..095b9c0d16
--- /dev/null
+++ b/gtk2/goto_dlg.h
@@ -0,0 +1,31 @@
+/* goto_dlg.h
+ * Definitions for "go to frame" window
+ *
+ * $Id: goto_dlg.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+#ifndef __GOTO_DLG_H__
+#define __GOTO_DLG_H__
+
+void goto_frame_cb(GtkWidget *, gpointer);
+
+#endif /* goto_dlg.h */
diff --git a/gtk2/gtkglobals.h b/gtk2/gtkglobals.h
new file mode 100644
index 0000000000..4889a3f7c8
--- /dev/null
+++ b/gtk2/gtkglobals.h
@@ -0,0 +1,58 @@
+/* gtkglobals.h
+ * GTK-related Global defines, etc.
+ *
+ * $Id: gtkglobals.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifndef __GTKGLOBALS_H__
+#define __GTKGLOBALS_H__
+
+#ifndef __GTK_H__
+#include <gtk/gtk.h>
+#endif
+
+extern GtkWidget *top_level, *packet_list, *tree_view, *byte_nb_ptr;
+extern PangoFontDescription *m_r_font, *m_b_font;
+
+void set_plist_sel_browse(gboolean);
+void set_plist_font(PangoFontDescription *font);
+
+#ifdef _WIN32
+/* It appears that isprint() is not working well
+ * with gtk+'s text widget. By narrowing down what
+ * we print, the ascii portion of the hex display works.
+ * MSVCRT's isprint() returns true on values like 0xd2,
+ * which cause the GtkTextWidget to go wacko.
+ *
+ * (I.e., whilst non-ASCII characters are considered printable
+ * in the locale in which Ethereal is running - which they might
+ * well be, if, for example, the locale supports ISO Latin 1 -
+ * GTK+'s text widget on Windows doesn't seem to handle them
+ * correctly.)
+ *
+ * This is a quick fix for the symptom, not the
+ * underlying problem.
+ */
+#undef isprint
+#define isprint(c) (c >= 0x20 && c <= 0x7f)
+#endif
+
+#endif
diff --git a/gtk2/gui_prefs.c b/gtk2/gui_prefs.c
new file mode 100644
index 0000000000..a7e7763407
--- /dev/null
+++ b/gtk2/gui_prefs.c
@@ -0,0 +1,732 @@
+/* gui_prefs.c
+ * Dialog box for GUI preferences
+ *
+ * $Id: gui_prefs.c,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <gtk/gtk.h>
+
+#include "color.h"
+#include "color_utils.h"
+#include "globals.h"
+#include "gui_prefs.h"
+#include "gtkglobals.h"
+#include "follow_dlg.h"
+#include "help_dlg.h"
+#include "prefs.h"
+#include "prefs_dlg.h"
+#include "ui_util.h"
+#include "simple_dialog.h"
+#include "dlg_utils.h"
+#include "proto_draw.h"
+#include "main.h"
+
+static void font_browse_cb(GtkWidget *w, gpointer data);
+static void font_browse_ok_cb(GtkWidget *w, GtkFontSelectionDialog *fs);
+static void font_browse_destroy(GtkWidget *win, gpointer data);
+static gint fetch_enum_value(gpointer control, const enum_val_t *enumvals);
+static void color_browse_cb(GtkWidget *w, gpointer data);
+static void update_text_color(GtkWidget *w, gpointer data);
+static void update_current_color(GtkWidget *w, gpointer data);
+static void color_ok_cb(GtkWidget *w, gpointer data);
+static void color_cancel_cb(GtkWidget *w, gpointer data);
+static gboolean color_delete_cb(GtkWidget *prefs_w, gpointer dummy);
+static void color_destroy_cb(GtkWidget *w, gpointer data);
+static void fetch_colors(void);
+
+#define SCROLLBAR_PLACEMENT_KEY "scrollbar_placement"
+#define PLIST_SEL_BROWSE_KEY "plist_sel_browse"
+#define PTREE_SEL_BROWSE_KEY "ptree_sel_browse"
+#define PTREE_LINE_STYLE_KEY "ptree_line_style"
+#define PTREE_EXPANDER_STYLE_KEY "ptree_expander_style"
+#define HEX_DUMP_HIGHLIGHT_STYLE_KEY "hex_dump_highlight_style"
+#define GEOMETRY_POSITION_KEY "geometry_position"
+#define GEOMETRY_SIZE_KEY "geometry_size"
+
+#define FONT_DIALOG_PTR_KEY "font_dialog_ptr"
+#define FONT_CALLER_PTR_KEY "font_caller_ptr"
+#define COLOR_DIALOG_PTR_KEY "color_dialog_ptr"
+#define COLOR_CALLER_PTR_KEY "color_caller_ptr"
+#define COLOR_SAMPLE_PTR_KEY "color_sample_ptr"
+#define COLOR_SELECTION_PTR_KEY "color_selection_ptr"
+
+static const enum_val_t scrollbar_placement_vals[] = {
+ { "Left", FALSE },
+ { "Right", TRUE },
+ { NULL, 0 }
+};
+
+static const enum_val_t selection_mode_vals[] = {
+ { "Selects", FALSE },
+ { "Browses", TRUE },
+ { NULL, 0 }
+};
+
+static const enum_val_t line_style_vals[] = {
+ { "None", 0 },
+ { "Solid", 1 },
+ { "Dotted", 2 },
+ { "Tabbed", 3 },
+ { NULL, 0 }
+};
+
+static const enum_val_t expander_style_vals[] = {
+ { "None", 0 },
+ { "Square", 1 },
+ { "Triangle", 2 },
+ { "Circular", 3 },
+ { NULL, 0 }
+};
+
+static const enum_val_t highlight_style_vals[] = {
+ { "Bold", 0 },
+ { "Inverse", 1 },
+ { NULL, 0 }
+};
+
+/* Set to FALSE initially; set to TRUE if the user ever hits "OK" on
+ the "Colors..." dialog, so that we know that they (probably) changed
+ colors, and therefore that the "apply" function needs to recolor
+ any marked packets. */
+static gboolean colors_changed;
+
+/* Set to FALSE initially; set to TRUE if the user ever hits "OK" on
+ the "Font..." dialog, so that we know that they (probably) changed
+ the font, and therefore that the "apply" function needs to take care
+ of that */
+static gboolean font_changed;
+
+/* Font name from the font dialog box; if "font_changed" is TRUE, this
+ has been set to the name of the font the user selected. */
+static gchar *new_font_name;
+
+#define GUI_TABLE_ROWS 8
+GtkWidget*
+gui_prefs_show(void)
+{
+ GtkWidget *main_tb, *main_vb, *hbox, *font_bt, *color_bt;
+ GtkWidget *scrollbar_om, *plist_browse_om;
+ GtkWidget *ptree_browse_om, *line_style_om;
+ GtkWidget *expander_style_om, *highlight_style_om;
+ GtkWidget *save_position_cb, *save_size_cb;
+
+ /* The colors or font haven't been changed yet. */
+ colors_changed = FALSE;
+ font_changed = FALSE;
+
+ /* Main vertical box */
+ main_vb = gtk_vbox_new(FALSE, 7);
+ gtk_container_border_width( GTK_CONTAINER(main_vb), 5 );
+
+ /* Main horizontal box */
+ /* XXX - Is therea a better way to center the table? */
+ hbox = gtk_hbox_new(FALSE, 7);
+ gtk_box_pack_start (GTK_BOX(main_vb), hbox, TRUE, FALSE, 0);
+
+ /* Main table */
+ main_tb = gtk_table_new(GUI_TABLE_ROWS, 3, FALSE);
+ gtk_box_pack_start( GTK_BOX(hbox), main_tb, TRUE, FALSE, 0 );
+ gtk_table_set_row_spacings( GTK_TABLE(main_tb), 10 );
+ gtk_table_set_col_spacings( GTK_TABLE(main_tb), 15 );
+ gtk_table_set_col_spacing( GTK_TABLE(main_tb), 1, 50 );
+
+ /* Scrollbar placement */
+ scrollbar_om = create_preference_option_menu(main_tb, 0,
+ "Vertical scrollbar placement:", NULL, scrollbar_placement_vals,
+ prefs.gui_scrollbar_on_right);
+ gtk_object_set_data(GTK_OBJECT(main_vb), SCROLLBAR_PLACEMENT_KEY,
+ scrollbar_om);
+
+ /* Packet list selection browseable */
+ plist_browse_om = create_preference_option_menu(main_tb, 1,
+ "Packet list mouse behavior:", NULL, selection_mode_vals,
+ prefs.gui_plist_sel_browse);
+ gtk_object_set_data(GTK_OBJECT(main_vb), PLIST_SEL_BROWSE_KEY,
+ plist_browse_om);
+
+ /* Proto tree selection browseable */
+ ptree_browse_om = create_preference_option_menu(main_tb, 2,
+ "Protocol tree mouse behavior:", NULL, selection_mode_vals,
+ prefs.gui_ptree_sel_browse);
+ gtk_object_set_data(GTK_OBJECT(main_vb), PTREE_SEL_BROWSE_KEY,
+ ptree_browse_om);
+
+ /* Tree line style */
+ line_style_om = create_preference_option_menu(main_tb, 3,
+ "Tree line style:", NULL, line_style_vals,
+ prefs.gui_ptree_line_style);
+ gtk_object_set_data(GTK_OBJECT(main_vb), PTREE_LINE_STYLE_KEY,
+ line_style_om);
+
+ /* Tree expander style */
+ expander_style_om = create_preference_option_menu(main_tb, 4,
+ "Tree expander style:", NULL, expander_style_vals,
+ prefs.gui_ptree_expander_style);
+ gtk_object_set_data(GTK_OBJECT(main_vb), PTREE_EXPANDER_STYLE_KEY,
+ expander_style_om);
+
+ /* Hex Dump highlight style */
+ highlight_style_om = create_preference_option_menu(main_tb, 5,
+ "Hex display highlight style:", NULL, highlight_style_vals,
+ prefs.gui_hex_dump_highlight_style);
+ gtk_object_set_data(GTK_OBJECT(main_vb), HEX_DUMP_HIGHLIGHT_STYLE_KEY,
+ highlight_style_om);
+
+ /* Geometry prefs */
+ save_position_cb = create_preference_check_button(main_tb,
+ 6, "Save window position:", NULL, prefs.gui_geometry_save_position);
+ gtk_object_set_data(GTK_OBJECT(main_vb), GEOMETRY_POSITION_KEY,
+ save_position_cb);
+
+ save_size_cb = create_preference_check_button(main_tb,
+ 7, "Save window size:", NULL, prefs.gui_geometry_save_size);
+ gtk_object_set_data(GTK_OBJECT(main_vb), GEOMETRY_SIZE_KEY,
+ save_size_cb);
+
+ /* "Font..." button - click to open a font selection dialog box. */
+ font_bt = gtk_button_new_from_stock(GTK_STOCK_SELECT_FONT);
+ g_signal_connect(G_OBJECT(font_bt), "clicked",
+ G_CALLBACK(font_browse_cb), NULL);
+ gtk_table_attach_defaults( GTK_TABLE(main_tb), font_bt, 2, 3, 0, 1 );
+
+ /* "Colors..." button - click to open a color selection dialog box. */
+ color_bt = gtk_button_new_from_stock(GTK_STOCK_SELECT_COLOR);
+ g_signal_connect(G_OBJECT(color_bt), "clicked",
+ G_CALLBACK(color_browse_cb), NULL);
+ gtk_table_attach_defaults( GTK_TABLE(main_tb), color_bt, 2, 3, 1, 2 );
+
+ /* Show 'em what we got */
+ gtk_widget_show_all(main_vb);
+
+ return(main_vb);
+}
+
+/* Create a font dialog for browsing. */
+static void
+font_browse_cb(GtkWidget *w, gpointer data _U_)
+{
+ GtkWidget *caller = gtk_widget_get_toplevel(w);
+ GtkWidget *font_browse_w;
+ static gchar *fixedwidths[] = { "c", "m", NULL };
+
+ /* Has a font dialog box already been opened for that top-level
+ widget? */
+ font_browse_w = gtk_object_get_data(GTK_OBJECT(caller),
+ FONT_DIALOG_PTR_KEY);
+
+ if (font_browse_w != NULL) {
+ /* Yes. Just re-activate that dialog box. */
+ reactivate_window(font_browse_w);
+ return;
+ }
+
+ /* Now create a new dialog. */
+ font_browse_w = gtk_font_selection_dialog_new("Ethereal: Select Font");
+ gtk_window_set_transient_for(GTK_WINDOW(font_browse_w),
+ GTK_WINDOW(top_level));
+
+ /* Call a handler when we're destroyed, so we can inform
+ our caller, if any, that we've been destroyed. */
+ g_signal_connect(G_OBJECT(font_browse_w), "destroy",
+ G_CALLBACK(font_browse_destroy), NULL);
+
+ /* Set its filter to show only fixed_width fonts. */
+#if 0
+ /* XXX - doesn't work with GTK+ v2 */
+ gtk_font_selection_dialog_set_filter(
+ GTK_FONT_SELECTION_DIALOG(font_browse_w),
+ GTK_FONT_FILTER_BASE, /* user can't change the filter */
+ GTK_FONT_ALL, /* bitmap or scalable are fine */
+ NULL, /* all foundries are OK */
+ NULL, /* all weights are OK (XXX - normal only?) */
+ NULL, /* all slants are OK (XXX - Roman only?) */
+ NULL, /* all setwidths are OK */
+ fixedwidths, /* ONLY fixed-width fonts */
+ NULL); /* all charsets are OK (XXX - ISO 8859/1 only?) */
+#endif
+ /* Set the font to the current font. */
+ gtk_font_selection_dialog_set_font_name(
+ GTK_FONT_SELECTION_DIALOG(font_browse_w), prefs.gui_font_name);
+
+ /* Set the FONT_CALLER_PTR_KEY for the new dialog to point to
+ our caller. */
+ gtk_object_set_data(GTK_OBJECT(font_browse_w), FONT_CALLER_PTR_KEY,
+ caller);
+
+ /* Set the FONT_DIALOG_PTR_KEY for the caller to point to us */
+ gtk_object_set_data(GTK_OBJECT(caller), FONT_DIALOG_PTR_KEY,
+ font_browse_w);
+
+ /* Connect the ok_button to font_browse_ok_cb function and pass along a
+ pointer to the font selection box widget */
+ g_signal_connect(G_OBJECT(GTK_FONT_SELECTION_DIALOG(font_browse_w)->ok_button),
+ "clicked", G_CALLBACK(font_browse_ok_cb),
+ font_browse_w);
+
+ /* Connect the cancel_button to destroy the widget */
+ gtk_signal_connect_object(GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(font_browse_w)->cancel_button),
+ "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT(font_browse_w));
+
+ /* 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(font_browse_w,
+ GTK_FONT_SELECTION_DIALOG(font_browse_w)->cancel_button);
+
+ gtk_widget_show(font_browse_w);
+}
+
+static void
+font_browse_ok_cb(GtkWidget *w _U_, GtkFontSelectionDialog *fs)
+{
+ gchar *font_name;
+ PangoFontDescription *new_r_font, *new_b_font;
+
+ font_name = g_strdup(gtk_font_selection_dialog_get_font_name(
+ GTK_FONT_SELECTION_DIALOG(fs)));
+ if (font_name == NULL) {
+ /* No font was selected; let the user know, but don't
+ tear down the font selection dialog, so they can
+ try again. */
+ simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
+ "You have not selected a font.");
+ return;
+ }
+
+ /* Now load those fonts, just to make sure we can. */
+ new_r_font = pango_font_description_from_string(font_name);
+ if (new_r_font == NULL) {
+ /* Oops, that font didn't work.
+ Tell the user, but don't tear down the font selection
+ dialog, so that they can try again. */
+ simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
+ "The font you selected cannot be loaded.");
+
+ g_free(font_name);
+ return;
+ }
+
+ new_b_font = pango_font_description_copy(new_r_font);
+ pango_font_description_set_weight(new_b_font,
+ PANGO_WEIGHT_BOLD);
+ if (new_b_font == NULL) {
+ /* Oops, that font didn't work.
+ Tell the user, but don't tear down the font selection
+ dialog, so that they can try again. */
+ simple_dialog(ESD_TYPE_CRIT | ESD_TYPE_MODAL, NULL,
+ "The font you selected doesn't have a boldface version.");
+
+ g_free(font_name);
+ pango_font_description_free(new_r_font);
+ return;
+ }
+
+ font_changed = TRUE;
+ new_font_name = font_name;
+
+ gtk_widget_hide(GTK_WIDGET(fs));
+ gtk_widget_destroy(GTK_WIDGET(fs));
+}
+
+static void
+font_browse_destroy(GtkWidget *win, gpointer data _U_)
+{
+ GtkWidget *caller;
+
+ /* Get the widget that requested that we be popped up, if any.
+ (It should arrange to destroy us if it's destroyed, so
+ that we don't get a pointer to a non-existent window here.) */
+ caller = gtk_object_get_data(GTK_OBJECT(win), FONT_CALLER_PTR_KEY);
+
+ if (caller != NULL) {
+ /* Tell it we no longer exist. */
+ gtk_object_set_data(GTK_OBJECT(caller), FONT_DIALOG_PTR_KEY,
+ NULL);
+ }
+
+ /* Now nuke this window. */
+ gtk_grab_remove(GTK_WIDGET(win));
+ gtk_widget_destroy(GTK_WIDGET(win));
+}
+
+static gint
+fetch_enum_value(gpointer control, const enum_val_t *enumvals)
+{
+ return fetch_preference_option_menu_val(GTK_WIDGET(control), enumvals);
+}
+
+void
+gui_prefs_fetch(GtkWidget *w)
+{
+ prefs.gui_scrollbar_on_right = fetch_enum_value(
+ gtk_object_get_data(GTK_OBJECT(w), SCROLLBAR_PLACEMENT_KEY),
+ scrollbar_placement_vals);
+ prefs.gui_plist_sel_browse = fetch_enum_value(
+ gtk_object_get_data(GTK_OBJECT(w), PLIST_SEL_BROWSE_KEY),
+ selection_mode_vals);
+ prefs.gui_ptree_sel_browse = fetch_enum_value(
+ gtk_object_get_data(GTK_OBJECT(w), PTREE_SEL_BROWSE_KEY),
+ selection_mode_vals);
+ prefs.gui_ptree_line_style = fetch_enum_value(
+ gtk_object_get_data(GTK_OBJECT(w), PTREE_LINE_STYLE_KEY),
+ line_style_vals);
+ prefs.gui_ptree_expander_style = fetch_enum_value(
+ gtk_object_get_data(GTK_OBJECT(w), PTREE_EXPANDER_STYLE_KEY),
+ expander_style_vals);
+ prefs.gui_hex_dump_highlight_style = fetch_enum_value(
+ gtk_object_get_data(GTK_OBJECT(w), HEX_DUMP_HIGHLIGHT_STYLE_KEY),
+ highlight_style_vals);
+ prefs.gui_geometry_save_position =
+ gtk_toggle_button_get_active(gtk_object_get_data(GTK_OBJECT(w),
+ GEOMETRY_POSITION_KEY));
+ prefs.gui_geometry_save_size =
+ gtk_toggle_button_get_active(gtk_object_get_data(GTK_OBJECT(w),
+ GEOMETRY_SIZE_KEY));
+
+ if (font_changed) {
+ if (prefs.gui_font_name != NULL)
+ g_free(prefs.gui_font_name);
+ prefs.gui_font_name = g_strdup(new_font_name);
+ }
+
+ if (colors_changed)
+ fetch_colors();
+}
+
+void
+gui_prefs_apply(GtkWidget *w _U_)
+{
+ PangoFontDescription *new_r_font, *new_b_font;
+ PangoFontDescription *old_r_font = NULL, *old_b_font = NULL;
+
+ if (font_changed) {
+ /* XXX - what if the world changed out from under
+ us, so that one or both of these fonts cannot
+ be loaded? */
+ new_r_font = pango_font_description_from_string(prefs.gui_font_name);
+ new_b_font = pango_font_description_copy(new_r_font);
+ pango_font_description_set_weight(new_b_font,
+ PANGO_WEIGHT_BOLD);
+ set_plist_font(new_r_font);
+ set_ptree_font_all(new_r_font);
+ old_r_font = m_r_font;
+ old_b_font = m_b_font;
+ set_fonts(new_r_font, new_b_font);
+ }
+
+ /* Redraw the hex dump windows, in case either the font or the
+ highlight style changed. */
+ redraw_hex_dump_all();
+
+ /* Redraw the help window. */
+ help_redraw();
+
+ /* Redraw the "Follow TCP Stream" windows, in case either the font
+ or the colors to use changed. */
+ follow_redraw_all();
+
+ set_scrollbar_placement_all();
+ set_plist_sel_browse(prefs.gui_plist_sel_browse);
+ set_ptree_sel_browse_all(prefs.gui_ptree_sel_browse);
+ if (colors_changed)
+ update_marked_frames();
+
+ /* We're no longer using the old fonts; unreference them. */
+ if (old_r_font != NULL)
+ pango_font_description_free(old_r_font);
+ if (old_b_font != NULL)
+ pango_font_description_free(old_b_font);
+}
+
+void
+gui_prefs_destroy(GtkWidget *w)
+{
+ GtkWidget *caller = gtk_widget_get_toplevel(w);
+ GtkWidget *fs;
+
+ /* Is there a font selection dialog associated with this
+ Preferences dialog? */
+ fs = gtk_object_get_data(GTK_OBJECT(caller), FONT_DIALOG_PTR_KEY);
+
+ if (fs != NULL) {
+ /* Yes. Destroy it. */
+ gtk_widget_destroy(fs);
+ }
+
+ /* Is there a color selection dialog associated with this
+ Preferences dialog? */
+ fs = gtk_object_get_data(GTK_OBJECT(caller), COLOR_DIALOG_PTR_KEY);
+
+ if (fs != NULL) {
+ /* Yes. Destroy it. */
+ gtk_widget_destroy(fs);
+ }
+
+ /* Free up any saved font name. */
+ if (new_font_name != NULL) {
+ g_free(new_font_name);
+ new_font_name = NULL;
+ }
+}
+
+/* color selection part */
+
+#define MAX_HANDLED_COL 2
+
+typedef struct {
+ GdkColor color;
+ char *label;
+} color_info_t;
+
+static color_info_t color_info[MAX_HANDLED_COL] = {
+#define MFG_IDX 0
+ { {0.0, 0.0, 0.0, 0.0}, "Marked frame foreground" },
+#define MBG_IDX 1
+ { {0.0, 0.0, 0.0, 0.0}, "Marked frame background" }
+};
+
+#define SAMPLE_MARKED_TEXT "Sample marked frame text\n"
+
+#define CS_RED 0
+#define CS_GREEN 1
+#define CS_BLUE 2
+#define CS_OPACITY 3
+
+static GdkColor *curcolor = NULL;
+
+static void
+color_browse_cb(GtkWidget *w, gpointer data _U_)
+{
+
+ GtkWidget *main_vb, *main_tb, *label, *optmenu, *menu, *menuitem;
+ GtkWidget *sample, *colorsel, *bbox, *cancel_bt, *ok_bt, *color_w;
+ int i;
+ gdouble scolor[4];
+ GtkWidget *caller = gtk_widget_get_toplevel(w);
+ GtkTextBuffer *buffer;
+ GtkTextIter iter;
+
+ /* Has a color dialog box already been opened for that top-level
+ widget? */
+ color_w = gtk_object_get_data(GTK_OBJECT(caller),
+ COLOR_DIALOG_PTR_KEY);
+
+ if (color_w != NULL) {
+ /* Yes. Just re-activate that dialog box. */
+ reactivate_window(color_w);
+ return;
+ }
+
+ color_t_to_gdkcolor(&color_info[MFG_IDX].color, &prefs.gui_marked_fg);
+ color_t_to_gdkcolor(&color_info[MBG_IDX].color, &prefs.gui_marked_bg);
+ curcolor = &color_info[MFG_IDX].color;
+ scolor[CS_RED] = (gdouble) (curcolor->red) / 65535.0;
+ scolor[CS_GREEN] = (gdouble) (curcolor->green) / 65535.0;
+ scolor[CS_BLUE] = (gdouble) (curcolor->blue) / 65535.0;
+ scolor[CS_OPACITY] = 1.0;
+
+ /* Now create a new dialog.
+ You can't put your own extra widgets into a color selection
+ dialog, as you can with a file selection dialog, so we have to
+ construct our own dialog and put a color selection widget
+ into it. */
+ color_w = dlg_window_new("Ethereal: Select Color");
+
+ g_signal_connect(G_OBJECT(color_w), "delete_event",
+ G_CALLBACK(color_delete_cb), NULL);
+
+ /* Call a handler when we're destroyed, so we can inform our caller,
+ if any, that we've been destroyed. */
+ g_signal_connect(G_OBJECT(color_w), "destroy",
+ G_CALLBACK(color_destroy_cb), NULL);
+
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add (GTK_CONTAINER (color_w), main_vb);
+ main_tb = gtk_table_new(3, 3, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
+ gtk_widget_show(main_tb);
+ label = gtk_label_new("Set:");
+ gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, 0, 1);
+ gtk_widget_show(label);
+
+ colorsel = gtk_color_selection_new();
+ optmenu = gtk_option_menu_new();
+ menu = gtk_menu_new();
+ for (i = 0; i < MAX_HANDLED_COL; i++){
+ menuitem = gtk_menu_item_new_with_label(color_info[i].label);
+ gtk_object_set_data(GTK_OBJECT(menuitem), COLOR_SELECTION_PTR_KEY,
+ (gpointer) colorsel);
+ g_signal_connect(G_OBJECT(menuitem), "activate",
+ G_CALLBACK(update_current_color),
+ &color_info[i].color);
+ gtk_widget_show(menuitem);
+ gtk_menu_append(GTK_MENU (menu), menuitem);
+ }
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (optmenu), menu);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), optmenu, 1, 2, 0, 1);
+ gtk_widget_show(optmenu);
+
+ sample = gtk_text_view_new();
+ buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(sample));
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(sample), FALSE);
+ gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(sample), FALSE);
+ gtk_text_buffer_create_tag(buffer, "color", "foreground-gdk",
+ &color_info[MFG_IDX].color, "background-gdk",
+ &color_info[MBG_IDX].color, NULL);
+ gtk_text_buffer_get_start_iter(buffer, &iter);
+ gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, SAMPLE_MARKED_TEXT,
+ -1, "color", NULL);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), sample, 2, 3, 0, 2);
+ gtk_widget_show(sample);
+ gtk_color_selection_set_color(GTK_COLOR_SELECTION(colorsel),
+ &scolor[CS_RED]);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), colorsel, 0, 3, 2, 3);
+ gtk_object_set_data(GTK_OBJECT(colorsel), COLOR_SAMPLE_PTR_KEY,
+ (gpointer) sample);
+ g_signal_connect(G_OBJECT(colorsel), "color-changed",
+ G_CALLBACK(update_text_color), NULL);
+ gtk_widget_show(colorsel);
+ gtk_widget_show(main_vb);
+
+ gtk_object_set_data(GTK_OBJECT(color_w), COLOR_CALLER_PTR_KEY, caller);
+ gtk_object_set_data(GTK_OBJECT(caller), COLOR_DIALOG_PTR_KEY, color_w);
+
+ /* Ok, Cancel Buttons */
+ 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(main_vb), bbox);
+ gtk_widget_show(bbox);
+
+ ok_bt = gtk_button_new_from_stock(GTK_STOCK_OK);
+ g_signal_connect(G_OBJECT(ok_bt), "clicked",
+ G_CALLBACK(color_ok_cb), color_w);
+ GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start(GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
+ gtk_widget_grab_default(ok_bt);
+ gtk_widget_show(ok_bt);
+ cancel_bt = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+ gtk_signal_connect_object(GTK_OBJECT(cancel_bt), "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT(color_w));
+ gtk_box_pack_start(GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
+ gtk_widget_show(cancel_bt);
+ dlg_set_cancel(color_w, cancel_bt);
+
+ gtk_widget_show(color_w);
+}
+
+static void
+update_text_color(GtkWidget *w, gpointer data _U_) {
+ GtkWidget *sample = gtk_object_get_data(GTK_OBJECT(w),
+ COLOR_SAMPLE_PTR_KEY);
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(sample));
+ GtkTextTag *tag;
+ gdouble scolor[4];
+
+ gtk_color_selection_get_color(GTK_COLOR_SELECTION(w), &scolor[CS_RED]);
+
+ curcolor->red = (gushort) (scolor[CS_RED] * 65535.0);
+ curcolor->green = (gushort) (scolor[CS_GREEN] * 65535.0);
+ curcolor->blue = (gushort) (scolor[CS_BLUE] * 65535.0);
+
+ tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer),
+ "color");
+ g_object_set(tag, "foreground-gdk", &color_info[MFG_IDX].color,
+ "background-gdk", &color_info[MBG_IDX].color, NULL);
+}
+
+static void
+update_current_color(GtkWidget *w, gpointer data)
+{
+ GtkColorSelection *colorsel;
+ gdouble scolor[4];
+
+ colorsel = GTK_COLOR_SELECTION(gtk_object_get_data(GTK_OBJECT(w),
+ COLOR_SELECTION_PTR_KEY));
+ curcolor = (GdkColor *)data;
+ scolor[CS_RED] = (gdouble) (curcolor->red) / 65535.0;
+ scolor[CS_GREEN] = (gdouble) (curcolor->green) / 65535.0;
+ scolor[CS_BLUE] = (gdouble) (curcolor->blue) / 65535.0;
+ scolor[CS_OPACITY] = 1.0;
+
+ gtk_color_selection_set_color(colorsel, &scolor[CS_RED]);
+}
+
+static void
+color_ok_cb(GtkWidget *w _U_, gpointer data)
+{
+ /* We assume the user actually changed a color here. */
+ colors_changed = TRUE;
+
+ gtk_widget_hide(GTK_WIDGET(data));
+ gtk_widget_destroy(GTK_WIDGET(data));
+}
+
+static void
+color_cancel_cb(GtkWidget *w _U_, gpointer data)
+{
+ /* Revert the colors to the current preference settings. */
+ color_t_to_gdkcolor(&color_info[MFG_IDX].color, &prefs.gui_marked_fg);
+ color_t_to_gdkcolor(&color_info[MBG_IDX].color, &prefs.gui_marked_bg);
+ gtk_widget_hide(GTK_WIDGET(data));
+ gtk_widget_destroy(GTK_WIDGET(data));
+}
+
+/* Treat this as a cancel, by calling "color_cancel_cb()".
+ XXX - that'll destroy the Select Color dialog; will that upset
+ a higher-level handler that says "OK, we've been asked to delete
+ this, so destroy it"? */
+static gboolean
+color_delete_cb(GtkWidget *prefs_w _U_, gpointer dummy _U_)
+{
+ color_cancel_cb(NULL, NULL);
+ return FALSE;
+}
+
+static void
+color_destroy_cb(GtkWidget *w, gpointer data _U_)
+{
+ GtkWidget *caller = gtk_object_get_data(GTK_OBJECT(w),
+ COLOR_CALLER_PTR_KEY);
+ if (caller != NULL) {
+ gtk_object_set_data(GTK_OBJECT(caller), COLOR_DIALOG_PTR_KEY, NULL);
+ }
+ gtk_grab_remove(GTK_WIDGET(w));
+ gtk_widget_destroy(GTK_WIDGET(w));
+}
+
+static void
+fetch_colors(void)
+{
+ gdkcolor_to_color_t(&prefs.gui_marked_fg, &color_info[MFG_IDX].color);
+ gdkcolor_to_color_t(&prefs.gui_marked_bg, &color_info[MBG_IDX].color);
+}
diff --git a/gtk2/gui_prefs.h b/gtk2/gui_prefs.h
new file mode 100644
index 0000000000..e31fcb4400
--- /dev/null
+++ b/gtk2/gui_prefs.h
@@ -0,0 +1,34 @@
+/* gui_prefs.h
+ * Definitions for GUI preferences window
+ *
+ * $Id: gui_prefs.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+#ifndef __GUI_PREFS_H__
+#define __GUI_PREFS_H__
+
+GtkWidget *gui_prefs_show(void);
+void gui_prefs_fetch(GtkWidget *w);
+void gui_prefs_apply(GtkWidget *w);
+void gui_prefs_destroy(GtkWidget *w);
+
+#endif
diff --git a/gtk2/help_dlg.c b/gtk2/help_dlg.c
new file mode 100644
index 0000000000..8d348dff35
--- /dev/null
+++ b/gtk2/help_dlg.c
@@ -0,0 +1,397 @@
+/* help_dlg.c
+ *
+ * $Id: help_dlg.c,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Laurent Deniel <deniel@worldnet.fr>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 2000 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef NEED_SNPRINTF_H
+# include "snprintf.h"
+#endif
+
+#include "help_dlg.h"
+#include "prefs.h"
+#include "globals.h"
+#include "gtkglobals.h"
+#include "main.h"
+#include "ui_util.h"
+#include <epan/proto.h>
+
+typedef enum {
+ OVERVIEW_HELP,
+ PROTOCOL_HELP,
+ DFILTER_HELP,
+ CFILTER_HELP
+} help_type_t;
+
+static void help_close_cb(GtkWidget *w, gpointer data);
+static void help_destroy_cb(GtkWidget *w, gpointer data);
+static void insert_text(GtkWidget *w, char *buffer, int nchars);
+static void set_help_text(GtkWidget *w, help_type_t type);
+
+/*
+ * Keep a static pointer to the current "Help" window, if any, so that
+ * if somebody tries to do "Help->Help" while there's already a
+ * "Help" window up, we just pop up the existing one, rather than
+ * creating a new one.
+ */
+static GtkWidget *help_w = NULL;
+
+/*
+ * Keep static pointers to the text widgets as well.
+ */
+GtkWidget *overview_text, *proto_text, *dfilter_text, *cfilter_text;
+
+void help_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+
+ GtkWidget *main_vb, *bbox, *help_nb, *close_bt, *label, *txt_scrollw,
+ *overview_vb,
+ *proto_vb,
+ *dfilter_vb,
+ *cfilter_vb;
+
+ if (help_w != NULL) {
+ /* There's already a "Help" dialog box; reactivate it. */
+ reactivate_window(help_w);
+ return;
+ }
+
+ help_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_name(help_w, "Ethereal Help window" );
+ gtk_window_set_title(GTK_WINDOW(help_w), "Ethereal: Help");
+ g_signal_connect(G_OBJECT(help_w), "destroy",
+ G_CALLBACK(help_destroy_cb), NULL);
+ g_signal_connect(G_OBJECT(help_w), "realize",
+ G_CALLBACK(window_icon_realize_cb), NULL);
+ gtk_widget_set_size_request(GTK_WIDGET(help_w), DEF_WIDTH * 2/3,
+ DEF_HEIGHT * 2/3);
+ gtk_container_border_width(GTK_CONTAINER(help_w), 2);
+
+ /* Container for each row of widgets */
+
+ main_vb = gtk_vbox_new(FALSE, 1);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 1);
+ gtk_container_add(GTK_CONTAINER(help_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* help topics container */
+
+ help_nb = gtk_notebook_new();
+ gtk_container_add(GTK_CONTAINER(main_vb), help_nb);
+
+ /* Overview panel */
+
+ overview_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_border_width(GTK_CONTAINER(overview_vb), 1);
+ txt_scrollw = scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(overview_vb), txt_scrollw, TRUE, TRUE, 0);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ overview_text = gtk_text_view_new();
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(overview_text), FALSE);
+ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(overview_text), GTK_WRAP_WORD);
+ set_help_text(overview_text, OVERVIEW_HELP);
+ gtk_container_add(GTK_CONTAINER(txt_scrollw), overview_text);
+ gtk_widget_show(txt_scrollw);
+ gtk_widget_show(overview_text);
+ gtk_widget_show(overview_vb);
+ label = gtk_label_new("Overview");
+ gtk_notebook_append_page(GTK_NOTEBOOK(help_nb), overview_vb, label);
+
+ /* protocol list */
+
+ proto_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_border_width(GTK_CONTAINER(proto_vb), 1);
+
+ txt_scrollw = scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(proto_vb), txt_scrollw, TRUE, TRUE, 0);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ proto_text = gtk_text_view_new();
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(proto_text), FALSE);
+ set_help_text(proto_text, PROTOCOL_HELP);
+ gtk_container_add(GTK_CONTAINER(txt_scrollw), proto_text);
+ gtk_widget_show(txt_scrollw);
+ gtk_widget_show(proto_text);
+ gtk_widget_show(proto_vb);
+ label = gtk_label_new("Protocols");
+ gtk_notebook_append_page(GTK_NOTEBOOK(help_nb), proto_vb, label);
+
+ /* display filter help */
+ /* X windows have a maximum size of 32767. Since the height can easily
+ exceed this, we have to jump through some hoops to have a functional
+ vertical scroll bar. */
+
+ dfilter_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_border_width(GTK_CONTAINER(dfilter_vb), 1);
+
+ txt_scrollw = scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(dfilter_vb), txt_scrollw, TRUE, TRUE, 0);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ dfilter_text = gtk_text_view_new();
+ if (prefs.gui_scrollbar_on_right) {
+ gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_CORNER_TOP_LEFT);
+ }
+ else {
+ gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_CORNER_TOP_RIGHT);
+ }
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(dfilter_text), FALSE);
+ set_help_text(dfilter_text, DFILTER_HELP);
+ gtk_container_add(GTK_CONTAINER(txt_scrollw), dfilter_text);
+ gtk_widget_show(txt_scrollw);
+ gtk_widget_show(dfilter_text);
+ gtk_widget_show(dfilter_vb);
+ label = gtk_label_new("Display Filters");
+ gtk_notebook_append_page(GTK_NOTEBOOK(help_nb), dfilter_vb, label);
+
+ /* capture filter help (this one has no horizontal scrollbar) */
+
+ cfilter_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_border_width(GTK_CONTAINER(cfilter_vb), 1);
+ txt_scrollw = scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(cfilter_vb), txt_scrollw, TRUE, TRUE, 0);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+ cfilter_text = gtk_text_view_new();
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(cfilter_text), FALSE);
+ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(cfilter_text), GTK_WRAP_WORD);
+ set_help_text(cfilter_text, CFILTER_HELP);
+ gtk_container_add(GTK_CONTAINER(txt_scrollw), cfilter_text);
+ gtk_widget_show(txt_scrollw);
+ gtk_widget_show(cfilter_text);
+ gtk_widget_show(cfilter_vb);
+ label = gtk_label_new("Capture Filters");
+ gtk_notebook_append_page(GTK_NOTEBOOK(help_nb), cfilter_vb, label);
+
+ /* XXX add other help panels here ... */
+
+ gtk_widget_show(help_nb);
+
+ /* Buttons (only one for now) */
+
+ bbox = gtk_hbox_new(FALSE, 1);
+ gtk_box_pack_end(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+ close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ g_signal_connect(G_OBJECT(close_bt), "clicked",
+ G_CALLBACK(help_close_cb), GTK_OBJECT(help_w));
+ GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
+ gtk_container_add(GTK_CONTAINER(bbox), close_bt);
+ gtk_widget_grab_default(close_bt);
+ gtk_widget_show(close_bt);
+
+ gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(help_w));
+ gtk_widget_show(help_w);
+
+} /* help_cb */
+
+static void help_close_cb(GtkWidget *w _U_, gpointer data)
+{
+ gtk_widget_destroy(GTK_WIDGET(data));
+}
+
+static void help_destroy_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ /* Note that we no longer have a Help window. */
+ help_w = NULL;
+}
+
+static void insert_text(GtkWidget *w, char *buffer, int nchars)
+{
+ GtkTextBuffer *buf= gtk_text_view_get_buffer(GTK_TEXT_VIEW(w));
+ GtkTextIter iter;
+
+ gtk_text_buffer_get_end_iter(buf, &iter);
+ gtk_widget_modify_font(w, m_r_font);
+ if (!g_utf8_validate(buffer, -1, NULL))
+ printf(buffer);
+ gtk_text_buffer_insert(buf, &iter, buffer, nchars);
+}
+
+static char *proto_help =
+"The protocols (and packet types) currently supported by\n"
+"Ethereal are the following:\n\n";
+
+static char *dfilter_help =
+"The following list shows all per-protocol fields that\n"
+"can be used in a display filter:\n";
+
+static char *cfilter_help =
+"Packet capturing is performed with the pcap library. The capture filter "
+"syntax follows the rules of this library.\nSo this syntax is different "
+"from the display filter syntax: see manual page of tcpdump.\n"
+#ifndef HAVE_LIBPCAP
+"\nNote: packet capturing is not enabled in this version.\n";
+#else
+;
+#endif
+
+static char *overview_help =
+"Ethereal is a GUI network protocol analyzer. It lets you interactively "
+"browse packet data from a live network or from a previously saved capture "
+"file.\n\nEthereal knows how to read libpcap capture files, including those "
+"of tcpdump. In addition, Ethereal can read capture files from snoop "
+"(including Shomiti) and atmsnoop, LanAlyzer, Sniffer (compressed or "
+"uncompressed), Microsoft Network Monitor, AIX's iptrace, NetXray, "
+"Sniffer Pro, RADCOM's WAN/LAN analyzer, Lucent/Ascend router debug output, "
+"HP-UX's nettl, the dump output from Toshiba's ISDN routers, the output from "
+"i4btrace from the ISDN4BSD project, and output in IPLog format from the "
+"Cisco Secure Intrusion Detection System.\n\n"
+"There is no need to tell Ethereal what type of file you are reading; it will "
+"determine the file type by itself. Ethereal is also capable of reading any "
+"of these file formats if they are compressed using gzip. Ethereal recognizes "
+"this directly from the file; the '.gz' extension is not required for this "
+"purpose.";
+
+static void set_help_text(GtkWidget *w, help_type_t type)
+{
+
+#define BUFF_LEN 4096
+#define B_LEN 256
+ char buffer[BUFF_LEN];
+ header_field_info *hfinfo;
+ int i, len, maxlen = 0, maxlen2 = 0;
+ const char *type_name;
+ char blanks[B_LEN];
+ int blks;
+ void *cookie;
+
+ memset(blanks, ' ', B_LEN - 1);
+ blanks[B_LEN-1] = '\0';
+
+ switch(type) {
+
+ case OVERVIEW_HELP :
+ insert_text(w, overview_help, -1);
+ break;
+
+ case PROTOCOL_HELP :
+ /* first pass to know the maximum length of first field */
+ for (i = proto_get_first_protocol(&cookie); i != -1;
+ i = proto_get_next_protocol(&cookie)) {
+ hfinfo = proto_registrar_get_nth(i);
+ if ((len = strlen(hfinfo->abbrev)) > maxlen)
+ maxlen = len;
+ }
+ maxlen++;
+
+ insert_text(w, proto_help, strlen(proto_help));
+
+ /* ok, display the correctly aligned strings */
+ for (i = proto_get_first_protocol(&cookie); i != -1;
+ i = proto_get_next_protocol(&cookie)) {
+ hfinfo = proto_registrar_get_nth(i);
+ blks = maxlen - strlen(hfinfo->abbrev);
+ snprintf(buffer, BUFF_LEN, "%s%s%s\n",
+ hfinfo->abbrev,
+ &blanks[B_LEN - blks - 2],
+ hfinfo->name);
+ insert_text(w, buffer, strlen(buffer));
+ }
+ break;
+
+ case DFILTER_HELP :
+
+ /* XXX we should display hinfo->blurb instead of name (if not empty) */
+
+ /* first pass to know the maximum length of first and second fields */
+ for (i = 0; i < proto_registrar_n() ; i++) {
+ if (!proto_registrar_is_protocol(i)) {
+ hfinfo = proto_registrar_get_nth(i);
+ if ((len = strlen(hfinfo->abbrev)) > maxlen)
+ maxlen = len;
+ if ((len = strlen(hfinfo->name)) > maxlen2)
+ maxlen2 = len;
+ }
+ }
+ maxlen++;
+ maxlen2++;
+
+ insert_text(w, dfilter_help, strlen(dfilter_help));
+
+ for (i = 0; i < proto_registrar_n() ; i++) {
+ hfinfo = proto_registrar_get_nth(i);
+ if (proto_registrar_is_protocol(i)) {
+ snprintf(buffer, BUFF_LEN, "\n%s:\n", hfinfo->name);
+ insert_text(w, buffer, strlen(buffer));
+ } else {
+
+ type_name = ftype_pretty_name(hfinfo->type);
+ snprintf(buffer, BUFF_LEN, "%s%s%s%s(%s)\n",
+ hfinfo->abbrev,
+ &blanks[B_LEN - (maxlen - strlen(hfinfo->abbrev)) - 2],
+ hfinfo->name,
+ &blanks[B_LEN - (maxlen2 - strlen(hfinfo->name)) - 2],
+ type_name);
+ insert_text(w, buffer, strlen(buffer));
+ }
+ }
+ break;
+ case CFILTER_HELP :
+ insert_text(w, cfilter_help, -1);
+ break;
+ default :
+ g_assert_not_reached();
+ break;
+ } /* switch(type) */
+} /* set_help_text */
+
+static void clear_help_text(GtkWidget *w)
+{
+ GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(w));
+
+ gtk_text_buffer_set_text(buf, "", 0);
+}
+
+/* Redraw all the text widgets, to use a new font. */
+void help_redraw(void)
+{
+ if (help_w != NULL) {
+ clear_help_text(overview_text);
+ set_help_text(overview_text, OVERVIEW_HELP);
+
+ clear_help_text(proto_text);
+ set_help_text(proto_text, PROTOCOL_HELP);
+
+ clear_help_text(dfilter_text);
+ set_help_text(dfilter_text, DFILTER_HELP);
+
+ clear_help_text(cfilter_text);
+ set_help_text(cfilter_text, CFILTER_HELP);
+ }
+}
diff --git a/gtk2/help_dlg.h b/gtk2/help_dlg.h
new file mode 100644
index 0000000000..f576b261e2
--- /dev/null
+++ b/gtk2/help_dlg.h
@@ -0,0 +1,35 @@
+/* help_dlg.h
+ *
+ * $Id: help_dlg.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Laurent Deniel <deniel@worldnet.fr>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.org>
+ * Copyright 2000 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.
+ *
+ */
+
+#ifndef __HELP_DLG_H__
+#define __HELP_DLG_H__
+
+void help_cb(GtkWidget *, gpointer);
+
+/* Redraw all the text widgets, to use a new font. */
+void help_redraw(void);
+
+#endif
diff --git a/gtk2/keys.h b/gtk2/keys.h
new file mode 100644
index 0000000000..73802a7fc5
--- /dev/null
+++ b/gtk2/keys.h
@@ -0,0 +1,48 @@
+/* keys.h
+ * Key definitions for various objects
+ *
+ * $Id: keys.h,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifndef __KEYS_H__
+#define __KEYS_H__
+
+/* Keys for gtk_object_set_data */
+
+#define E_DFILTER_TE_KEY "display_filter_entry"
+#define E_RFILTER_TE_KEY "read_filter_te"
+#define E_MPACKET_LIST_KEY "menu_packet_list"
+#define E_MPACKET_LIST_ROW_KEY "menu_packet_list_row"
+#define E_MPACKET_LIST_COL_KEY "menu_packet_list_col"
+
+#define PRINT_CMD_LB_KEY "printer_command_label"
+#define PRINT_CMD_TE_KEY "printer_command_entry"
+#define PRINT_FILE_BT_KEY "printer_file_button"
+#define PRINT_FILE_TE_KEY "printer_file_entry"
+
+#define PLUGINS_DFILTER_TE "plugins_dfilter_te"
+
+#define PM_MENU_LIST_KEY "popup_menu_menu_list"
+#define PM_PACKET_LIST_KEY "popup_menu_packet_list"
+#define PM_TREE_VIEW_KEY "popup_menu_tree_view"
+#define PM_HEXDUMP_KEY "popup_menu_hexdump"
+
+#endif
diff --git a/gtk2/main.c b/gtk2/main.c
new file mode 100644
index 0000000000..1b64ba7deb
--- /dev/null
+++ b/gtk2/main.c
@@ -0,0 +1,2467 @@
+/* main.c
+ *
+ * $Id: main.c,v 1.1 2002/08/31 09:55:21 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ *
+ * Richard Sharpe, 13-Feb-1999, added support for initializing structures
+ * needed by dissect routines
+ * Jeff Foster, 2001/03/12, added support tabbed hex display windowss
+ *
+ *
+ * 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.
+ *
+ *
+ * To do:
+ * - Graphs
+ * - Playback window
+ * - Multiple window support
+ * - Add cut/copy/paste
+ * - Create header parsing routines
+ * - Make byte view selections more fancy?
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <errno.h>
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_IO_H
+#include <io.h> /* open/close on win32 */
+#endif
+
+#ifdef HAVE_DIRECT_H
+#include <direct.h>
+#endif
+
+#include <signal.h>
+
+#ifdef HAVE_LIBPCAP
+#include <pcap.h>
+#endif
+
+#ifdef HAVE_LIBZ
+#include <zlib.h> /* to get the libz version number */
+#endif
+
+#ifdef NEED_SNPRINTF_H
+# include "snprintf.h"
+#endif
+
+#ifdef HAVE_UCD_SNMP_VERSION_H
+#include <ucd-snmp/version.h>
+#endif /* HAVE_UCD_SNMP_VERSION_H */
+
+#ifdef NEED_STRERROR_H
+#include "strerror.h"
+#endif
+
+#ifdef NEED_GETOPT_H
+#include "getopt.h"
+#endif
+
+#ifdef WIN32 /* Needed for console I/O */
+#include <fcntl.h>
+#include <conio.h>
+#endif
+
+#include <epan/epan.h>
+#include <epan/filesystem.h>
+#include <epan/epan_dissect.h>
+
+#include "main.h"
+#include <epan/timestamp.h>
+#include <epan/packet.h>
+#include "capture.h"
+#include "summary.h"
+#include "file.h"
+#include "filters.h"
+#include "prefs.h"
+#include "menu.h"
+#include "../menu.h"
+#include "color.h"
+#include "color_utils.h"
+#include "filter_prefs.h"
+#include "file_dlg.h"
+#include "column.h"
+#include "print.h"
+#include <epan/resolv.h>
+#ifdef HAVE_LIBPCAP
+#include "pcap-util.h"
+#endif
+#include "statusbar.h"
+#include "simple_dialog.h"
+#include "proto_draw.h"
+#include <epan/dfilter/dfilter.h>
+#include "keys.h"
+#include "packet_win.h"
+#include "gtkglobals.h"
+#include <epan/plugins.h>
+#include "colors.h"
+#include <epan/strutil.h>
+#include "register.h"
+#include "ringbuffer.h"
+#include "ui_util.h"
+#include "image/clist_ascend.xpm"
+#include "image/clist_descend.xpm"
+
+#ifdef WIN32
+#include "capture-wpcap.h"
+#endif
+
+typedef struct column_arrows {
+ GtkWidget *table;
+ GtkWidget *ascend_pm;
+ GtkWidget *descend_pm;
+} column_arrows;
+
+capture_file cfile;
+GtkWidget *top_level, *packet_list, *tree_view, *byte_nb_ptr,
+ *tv_scrollw, *pkt_scrollw;
+static GtkWidget *info_bar;
+PangoFontDescription *m_r_font, *m_b_font;
+static guint main_ctx, file_ctx, help_ctx;
+static GString *comp_info_str;
+gchar *ethereal_path = NULL;
+gchar *last_open_dir = NULL;
+gint root_x = G_MAXINT, root_y = G_MAXINT, top_width, top_height;
+
+ts_type timestamp_type = RELATIVE;
+
+/* Specifies the field currently selected in the GUI protocol tree */
+field_info *finfo_selected = NULL;
+
+#ifdef WIN32
+static gboolean has_no_console; /* TRUE if app has no console */
+static gboolean console_was_created; /* TRUE if console was created */
+static void create_console(void);
+static void destroy_console(void);
+static void console_log_handler(const char *log_domain,
+ GLogLevelFlags log_level, const char *message, gpointer user_data);
+#endif
+
+static void create_main_window(gint, gint, gint, e_prefs*);
+
+#define E_DFILTER_CM_KEY "display_filter_combo"
+#define E_DFILTER_FL_KEY "display_filter_list"
+
+/* About Ethereal window */
+void
+about_ethereal( GtkWidget *w _U_, gpointer data _U_ ) {
+ simple_dialog(ESD_TYPE_INFO, NULL,
+ "Ethereal - Network Protocol Analyzer\n"
+ "Version " VERSION " (C) 1998-2002 Gerald Combs <gerald@ethereal.com>\n"
+ "Compiled %s\n\n"
+
+ "Check the man page for complete documentation and\n"
+ "for the list of contributors.\n"
+
+ "\nSee http://www.ethereal.com/ for more information.",
+ comp_info_str->str);
+}
+
+void
+set_fonts(PangoFontDescription *regular, PangoFontDescription *bold)
+{
+ /* Yes, assert. The code that loads the font should check
+ * for NULL and provide its own error message. */
+ g_assert(m_r_font && m_b_font);
+ m_r_font = regular;
+ m_b_font = bold;
+}
+
+
+/* Match selected byte pattern */
+static void
+match_selected_cb_do(gpointer data, int action, gchar *text)
+{
+ GtkWidget *filter_te;
+ char *cur_filter, *new_filter;
+
+ if (!text)
+ return;
+ g_assert(data);
+ filter_te = gtk_object_get_data(GTK_OBJECT(data), E_DFILTER_TE_KEY);
+ g_assert(filter_te);
+
+ cur_filter = gtk_editable_get_chars(GTK_EDITABLE(filter_te), 0, -1);
+
+ switch (action&MATCH_SELECTED_MASK) {
+
+ case MATCH_SELECTED_REPLACE:
+ new_filter = g_strdup(text);
+ break;
+
+ case MATCH_SELECTED_AND:
+ if ((!cur_filter) || (0 == strlen(cur_filter)))
+ new_filter = g_strdup(text);
+ else
+ new_filter = g_strconcat("(", cur_filter, ") && (", text, ")", NULL);
+ break;
+
+ case MATCH_SELECTED_OR:
+ if ((!cur_filter) || (0 == strlen(cur_filter)))
+ new_filter = g_strdup(text);
+ else
+ new_filter = g_strconcat("(", cur_filter, ") || (", text, ")", NULL);
+ break;
+
+ case MATCH_SELECTED_NOT:
+ new_filter = g_strconcat("!(", text, ")", NULL);
+ break;
+
+ case MATCH_SELECTED_AND_NOT:
+ if ((!cur_filter) || (0 == strlen(cur_filter)))
+ new_filter = g_strconcat("!(", text, ")", NULL);
+ else
+ new_filter = g_strconcat("(", cur_filter, ") && !(", text, ")", NULL);
+ break;
+
+ case MATCH_SELECTED_OR_NOT:
+ if ((!cur_filter) || (0 == strlen(cur_filter)))
+ new_filter = g_strconcat("!(", text, ")", NULL);
+ else
+ new_filter = g_strconcat("(", cur_filter, ") || !(", text, ")", NULL);
+ break;
+
+ default:
+ g_assert_not_reached();
+ new_filter = NULL;
+ break;
+ }
+
+ /* Free up the copy we got of the old filter text. */
+ g_free(cur_filter);
+
+ /* create a new one and set the display filter entry accordingly */
+ gtk_entry_set_text(GTK_ENTRY(filter_te), new_filter);
+
+ /* Run the display filter so it goes in effect. */
+ if (action&MATCH_SELECTED_APPLY_NOW)
+ filter_packets(&cfile, new_filter);
+
+ /* Free up the new filter text. */
+ g_free(new_filter);
+
+ /* Free up the generated text we were handed. */
+ g_free(text);
+}
+
+void
+match_selected_cb_replace_ptree(GtkWidget *w, gpointer data)
+{
+ if (finfo_selected)
+ match_selected_cb_do((data ? data : w),
+ MATCH_SELECTED_REPLACE|MATCH_SELECTED_APPLY_NOW,
+ proto_alloc_dfilter_string(finfo_selected,
+ cfile.pd));
+}
+
+ void
+match_selected_cb_and_ptree(GtkWidget *w, gpointer data)
+{
+ if (finfo_selected)
+ match_selected_cb_do((data ? data : w),
+ MATCH_SELECTED_AND|MATCH_SELECTED_APPLY_NOW,
+ proto_alloc_dfilter_string(finfo_selected,
+ cfile.pd));
+}
+
+ void
+match_selected_cb_or_ptree(GtkWidget *w, gpointer data)
+{
+ if (finfo_selected)
+ match_selected_cb_do((data ? data : w),
+ MATCH_SELECTED_OR|MATCH_SELECTED_APPLY_NOW,
+ proto_alloc_dfilter_string(finfo_selected,
+ cfile.pd));
+}
+
+ void
+match_selected_cb_not_ptree(GtkWidget *w, gpointer data)
+{
+ if (finfo_selected)
+ match_selected_cb_do((data ? data : w),
+ MATCH_SELECTED_NOT|MATCH_SELECTED_APPLY_NOW,
+ proto_alloc_dfilter_string(finfo_selected,
+ cfile.pd));
+}
+
+ void
+match_selected_cb_and_ptree_not(GtkWidget *w, gpointer data)
+{
+ if (finfo_selected)
+ match_selected_cb_do((data ? data : w),
+ MATCH_SELECTED_AND_NOT|MATCH_SELECTED_APPLY_NOW,
+ proto_alloc_dfilter_string(finfo_selected,
+ cfile.pd));
+}
+
+ void
+match_selected_cb_or_ptree_not(GtkWidget *w, gpointer data)
+{
+ if (finfo_selected)
+ match_selected_cb_do((data ? data : w),
+ MATCH_SELECTED_OR_NOT,
+ proto_alloc_dfilter_string(finfo_selected,
+ cfile.pd));
+}
+
+ void
+prepare_selected_cb_replace_ptree(GtkWidget *w, gpointer data)
+{
+ if (finfo_selected)
+ match_selected_cb_do((data ? data : w),
+ MATCH_SELECTED_REPLACE,
+ proto_alloc_dfilter_string(finfo_selected,
+ cfile.pd));
+}
+
+ void
+prepare_selected_cb_and_ptree(GtkWidget *w, gpointer data)
+{
+ if (finfo_selected)
+ match_selected_cb_do((data ? data : w),
+ MATCH_SELECTED_AND,
+ proto_alloc_dfilter_string(finfo_selected,
+ cfile.pd));
+}
+
+ void
+prepare_selected_cb_or_ptree(GtkWidget *w, gpointer data)
+{
+ if (finfo_selected)
+ match_selected_cb_do((data ? data : w),
+ MATCH_SELECTED_OR,
+ proto_alloc_dfilter_string(finfo_selected,
+ cfile.pd));
+}
+
+ void
+prepare_selected_cb_not_ptree(GtkWidget *w, gpointer data)
+{
+ if (finfo_selected)
+ match_selected_cb_do((data ? data : w),
+ MATCH_SELECTED_NOT,
+ proto_alloc_dfilter_string(finfo_selected,
+ cfile.pd));
+}
+
+ void
+prepare_selected_cb_and_ptree_not(GtkWidget *w, gpointer data)
+{
+ if (finfo_selected)
+ match_selected_cb_do((data ? data : w),
+ MATCH_SELECTED_AND_NOT,
+ proto_alloc_dfilter_string(finfo_selected,
+ cfile.pd));
+}
+
+ void
+prepare_selected_cb_or_ptree_not(GtkWidget *w, gpointer data)
+{
+ if (finfo_selected)
+ match_selected_cb_do((data ? data : w),
+ MATCH_SELECTED_OR_NOT,
+ proto_alloc_dfilter_string(finfo_selected,
+ cfile.pd));
+}
+
+static gchar *
+get_text_from_packet_list(gpointer data)
+{
+ gint row = (gint)gtk_object_get_data(GTK_OBJECT(data),
+ E_MPACKET_LIST_ROW_KEY);
+ gint column = (gint)gtk_object_get_data(GTK_OBJECT(data),
+ E_MPACKET_LIST_COL_KEY);
+ frame_data *fdata = (frame_data *)gtk_clist_get_row_data(GTK_CLIST(packet_list), row);
+ epan_dissect_t *edt;
+ gchar *buf=NULL;
+ int len;
+ int err;
+
+ if (fdata != NULL) {
+ /* XXX - do something with "err" */
+ wtap_seek_read(cfile.wth, fdata->file_off, &cfile.pseudo_header,
+ cfile.pd, fdata->cap_len, &err);
+
+ edt = epan_dissect_new(FALSE, FALSE);
+ epan_dissect_run(edt, &cfile.pseudo_header, cfile.pd, fdata,
+ &cfile.cinfo);
+ epan_dissect_fill_in_columns(edt);
+
+ if (strlen(cfile.cinfo.col_expr[column]) != 0 &&
+ strlen(cfile.cinfo.col_expr_val[column]) != 0) {
+ len = strlen(cfile.cinfo.col_expr[column]) +
+ strlen(cfile.cinfo.col_expr_val[column]) + 5;
+ buf = g_malloc0(len);
+ snprintf(buf, len, "%s == %s", cfile.cinfo.col_expr[column],
+ cfile.cinfo.col_expr_val[column]);
+ }
+
+ epan_dissect_free(edt);
+ }
+
+ return buf;
+}
+
+ void
+match_selected_cb_replace_plist(GtkWidget *w _U_, gpointer data)
+{
+ match_selected_cb_do(data,
+ MATCH_SELECTED_REPLACE|MATCH_SELECTED_APPLY_NOW,
+ get_text_from_packet_list(data));
+}
+
+ void
+match_selected_cb_and_plist(GtkWidget *w _U_, gpointer data)
+{
+ match_selected_cb_do(data,
+ MATCH_SELECTED_AND|MATCH_SELECTED_APPLY_NOW,
+ get_text_from_packet_list(data));
+}
+
+ void
+match_selected_cb_or_plist(GtkWidget *w _U_, gpointer data)
+{
+ match_selected_cb_do(data,
+ MATCH_SELECTED_OR|MATCH_SELECTED_APPLY_NOW,
+ get_text_from_packet_list(data));
+}
+
+ void
+match_selected_cb_not_plist(GtkWidget *w _U_, gpointer data)
+{
+ match_selected_cb_do(data,
+ MATCH_SELECTED_NOT|MATCH_SELECTED_APPLY_NOW,
+ get_text_from_packet_list(data));
+}
+
+ void
+match_selected_cb_and_plist_not(GtkWidget *w _U_, gpointer data)
+{
+ match_selected_cb_do(data,
+ MATCH_SELECTED_AND_NOT|MATCH_SELECTED_APPLY_NOW,
+ get_text_from_packet_list(data));
+}
+
+ void
+match_selected_cb_or_plist_not(GtkWidget *w _U_, gpointer data)
+{
+ match_selected_cb_do(data,
+ MATCH_SELECTED_OR_NOT|MATCH_SELECTED_APPLY_NOW,
+ get_text_from_packet_list(data));
+}
+
+ void
+prepare_selected_cb_replace_plist(GtkWidget *w _U_, gpointer data)
+{
+ match_selected_cb_do(data,
+ MATCH_SELECTED_REPLACE,
+ get_text_from_packet_list(data));
+}
+
+ void
+prepare_selected_cb_and_plist(GtkWidget *w _U_, gpointer data)
+{
+ match_selected_cb_do(data,
+ MATCH_SELECTED_AND,
+ get_text_from_packet_list(data));
+}
+
+ void
+prepare_selected_cb_or_plist(GtkWidget *w _U_, gpointer data)
+{
+ match_selected_cb_do(data,
+ MATCH_SELECTED_OR,
+ get_text_from_packet_list(data));
+}
+
+void
+prepare_selected_cb_not_plist(GtkWidget *w _U_, gpointer data)
+{
+ match_selected_cb_do(data,
+ MATCH_SELECTED_NOT,
+ get_text_from_packet_list(data));
+}
+
+void
+prepare_selected_cb_and_plist_not(GtkWidget *w _U_, gpointer data)
+{
+ match_selected_cb_do(data,
+ MATCH_SELECTED_AND_NOT,
+ get_text_from_packet_list(data));
+}
+
+void
+prepare_selected_cb_or_plist_not(GtkWidget *w _U_, gpointer data)
+{
+ match_selected_cb_do(data,
+ MATCH_SELECTED_OR_NOT,
+ get_text_from_packet_list(data));
+}
+
+/* Run the current display filter on the current packet set, and
+ redisplay. */
+static void
+filter_activate_cb(GtkWidget *w, gpointer data)
+{
+ GtkCombo *filter_cm = gtk_object_get_data(GTK_OBJECT(w),
+ E_DFILTER_CM_KEY);
+ GList *filter_list = gtk_object_get_data(GTK_OBJECT(filter_cm),
+ E_DFILTER_FL_KEY);
+ GList *li;
+ gboolean add_filter = TRUE;
+ gboolean free_filter = TRUE;
+ char *s;
+
+ g_assert(data);
+ s = g_strdup(gtk_entry_get_text(GTK_ENTRY(data)));
+
+ /* GtkCombos don't let us get at their list contents easily, so we maintain
+ our own filter list, and feed it to gtk_combo_set_popdown_strings when
+ a new filter is added. */
+ if (filter_packets(&cfile, s)) {
+ li = g_list_first(filter_list);
+ while (li) {
+ if (li->data && strcmp(s, li->data) == 0)
+ add_filter = FALSE;
+ li = li->next;
+ }
+
+ if (add_filter) {
+ free_filter = FALSE;
+ filter_list = g_list_append(filter_list, s);
+ gtk_object_set_data(GTK_OBJECT(filter_cm), E_DFILTER_FL_KEY, filter_list);
+ gtk_combo_set_popdown_strings(filter_cm, filter_list);
+ gtk_entry_set_text(GTK_ENTRY(filter_cm->entry),
+ g_list_last(filter_list)->data);
+ }
+ }
+ if (free_filter)
+ g_free(s);
+}
+
+/* redisplay with no display filter */
+static void
+filter_reset_cb(GtkWidget *w, gpointer data _U_)
+{
+ GtkWidget *filter_te = NULL;
+
+ if ((filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY))) {
+ gtk_entry_set_text(GTK_ENTRY(filter_te), "");
+ }
+ filter_packets(&cfile, NULL);
+}
+
+/* GTKClist compare routine, overrides default to allow numeric comparison */
+static gint
+packet_list_compare(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
+{
+ /* Get row text strings */
+ char *text1 = GTK_CELL_TEXT (((GtkCListRow *)ptr1)->cell[clist->sort_column])->text;
+ char *text2 = GTK_CELL_TEXT (((GtkCListRow *)ptr2)->cell[clist->sort_column])->text;
+
+ /* Attempt to convert to numbers */
+ double num1 = atof(text1);
+ double num2 = atof(text2);
+
+ gint col_fmt = cfile.cinfo.col_fmt[clist->sort_column];
+
+ if ((col_fmt == COL_NUMBER) || (col_fmt == COL_REL_TIME) ||
+ (col_fmt == COL_DELTA_TIME) ||
+ ((col_fmt == COL_CLS_TIME) && (timestamp_type == RELATIVE)) ||
+ ((col_fmt == COL_CLS_TIME) && (timestamp_type == DELTA)) ||
+ (col_fmt == COL_UNRES_SRC_PORT) || (col_fmt == COL_UNRES_DST_PORT) ||
+ ((num1 != 0) && (num2 != 0) && ((col_fmt == COL_DEF_SRC_PORT) ||
+ (col_fmt == COL_RES_SRC_PORT) ||
+ (col_fmt == COL_DEF_DST_PORT) ||
+ (col_fmt == COL_RES_DST_PORT))) ||
+ (col_fmt == COL_PACKET_LENGTH)) {
+
+ /* Compare numeric column */
+
+ if (num1 < num2)
+ return -1;
+ else if (num1 > num2)
+ return 1;
+ else
+ return 0;
+ }
+
+ else {
+
+ /* Compare text column */
+ if (!text2)
+ return (text1 != NULL);
+
+ if (!text1)
+ return -1;
+
+ return strcmp(text1, text2);
+ }
+}
+
+/* What to do when a column is clicked */
+static void
+packet_list_click_column_cb(GtkCList *clist, gint column, gpointer data)
+{
+ column_arrows *col_arrows = (column_arrows *) data;
+ int i;
+
+ gtk_clist_freeze(clist);
+
+ for (i = 0; i < cfile.cinfo.num_cols; i++) {
+ gtk_widget_hide(col_arrows[i].ascend_pm);
+ gtk_widget_hide(col_arrows[i].descend_pm);
+ }
+
+ if (column == clist->sort_column) {
+ if (clist->sort_type == GTK_SORT_ASCENDING) {
+ clist->sort_type = GTK_SORT_DESCENDING;
+ gtk_widget_show(col_arrows[column].descend_pm);
+ } else {
+ clist->sort_type = GTK_SORT_ASCENDING;
+ gtk_widget_show(col_arrows[column].ascend_pm);
+ }
+ }
+ else {
+ clist->sort_type = GTK_SORT_ASCENDING;
+ gtk_widget_show(col_arrows[column].ascend_pm);
+ gtk_clist_set_sort_column(clist, column);
+ }
+ gtk_clist_thaw(clist);
+
+ gtk_clist_sort(clist);
+}
+
+/* mark packets */
+static void
+set_frame_mark(gboolean set, frame_data *frame, gint row) {
+ GdkColor fg, bg;
+
+ if (row == -1)
+ return;
+ if (set) {
+ mark_frame(&cfile, frame);
+ color_t_to_gdkcolor(&fg, &prefs.gui_marked_fg);
+ color_t_to_gdkcolor(&bg, &prefs.gui_marked_bg);
+ } else {
+ unmark_frame(&cfile, frame);
+ fg = BLACK;
+ bg = WHITE;
+ }
+ file_set_save_marked_sensitive();
+ gtk_clist_set_background(GTK_CLIST(packet_list), row, &bg);
+ gtk_clist_set_foreground(GTK_CLIST(packet_list), row, &fg);
+}
+
+static gint
+packet_list_button_pressed_cb(GtkWidget *w, GdkEvent *event, gpointer data _U_)
+{
+ GdkEventButton *event_button = (GdkEventButton *)event;
+ gint row, column;
+
+ if (w == NULL || event == NULL)
+ return FALSE;
+
+ if (event->type == GDK_BUTTON_PRESS &&
+ gtk_clist_get_selection_info(GTK_CLIST(w), event_button->x,
+ event_button->y,
+ &row, &column)) {
+ if (event_button->button == 2)
+ {
+ frame_data *fdata = (frame_data *)gtk_clist_get_row_data(GTK_CLIST(w),
+ row);
+ set_frame_mark(!fdata->flags.marked, fdata, row);
+ return TRUE;
+ }
+ else if (event_button->button == 1) {
+ gtk_clist_select_row(GTK_CLIST(w), row, column);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+void mark_frame_cb(GtkWidget *w _U_, gpointer data _U_) {
+ if (cfile.current_frame) {
+ /* XXX hum, should better have a "cfile->current_row" here ... */
+ set_frame_mark(!cfile.current_frame->flags.marked,
+ cfile.current_frame,
+ gtk_clist_find_row_from_data(GTK_CLIST(packet_list),
+ cfile.current_frame));
+ }
+}
+
+static void mark_all_frames(gboolean set) {
+ frame_data *fdata;
+ for (fdata = cfile.plist; fdata != NULL; fdata = fdata->next) {
+ set_frame_mark(set,
+ fdata,
+ gtk_clist_find_row_from_data(GTK_CLIST(packet_list), fdata));
+ }
+}
+
+void update_marked_frames(void) {
+ frame_data *fdata;
+ if (cfile.plist == NULL) return;
+ for (fdata = cfile.plist; fdata != NULL; fdata = fdata->next) {
+ if (fdata->flags.marked)
+ set_frame_mark(TRUE,
+ fdata,
+ gtk_clist_find_row_from_data(GTK_CLIST(packet_list),
+ fdata));
+ }
+}
+
+void mark_all_frames_cb(GtkWidget *w _U_, gpointer data _U_) {
+ mark_all_frames(TRUE);
+}
+
+void unmark_all_frames_cb(GtkWidget *w _U_, gpointer data _U_) {
+ mark_all_frames(FALSE);
+}
+
+/* What to do when a list item is selected/unselected */
+static void
+packet_list_select_cb(GtkWidget *w _U_, gint row, gint col _U_,
+ gpointer evt _U_)
+{
+ /* Remove the hex display tabbed pages */
+ while( (gtk_notebook_get_nth_page( GTK_NOTEBOOK(byte_nb_ptr), 0)))
+ gtk_notebook_remove_page( GTK_NOTEBOOK(byte_nb_ptr), 0);
+
+ select_packet(&cfile, row);
+}
+
+
+static void
+packet_list_unselect_cb(GtkWidget *w _U_, gint row _U_, gint col _U_,
+ gpointer evt _U_) {
+
+ unselect_packet(&cfile);
+}
+
+
+static void
+tree_view_selection_changed_cb(GtkTreeSelection *sel, gpointer user_data _U_)
+{
+ field_info *finfo;
+ gchar *help_str = NULL;
+ gchar len_str[2+10+1+5+1]; /* ", {N} bytes\0",
+ N < 4294967296 */
+ gboolean has_blurb = FALSE;
+ guint length = 0, byte_len;
+ GtkWidget *byte_view;
+ const guint8 *byte_data;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ /* if something is selected */
+ if (gtk_tree_selection_get_selected(sel, &model, &iter))
+ {
+ gtk_tree_model_get(model, &iter, 1, &finfo, -1);
+ if (!finfo) return;
+
+ set_notebook_page(byte_nb_ptr, finfo->ds_tvb);
+
+ byte_view = get_notebook_bv_ptr(byte_nb_ptr);
+ byte_data = get_byte_view_data_and_length(byte_view, &byte_len);
+ g_assert(byte_data != NULL);
+
+ finfo_selected = finfo;
+ set_menus_for_selected_tree_row(TRUE);
+
+ if (finfo->hfinfo) {
+ if (finfo->hfinfo->blurb != NULL &&
+ finfo->hfinfo->blurb[0] != '\0') {
+ has_blurb = TRUE;
+ length = strlen(finfo->hfinfo->blurb);
+ } else {
+ length = strlen(finfo->hfinfo->name);
+ }
+ if (finfo->length == 0) {
+ len_str[0] = '\0';
+ } else if (finfo->length == 1) {
+ strcpy (len_str, ", 1 byte");
+ } else {
+ snprintf (len_str, sizeof len_str, ", %d bytes", finfo->length);
+ }
+
+ statusbar_pop_field_msg(); /* get rid of current help msg */
+ if (length) {
+ length += strlen(finfo->hfinfo->abbrev) + strlen(len_str) + 10;
+ help_str = g_malloc(sizeof(gchar) * length);
+ sprintf(help_str, "%s (%s)%s",
+ (has_blurb) ? finfo->hfinfo->blurb :
+ finfo->hfinfo->name,
+ finfo->hfinfo->abbrev, len_str);
+ statusbar_push_field_msg(help_str);
+ g_free(help_str);
+ } else {
+ /*
+ * Don't show anything if the field name is zero-length;
+ * the pseudo-field for "proto_tree_add_text()" is such
+ * a field, and we don't want "Text (text)" showing up
+ * on the status line if you've selected such a field.
+ *
+ * XXX - there are zero-length fields for which we *do*
+ * want to show the field name.
+ *
+ * XXX - perhaps the name and abbrev field should be null
+ * pointers rather than null strings for that pseudo-field,
+ * but we'd have to add checks for null pointers in some
+ * places if we did that.
+ *
+ * Or perhaps protocol tree items added with
+ * "proto_tree_add_text()" should have -1 as the field index,
+ * with no pseudo-field being used, but that might also
+ * require special checks for -1 to be added.
+ */
+ statusbar_push_field_msg("");
+ }
+ }
+
+ packet_hex_print(GTK_TEXT_VIEW(byte_view), byte_data,
+ cfile.current_frame, finfo, byte_len);
+ }
+ else /* nothing selected */
+ {
+ /*
+ * Which byte view is displaying the current protocol tree
+ * row's data?
+ */
+ byte_view = get_notebook_bv_ptr(byte_nb_ptr);
+ if (byte_view == NULL)
+ return; /* none */
+
+ byte_data = get_byte_view_data_and_length(byte_view, &byte_len);
+ if (byte_data == NULL)
+ return; /* none */
+
+ unselect_field();
+ packet_hex_print(GTK_TEXT_VIEW(byte_view), byte_data,
+ cfile.current_frame, NULL, byte_len);
+ }
+}
+
+void collapse_all_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ if (cfile.edt->tree)
+ collapse_all_tree(cfile.edt->tree, tree_view);
+}
+
+void expand_all_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ if (cfile.edt->tree)
+ expand_all_tree(cfile.edt->tree, tree_view);
+}
+
+void resolve_name_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ if (cfile.edt->tree) {
+ guint32 tmp = g_resolv_flags;
+ g_resolv_flags = RESOLV_ALL;
+ proto_tree_draw(cfile.edt->tree, tree_view);
+ g_resolv_flags = tmp;
+ }
+}
+
+/* Set the selection mode of the packet list window. */
+void
+set_plist_sel_browse(gboolean val)
+{
+ gboolean old_val;
+
+ old_val =
+ (GTK_CLIST(packet_list)->selection_mode == GTK_SELECTION_SINGLE);
+
+ if (val == old_val) {
+ /*
+ * The mode isn't changing, so don't do anything.
+ * In particular, don't gratuitiously unselect the
+ * current packet.
+ *
+ * XXX - why do we have to unselect the current packet
+ * ourselves? The documentation for the GtkCList at
+ *
+ * http://developer.gnome.org/doc/API/gtk/gtkclist.html
+ *
+ * says "Note that setting the widget's selection mode to
+ * one of GTK_SELECTION_BROWSE or GTK_SELECTION_SINGLE will
+ * cause all the items in the GtkCList to become deselected."
+ */
+ return;
+ }
+
+ if (finfo_selected)
+ unselect_packet(&cfile);
+
+ /* Yeah, GTK uses "browse" in the case where we do not, but oh well. I think
+ * "browse" in Ethereal makes more sense than "SINGLE" in GTK+ */
+ if (val) {
+ gtk_clist_set_selection_mode(GTK_CLIST(packet_list),
+ GTK_SELECTION_SINGLE);
+ }
+ else {
+ gtk_clist_set_selection_mode(GTK_CLIST(packet_list),
+ GTK_SELECTION_BROWSE);
+ }
+}
+
+/* Set the font of the packet list window. */
+void
+set_plist_font(PangoFontDescription *font)
+{
+ int i;
+
+ gtk_widget_modify_font(packet_list, font);
+
+ /* Compute static column sizes to use during a "-S" capture, so that
+ the columns don't resize during a live capture. */
+ for (i = 0; i < cfile.cinfo.num_cols; i++) {
+ cfile.cinfo.col_width[i] = gdk_string_width(gdk_font_from_description(font),
+ get_column_longest_string(get_column_format(i)));
+ }
+}
+
+/*
+ * Push a message referring to file access onto the statusbar.
+ */
+void
+statusbar_push_file_msg(gchar *msg)
+{
+ gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, msg);
+}
+
+/*
+ * Pop a message referring to file access off the statusbar.
+ */
+void
+statusbar_pop_file_msg(void)
+{
+ gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx);
+}
+
+/*
+ * XXX - do we need multiple statusbar contexts?
+ */
+
+/*
+ * Push a message referring to the currently-selected field onto the statusbar.
+ */
+void
+statusbar_push_field_msg(gchar *msg)
+{
+ gtk_statusbar_push(GTK_STATUSBAR(info_bar), help_ctx, msg);
+}
+
+/*
+ * Pop a message referring to the currently-selected field off the statusbar.
+ */
+void
+statusbar_pop_field_msg(void)
+{
+ gtk_statusbar_pop(GTK_STATUSBAR(info_bar), help_ctx);
+}
+
+static gboolean
+do_quit(void)
+{
+ /* XXX - should we check whether the capture file is an
+ unsaved temporary file for a live capture and, if so,
+ pop up a "do you want to exit without saving the capture
+ file?" dialog, and then just return, leaving said dialog
+ box to forcibly quit if the user clicks "OK"?
+
+ If so, note that this should be done in a subroutine that
+ returns TRUE if we do so, and FALSE otherwise, and if it
+ returns TRUE we should return TRUE without nuking anything.
+
+ Note that, if we do that, we might also want to check if
+ an "Update list of packets in real time" capture is in
+ progress and, if so, ask whether they want to terminate
+ the capture and discard it, and return TRUE, before nuking
+ any child capture, if they say they don't want to do so. */
+
+#ifdef HAVE_LIBPCAP
+ /* Nuke any child capture in progress. */
+ kill_capture_child();
+#endif
+
+ /* Are we in the middle of reading a capture? */
+ if (cfile.state == FILE_READ_IN_PROGRESS) {
+ /* Yes, so we can't just close the file and quit, as
+ that may yank the rug out from under the read in
+ progress; instead, just set the state to
+ "FILE_READ_ABORTED" and return - the code doing the read
+ will check for that and, if it sees that, will clean
+ up and quit. */
+ cfile.state = FILE_READ_ABORTED;
+
+ /* Say that the window should *not* be deleted;
+ that'll be done by the code that cleans up. */
+ return TRUE;
+ } else {
+ /* Close any capture file we have open; on some OSes, you
+ can't unlink a temporary capture file if you have it
+ open.
+ "close_cap_file()" will unlink it after closing it if
+ it's a temporary file.
+
+ We do this here, rather than after the main loop returns,
+ as, after the main loop returns, the main window may have
+ been destroyed (if this is called due to a "destroy"
+ even on the main window rather than due to the user
+ selecting a menu item), and there may be a crash
+ or other problem when "close_cap_file()" tries to
+ clean up stuff in the main window.
+
+ XXX - is there a better place to put this?
+ Or should we have a routine that *just* closes the
+ capture file, and doesn't do anything with the UI,
+ which we'd call here, and another routine that
+ calls that routine and also cleans up the UI, which
+ we'd call elsewhere? */
+ close_cap_file(&cfile);
+
+ /* Exit by leaving the main loop, so that any quit functions
+ we registered get called. */
+ gtk_main_quit();
+
+ /* Say that the window should be deleted. */
+ return FALSE;
+ }
+}
+
+static gboolean
+main_window_delete_event_cb(GtkWidget *widget _U_, GdkEvent *event _U_,
+ gpointer data _U_)
+{
+ gint desk_x, desk_y;
+
+ /* Try to grab our geometry */
+ gdk_window_get_root_origin(top_level->window, &root_x, &root_y);
+ if (gdk_window_get_deskrelative_origin(top_level->window,
+ &desk_x, &desk_y)) {
+ if (desk_x <= root_x && desk_y <= root_y) {
+ root_x = desk_x;
+ root_y = desk_y;
+ }
+ }
+
+ /* XXX - Is this the "approved" method? */
+ gdk_window_get_size(top_level->window, &top_width, &top_height);
+
+ /* "do_quit()" indicates whether the main window should be deleted. */
+ return do_quit();
+}
+
+void
+file_quit_cmd_cb (GtkWidget *widget _U_, gpointer data _U_)
+{
+ do_quit();
+}
+
+static void
+print_usage(gboolean print_ver) {
+
+ if (print_ver) {
+ fprintf(stderr, "This is GNU " PACKAGE " " VERSION ", compiled %s\n",
+ comp_info_str->str);
+ }
+#ifdef HAVE_LIBPCAP
+ fprintf(stderr, "\n%s [ -vh ] [ -klpQS ] [ -a <capture autostop condition> ] ...\n",
+ PACKAGE);
+ fprintf(stderr, "\t[ -b <number of ringbuffer files> ] [ -B <byte view height> ]\n");
+ fprintf(stderr, "\t[ -c <count> ] [ -f <capture filter> ] [ -i <interface> ]\n");
+ fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -N <resolving> ]\n");
+ fprintf(stderr, "\t[ -o <preference setting> ] ... [ -P <packet list height> ]\n");
+ fprintf(stderr, "\t[ -r <infile> ] [ -R <read filter> ] [ -s <snaplen> ] \n");
+ fprintf(stderr, "\t[ -t <time stamp format> ] [ -T <tree view height> ]\n");
+ fprintf(stderr, "\t[ -w <savefile> ] [ <infile> ]\n");
+#else
+ fprintf(stderr, "\n%s [ -vh ] [ -B <byte view height> ] [ -m <medium font> ]\n",
+ PACKAGE);
+ fprintf(stderr, "\t[ -n ] [ -N <resolving> ]\n");
+ fprintf(stderr, "\t[ -o <preference setting> ... [ -P <packet list height> ]\n");
+ fprintf(stderr, "\t[ -r <infile> ] [ -R <read filter> ] [ -t <time stamp format> ]\n");
+ fprintf(stderr, "\t[ -T <tree view height> ] [ <infile> ]\n");
+#endif
+}
+
+static void
+show_version(void)
+{
+#ifdef WIN32
+ create_console();
+#endif
+
+ printf("%s %s, %s\n", PACKAGE, VERSION, comp_info_str->str);
+}
+
+static int
+get_positive_int(const char *string, const char *name)
+{
+ long number;
+ char *p;
+
+ number = strtol(string, &p, 10);
+ if (p == string || *p != '\0') {
+ fprintf(stderr, "ethereal: The specified %s \"%s\" is not a decimal number\n",
+ name, string);
+ exit(1);
+ }
+ if (number < 0) {
+ fprintf(stderr, "ethereal: The specified %s \"%s\" is a negative number\n",
+ name, string);
+ exit(1);
+ }
+ if (number == 0) {
+ fprintf(stderr, "ethereal: The specified %s \"%s\" is zero\n",
+ name, string);
+ exit(1);
+ }
+ if (number > INT_MAX) {
+ fprintf(stderr, "ethereal: The specified %s \"%s\" is too large (greater than %d)\n",
+ name, string, INT_MAX);
+ exit(1);
+ }
+ return number;
+}
+
+#ifdef HAVE_LIBPCAP
+/*
+ * Given a string of the form "<autostop criterion>:<value>", as might appear
+ * as an argument to a "-a" option, parse it and set the criterion in
+ * question. Return an indication of whether it succeeded or failed
+ * in some fashion.
+ */
+static gboolean
+set_autostop_criterion(const char *autostoparg)
+{
+ guchar *p, *colonp;
+
+ colonp = strchr(autostoparg, ':');
+ if (colonp == NULL)
+ return FALSE;
+
+ p = colonp;
+ *p++ = '\0';
+
+ /*
+ * Skip over any white space (there probably won't be any, but
+ * as we allow it in the preferences file, we might as well
+ * allow it here).
+ */
+ while (isspace(*p))
+ p++;
+ if (*p == '\0') {
+ /*
+ * Put the colon back, so if our caller uses, in an
+ * error message, the string they passed us, the message
+ * looks correct.
+ */
+ *colonp = ':';
+ return FALSE;
+ }
+ if (strcmp(autostoparg,"duration") == 0) {
+ capture_opts.has_autostop_duration = TRUE;
+ capture_opts.autostop_duration = get_positive_int(p,"autostop duration");
+ } else if (strcmp(autostoparg,"filesize") == 0) {
+ capture_opts.has_autostop_filesize = TRUE;
+ capture_opts.autostop_filesize = get_positive_int(p,"autostop filesize");
+ } else {
+ return FALSE;
+ }
+ *colonp = ':'; /* put the colon back */
+ return TRUE;
+}
+#endif
+
+/* And now our feature presentation... [ fade to music ] */
+int
+main(int argc, char *argv[])
+{
+#ifdef HAVE_LIBPCAP
+ char *command_name;
+#endif
+ char *s;
+ int i;
+ int opt;
+ extern char *optarg;
+ gboolean arg_error = FALSE;
+#ifdef HAVE_LIBPCAP
+#ifdef HAVE_PCAP_VERSION
+ extern char pcap_version[];
+#endif /* HAVE_PCAP_VERSION */
+#endif /* HAVE_LIBPCAP */
+
+#ifdef WIN32
+ WSADATA wsaData;
+#endif
+
+ char *gpf_path, *cf_path, *df_path;
+ char *pf_path;
+ int gpf_open_errno, pf_open_errno, cf_open_errno, df_open_errno;
+ int err;
+#ifdef HAVE_LIBPCAP
+ gboolean start_capture = FALSE;
+ gchar *save_file = NULL;
+ GList *if_list;
+ gchar err_str[PCAP_ERRBUF_SIZE];
+ gboolean stats_known;
+ struct pcap_stat stats;
+#else
+ gboolean capture_option_specified = FALSE;
+#endif
+ gint pl_size = 280, tv_size = 95, bv_size = 75;
+ gchar *rc_file, *cf_name = NULL, *rfilter = NULL;
+ dfilter_t *rfcode = NULL;
+ gboolean rfilter_parse_failed = FALSE;
+ e_prefs *prefs;
+ char badopt;
+ gint desk_x, desk_y;
+ gboolean prefs_write_needed = FALSE;
+
+
+#define OPTSTRING_INIT "a:b:B:c:f:hi:klm:nN:o:pP:Qr:R:Ss:t:T:w:v"
+
+#ifdef HAVE_LIBPCAP
+#ifdef WIN32
+#define OPTSTRING_CHILD "W:Z:"
+#else
+#define OPTSTRING_CHILD "W:"
+#endif /* WIN32 */
+#else
+#define OPTSTRING_CHILD ""
+#endif /* HAVE_LIBPCAP */
+
+ char optstring[sizeof(OPTSTRING_INIT) + sizeof(OPTSTRING_CHILD) - 1] =
+ OPTSTRING_INIT;
+
+ ethereal_path = argv[0];
+
+#ifdef WIN32
+ /* Arrange that if we have no console window, and a GLib message logging
+ routine is called to log a message, we pop up a console window.
+
+ We do that by inserting our own handler for all messages logged
+ to the default domain; that handler pops up a console if necessary,
+ and then calls the default handler. */
+ g_log_set_handler(NULL,
+ G_LOG_LEVEL_ERROR|
+ G_LOG_LEVEL_CRITICAL|
+ G_LOG_LEVEL_WARNING|
+ G_LOG_LEVEL_MESSAGE|
+ G_LOG_LEVEL_INFO|
+ G_LOG_LEVEL_DEBUG|
+ G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION,
+ console_log_handler, NULL);
+#endif
+
+#ifdef HAVE_LIBPCAP
+ command_name = get_basename(ethereal_path);
+ /* Set "capture_child" to indicate whether this is going to be a child
+ process for a "-S" capture. */
+ capture_child = (strcmp(command_name, CHILD_NAME) == 0);
+ if (capture_child)
+ strcat(optstring, OPTSTRING_CHILD);
+#endif
+
+ /* Register all dissectors; we must do this before checking for the
+ "-G" flag, as the "-G" flag dumps information registered by the
+ dissectors, and we must do it before we read the preferences, in
+ case any dissectors register preferences. */
+ epan_init(PLUGIN_DIR,register_all_protocols,register_all_protocol_handoffs);
+
+ /* Now register the preferences for any non-dissector modules.
+ We must do that before we read the preferences as well. */
+ prefs_register_modules();
+
+ /* If invoked with the "-G" flag, we dump out information based on
+ the argument to the "-G" flag; if no argument is specified,
+ for backwards compatibility we dump out a glossary of display
+ filter symbols.
+
+ We must do this before calling "gtk_init()", because "gtk_init()"
+ tries to open an X display, and we don't want to have to do any X
+ stuff just to do a build.
+
+ Given that we call "gtk_init()" before doing the regular argument
+ list processing, so that it can handle X and GTK+ arguments and
+ remove them from the list at which we look, this means we must do
+ this before doing the regular argument list processing, as well.
+
+ This means that:
+
+ you must give the "-G" flag as the first flag on the command line;
+
+ you must give it as "-G", nothing more, nothing less;
+
+ the first argument after the "-G" flag, if present, will be used
+ to specify the information to dump;
+
+ arguments after that will not be used. */
+ if (argc >= 2 && strcmp(argv[1], "-G") == 0) {
+ if (argc == 2)
+ proto_registrar_dump_fields();
+ else {
+ if (strcmp(argv[2], "fields") == 0)
+ proto_registrar_dump_fields();
+ else if (strcmp(argv[2], "protocols") == 0)
+ proto_registrar_dump_protocols();
+ else {
+ fprintf(stderr, "tethereal: Invalid \"%s\" option for -G flag\n",
+ argv[2]);
+ exit(1);
+ }
+ }
+ exit(0);
+ }
+
+ /* Set the current locale according to the program environment.
+ * We haven't localized anything, but some GTK widgets are localized
+ * (the file selection dialogue, for example).
+ * This also sets the C-language locale to the native environment. */
+ gtk_set_locale();
+
+ /* Let GTK get its args */
+ gtk_init (&argc, &argv);
+
+ /* Read the preference files. */
+ prefs = read_prefs(&gpf_open_errno, &gpf_path, &pf_open_errno, &pf_path);
+
+#ifdef HAVE_LIBPCAP
+ capture_opts.has_snaplen = FALSE;
+ capture_opts.snaplen = MIN_PACKET_SIZE;
+ capture_opts.has_autostop_count = FALSE;
+ capture_opts.autostop_count = 1;
+ capture_opts.has_autostop_duration = FALSE;
+ capture_opts.autostop_duration = 1;
+ capture_opts.has_autostop_filesize = FALSE;
+ capture_opts.autostop_filesize = 1;
+ capture_opts.ringbuffer_on = FALSE;
+ capture_opts.ringbuffer_num_files = RINGBUFFER_MIN_NUM_FILES;
+
+ /* If this is a capture child process, it should pay no attention
+ to the "prefs.capture_prom_mode" setting in the preferences file;
+ it should do what the parent process tells it to do, and if
+ the parent process wants it not to run in promiscuous mode, it'll
+ tell it so with a "-p" flag.
+
+ Otherwise, set promiscuous mode from the preferences setting. */
+ if (capture_child)
+ capture_opts.promisc_mode = TRUE;
+ else
+ capture_opts.promisc_mode = prefs->capture_prom_mode;
+
+ /* Set "Update list of packets in real time" mode from the preferences
+ setting. */
+ capture_opts.sync_mode = prefs->capture_real_time;
+
+ /* And do the same for "Automatic scrolling in live capture" mode. */
+ auto_scroll_live = prefs->capture_auto_scroll;
+#endif
+
+ /* Set the name resolution code's flags from the preferences. */
+ g_resolv_flags = prefs->name_resolve;
+
+ /* Read the capture filter file. */
+ read_filter_list(CFILTER_LIST, &cf_path, &cf_open_errno);
+
+ /* Read the display filter file. */
+ read_filter_list(DFILTER_LIST, &df_path, &df_open_errno);
+
+ /* Initialize the capture file struct */
+ cfile.plist = NULL;
+ cfile.plist_end = NULL;
+ cfile.wth = NULL;
+ cfile.filename = NULL;
+ cfile.user_saved = FALSE;
+ cfile.is_tempfile = FALSE;
+ cfile.rfcode = NULL;
+ cfile.dfilter = NULL;
+ cfile.dfcode = NULL;
+#ifdef HAVE_LIBPCAP
+ cfile.cfilter = g_strdup(EMPTY_FILTER);
+#endif
+ cfile.iface = NULL;
+ cfile.save_file = NULL;
+ cfile.save_file_fd = -1;
+ cfile.has_snap = FALSE;
+ cfile.snap = WTAP_MAX_PACKET_SIZE;
+ cfile.count = 0;
+
+ /* Assemble the compile-time options */
+ comp_info_str = g_string_new("");
+
+ g_string_append(comp_info_str, "with ");
+#ifdef GTK_MAJOR_VERSION
+ g_string_sprintfa(comp_info_str,
+ "GTK+ %d.%d.%d", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
+ GTK_MICRO_VERSION);
+#else
+ g_string_sprintfa(comp_info_str,
+ "GTK+ (version unknown)");
+#endif
+
+ g_string_append(comp_info_str, ", with ");
+#ifdef GLIB_MAJOR_VERSION
+ g_string_sprintfa(comp_info_str,
+ "GLib %d.%d.%d", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION,
+ GLIB_MICRO_VERSION);
+#else
+ g_string_sprintfa(comp_info_str, "GLib (version unknown)");
+#endif
+
+#ifdef HAVE_LIBPCAP
+ g_string_append(comp_info_str, ", with libpcap ");
+#ifdef HAVE_PCAP_VERSION
+ g_string_append(comp_info_str, pcap_version);
+#else /* HAVE_PCAP_VERSION */
+ g_string_append(comp_info_str, "(version unknown)");
+#endif /* HAVE_PCAP_VERSION */
+#else /* HAVE_LIBPCAP */
+ g_string_append(comp_info_str, ", without libpcap");
+#endif /* HAVE_LIBPCAP */
+
+#ifdef HAVE_LIBZ
+ g_string_append(comp_info_str, ", with libz ");
+#ifdef ZLIB_VERSION
+ g_string_append(comp_info_str, ZLIB_VERSION);
+#else /* ZLIB_VERSION */
+ g_string_append(comp_info_str, "(version unknown)");
+#endif /* ZLIB_VERSION */
+#else /* HAVE_LIBZ */
+ g_string_append(comp_info_str, ", without libz");
+#endif /* HAVE_LIBZ */
+
+ /* Oh, this is pretty */
+#ifdef HAVE_UCD_SNMP
+ g_string_append(comp_info_str, ", with UCD SNMP ");
+#ifdef HAVE_UCD_SNMP_VERSION_H
+ g_string_append(comp_info_str, VersionInfo);
+#else /* HAVE_UCD_SNMP_VERSION_H */
+ g_string_append(comp_info_str, "(version unknown)");
+#endif /* HAVE_UCD_SNMP_VERSION_H */
+#else /* no SNMP library */
+ g_string_append(comp_info_str, ", without UCD SNMP");
+#endif
+
+ /* Now get our args */
+ while ((opt = getopt(argc, argv, optstring)) != -1) {
+ switch (opt) {
+ case 'a': /* autostop criteria */
+#ifdef HAVE_LIBPCAP
+ if (set_autostop_criterion(optarg) == FALSE) {
+ fprintf(stderr, "ethereal: Invalid or unknown -a flag \"%s\"\n", optarg);
+ exit(1);
+ }
+#else
+ capture_option_specified = TRUE;
+ arg_error = TRUE;
+#endif
+ break;
+ case 'b': /* Ringbuffer option */
+#ifdef HAVE_LIBPCAP
+ capture_opts.ringbuffer_on = TRUE;
+ capture_opts.ringbuffer_num_files =
+ get_positive_int(optarg, "number of ring buffer files");
+#else
+ capture_option_specified = TRUE;
+ arg_error = TRUE;
+#endif
+ break;
+ case 'B': /* Byte view pane height */
+ bv_size = get_positive_int(optarg, "byte view pane height");
+ break;
+ case 'c': /* Capture xxx packets */
+#ifdef HAVE_LIBPCAP
+ capture_opts.has_autostop_count = TRUE;
+ capture_opts.autostop_count = get_positive_int(optarg, "packet count");
+#else
+ capture_option_specified = TRUE;
+ arg_error = TRUE;
+#endif
+ break;
+ case 'f':
+#ifdef HAVE_LIBPCAP
+ if (cfile.cfilter)
+ g_free(cfile.cfilter);
+ cfile.cfilter = g_strdup(optarg);
+#else
+ capture_option_specified = TRUE;
+ arg_error = TRUE;
+#endif
+ break;
+ case 'h': /* Print help and exit */
+ print_usage(TRUE);
+ exit(0);
+ break;
+ case 'i': /* Use interface xxx */
+#ifdef HAVE_LIBPCAP
+ cfile.iface = g_strdup(optarg);
+#else
+ capture_option_specified = TRUE;
+ arg_error = TRUE;
+#endif
+ break;
+ case 'k': /* Start capture immediately */
+#ifdef HAVE_LIBPCAP
+ start_capture = TRUE;
+#else
+ capture_option_specified = TRUE;
+ arg_error = TRUE;
+#endif
+ break;
+ case 'l': /* Automatic scrolling in live capture mode */
+#ifdef HAVE_LIBPCAP
+ auto_scroll_live = TRUE;
+#else
+ capture_option_specified = TRUE;
+ arg_error = TRUE;
+#endif
+ break;
+ case 'm': /* Fixed-width font for the display */
+ if (prefs->gui_font_name != NULL)
+ g_free(prefs->gui_font_name);
+ prefs->gui_font_name = g_strdup(optarg);
+ break;
+ case 'n': /* No name resolution */
+ g_resolv_flags = RESOLV_NONE;
+ break;
+ case 'N': /* Select what types of addresses/port #s to resolve */
+ if (g_resolv_flags == RESOLV_ALL)
+ g_resolv_flags = RESOLV_NONE;
+ badopt = string_to_name_resolve(optarg, &g_resolv_flags);
+ if (badopt != '\0') {
+ fprintf(stderr, "ethereal: -N specifies unknown resolving option '%c'; valid options are 'm', 'n', and 't'\n",
+ badopt);
+ exit(1);
+ }
+ break;
+ case 'o': /* Override preference from command line */
+ switch (prefs_set_pref(optarg)) {
+
+ case PREFS_SET_SYNTAX_ERR:
+ fprintf(stderr, "ethereal: Invalid -o flag \"%s\"\n", optarg);
+ exit(1);
+ break;
+
+ case PREFS_SET_NO_SUCH_PREF:
+ case PREFS_SET_OBSOLETE:
+ fprintf(stderr, "ethereal: -o flag \"%s\" specifies unknown preference\n",
+ optarg);
+ exit(1);
+ break;
+ }
+ break;
+ case 'p': /* Don't capture in promiscuous mode */
+#ifdef HAVE_LIBPCAP
+ capture_opts.promisc_mode = FALSE;
+#else
+ capture_option_specified = TRUE;
+ arg_error = TRUE;
+#endif
+ break;
+ case 'P': /* Packet list pane height */
+ pl_size = get_positive_int(optarg, "packet list pane height");
+ break;
+ case 'Q': /* Quit after capture (just capture to file) */
+#ifdef HAVE_LIBPCAP
+ quit_after_cap = 1;
+ start_capture = TRUE; /*** -Q implies -k !! ***/
+#else
+ capture_option_specified = TRUE;
+ arg_error = TRUE;
+#endif
+ break;
+ case 'r': /* Read capture file xxx */
+ /* We may set "last_open_dir" to "cf_name", and if we change
+ "last_open_dir" later, we free the old value, so we have to
+ set "cf_name" to something that's been allocated. */
+ cf_name = g_strdup(optarg);
+ break;
+ case 'R': /* Read file filter */
+ rfilter = optarg;
+ break;
+ case 's': /* Set the snapshot (capture) length */
+#ifdef HAVE_LIBPCAP
+ capture_opts.has_snaplen = TRUE;
+ capture_opts.snaplen = get_positive_int(optarg, "snapshot length");
+#else
+ capture_option_specified = TRUE;
+ arg_error = TRUE;
+#endif
+ break;
+ case 'S': /* "Sync" mode: used for following file ala tail -f */
+#ifdef HAVE_LIBPCAP
+ capture_opts.sync_mode = TRUE;
+#else
+ capture_option_specified = TRUE;
+ arg_error = TRUE;
+#endif
+ break;
+ case 't': /* Time stamp type */
+ if (strcmp(optarg, "r") == 0)
+ timestamp_type = RELATIVE;
+ else if (strcmp(optarg, "a") == 0)
+ timestamp_type = ABSOLUTE;
+ else if (strcmp(optarg, "ad") == 0)
+ timestamp_type = ABSOLUTE_WITH_DATE;
+ else if (strcmp(optarg, "d") == 0)
+ timestamp_type = DELTA;
+ else {
+ fprintf(stderr, "ethereal: Invalid time stamp type \"%s\"\n",
+ optarg);
+ fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
+ fprintf(stderr, "\"ad\" for absolute with date, or \"d\" for delta.\n");
+ exit(1);
+ }
+ break;
+ case 'T': /* Tree view pane height */
+ tv_size = get_positive_int(optarg, "tree view pane height");
+ break;
+ case 'v': /* Show version and exit */
+ show_version();
+#ifdef WIN32
+ if (console_was_created)
+ destroy_console();
+#endif
+ exit(0);
+ break;
+ case 'w': /* Write to capture file xxx */
+#ifdef HAVE_LIBPCAP
+ save_file = g_strdup(optarg);
+#else
+ capture_option_specified = TRUE;
+ arg_error = TRUE;
+#endif
+ break;
+#ifdef HAVE_LIBPCAP
+ /* This is a hidden option supporting Sync mode, so we don't set
+ * the error flags for the user in the non-libpcap case.
+ */
+ case 'W': /* Write to capture file FD xxx */
+ cfile.save_file_fd = atoi(optarg);
+ break;
+#endif
+
+#ifdef _WIN32
+#ifdef HAVE_LIBPCAP
+ /* Hidden option supporting Sync mode */
+ case 'Z': /* Write to pipe FD XXX */
+ /* associate stdout with pipe */
+ i = atoi(optarg);
+ if (dup2(i, 1) < 0) {
+ fprintf(stderr, "Unable to dup pipe handle\n");
+ exit(1);
+ }
+ break;
+#endif /* HAVE_LIBPCAP */
+#endif /* _WIN32 */
+
+ default:
+ case '?': /* Bad flag - print usage message */
+ arg_error = TRUE;
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc >= 1) {
+ if (cf_name != NULL) {
+ /*
+ * Input file name specified with "-r" *and* specified as a regular
+ * command-line argument.
+ */
+ arg_error = TRUE;
+ } else {
+ /*
+ * Input file name not specified with "-r", and a command-line argument
+ * was specified; treat it as the input file name.
+ *
+ * Yes, this is different from tethereal, where non-flag command-line
+ * arguments are a filter, but this works better on GUI desktops
+ * where a command can be specified to be run to open a particular
+ * file - yes, you could have "-r" as the last part of the command,
+ * but that's a bit ugly.
+ */
+ cf_name = g_strdup(argv[0]);
+ }
+ argc--;
+ argv++;
+ }
+
+ if (argc != 0) {
+ /*
+ * Extra command line arguments were specified; complain.
+ */
+ fprintf(stderr, "Invalid argument: %s\n", argv[0]);
+ arg_error = TRUE;
+ }
+ if (arg_error) {
+ print_usage(FALSE);
+ exit(1);
+ }
+
+#ifdef HAVE_LIBPCAP
+ if (capture_opts.ringbuffer_on) {
+ /* Ring buffer works only under certain conditions:
+ a) ring buffer does not work with temporary files;
+ b) sync_mode and capture_opts.ringbuffer_on are mutually exclusive -
+ sync_mode takes precedence;
+ c) it makes no sense to enable the ring buffer if the maximum
+ file size is set to "infinite". */
+ if (cfile.save_file == NULL) {
+ fprintf(stderr, "ethereal: Ring buffer requested, but capture isn't being saved to a permanent file.\n");
+ capture_opts.ringbuffer_on = FALSE;
+ }
+ if (capture_opts.sync_mode) {
+ fprintf(stderr, "ethereal: Ring buffer requested, but an \"Update list of packets in real time\" capture is being done.\n");
+ capture_opts.ringbuffer_on = FALSE;
+ }
+ if (!capture_opts.has_autostop_filesize) {
+ fprintf(stderr, "ethereal: Ring buffer requested, but no maximum capture file size was specified.\n");
+ capture_opts.ringbuffer_on = FALSE;
+ }
+ }
+#endif
+
+#ifdef WIN32
+ /* Load wpcap if possible */
+ load_wpcap();
+
+ /* Start windows sockets */
+ WSAStartup( MAKEWORD( 1, 1 ), &wsaData );
+#endif
+
+ /* Notify all registered modules that have had any of their preferences
+ changed either from one of the preferences file or from the command
+ line that their preferences have changed. */
+ prefs_apply_all();
+
+#ifndef HAVE_LIBPCAP
+ if (capture_option_specified)
+ fprintf(stderr, "This version of Ethereal was not built with support for capturing packets.\n");
+#endif
+#ifdef HAVE_LIBPCAP
+ if (start_capture) {
+ /* We're supposed to do a live capture; did the user specify an interface
+ to use? */
+ if (cfile.iface == NULL) {
+ /* No - is a default specified in the preferences file? */
+ if (prefs->capture_device != NULL) {
+ /* Yes - use it. */
+ cfile.iface = g_strdup(prefs->capture_device);
+ } else {
+ /* No - pick the first one from the list of interfaces. */
+ if_list = get_interface_list(&err, err_str);
+ if (if_list == NULL) {
+ switch (err) {
+
+ case CANT_GET_INTERFACE_LIST:
+ fprintf(stderr, "ethereal: Can't get list of interfaces: %s\n",
+ err_str);
+ break;
+
+ case NO_INTERFACES_FOUND:
+ fprintf(stderr, "ethereal: There are no interfaces on which a capture can be done\n");
+ break;
+ }
+ exit(2);
+ }
+ cfile.iface = g_strdup(if_list->data); /* first interface */
+ free_interface_list(if_list);
+ }
+ }
+ }
+ if (capture_child) {
+ if (cfile.save_file_fd == -1) {
+ /* XXX - send this to the standard output as something our parent
+ should put in an error message box? */
+ fprintf(stderr, "%s: \"-W\" flag not specified\n", CHILD_NAME);
+ exit(1);
+ }
+ }
+#endif
+
+ /* Build the column format array */
+ col_init(&cfile.cinfo, prefs->num_cols);
+ for (i = 0; i < cfile.cinfo.num_cols; i++) {
+ cfile.cinfo.col_fmt[i] = get_column_format(i);
+ cfile.cinfo.col_title[i] = g_strdup(get_column_title(i));
+ cfile.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
+ NUM_COL_FMTS);
+ get_column_format_matches(cfile.cinfo.fmt_matx[i], cfile.cinfo.col_fmt[i]);
+ cfile.cinfo.col_data[i] = NULL;
+ if (cfile.cinfo.col_fmt[i] == COL_INFO)
+ cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
+ else
+ cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
+ cfile.cinfo.col_expr[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
+ cfile.cinfo.col_expr_val[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
+ }
+
+#ifdef HAVE_LIBPCAP
+ if (capture_opts.has_snaplen) {
+ if (capture_opts.snaplen < 1)
+ capture_opts.snaplen = WTAP_MAX_PACKET_SIZE;
+ else if (capture_opts.snaplen < MIN_PACKET_SIZE)
+ capture_opts.snaplen = MIN_PACKET_SIZE;
+ }
+
+ /* Check the value range of the ringbuffer_num_files parameter */
+ if (capture_opts.ringbuffer_num_files < RINGBUFFER_MIN_NUM_FILES)
+ capture_opts.ringbuffer_num_files = RINGBUFFER_MIN_NUM_FILES;
+ else if (capture_opts.ringbuffer_num_files > RINGBUFFER_MAX_NUM_FILES)
+ capture_opts.ringbuffer_num_files = RINGBUFFER_MAX_NUM_FILES;
+#endif
+
+ rc_file = get_persconffile_path(RC_FILE, FALSE);
+ gtk_rc_parse(rc_file);
+
+ /* Try to load the regular and boldface fixed-width fonts */
+ m_r_font = pango_font_description_from_string(prefs->gui_font_name);
+ m_b_font = pango_font_description_copy(m_r_font);
+ pango_font_description_set_weight(m_b_font, PANGO_WEIGHT_BOLD);
+ if (m_r_font == NULL || m_b_font == NULL) {
+ /* XXX - pop this up as a dialog box? no */
+ if (m_r_font == NULL) {
+#ifdef HAVE_LIBPCAP
+ if (!capture_child)
+#endif
+ fprintf(stderr, "ethereal: Warning: font %s not found - defaulting to Monospace 12\n",
+ prefs->gui_font_name);
+ } else {
+ pango_font_description_free(m_r_font);
+ }
+ if (m_b_font == NULL) {
+#ifdef HAVE_LIBPCAP
+ if (!capture_child)
+#endif
+ fprintf(stderr, "ethereal: Warning: bold font %s not found - defaulting"
+ " to Monospace 9\n", prefs->gui_font_name);
+ } else {
+ pango_font_description_free(m_b_font);
+ }
+ if ((m_r_font = pango_font_description_from_string("Monospace 9")) == NULL) {
+ fprintf(stderr, "ethereal: Error: font Monospace 9 not found\n");
+ exit(1);
+ }
+ if ((m_b_font = pango_font_description_copy(m_r_font)) == NULL) {
+ fprintf(stderr, "ethereal: Error: font Monospace 9 bold not found\n");
+ exit(1);
+ }
+ pango_font_description_set_weight(m_b_font, PANGO_WEIGHT_BOLD);
+ g_free(prefs->gui_font_name);
+ prefs->gui_font_name = g_strdup("Monospace 9");
+ }
+
+ /* Call this for the side-effects that set_fonts() produces */
+ set_fonts(m_r_font, m_b_font);
+
+
+#ifdef HAVE_LIBPCAP
+ /* Is this a "child" ethereal, which is only supposed to pop up a
+ capture box to let us stop the capture, and run a capture
+ to a file that our parent will read? */
+ if (!capture_child) {
+#endif
+ /* No. Pop up the main window, and read in a capture file if
+ we were told to. */
+
+ create_main_window(pl_size, tv_size, bv_size, prefs);
+ set_menus_for_capture_file(FALSE);
+
+ colfilter_init();
+
+ /* If we were given the name of a capture file, read it in now;
+ we defer it until now, so that, if we can't open it, and pop
+ up an alert box, the alert box is more likely to come up on
+ top of the main window - but before the preference-file-error
+ alert box, so, if we get one of those, it's more likely to come
+ up on top of us. */
+ if (cf_name) {
+ if (rfilter != NULL) {
+ if (!dfilter_compile(rfilter, &rfcode)) {
+ simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
+ rfilter_parse_failed = TRUE;
+ }
+ }
+ if (!rfilter_parse_failed) {
+ if ((err = open_cap_file(cf_name, FALSE, &cfile)) == 0) {
+ /* "open_cap_file()" succeeded, so it closed the previous
+ capture file, and thus destroyed any previous read filter
+ attached to "cf". */
+ cfile.rfcode = rfcode;
+ switch (read_cap_file(&cfile, &err)) {
+
+ case READ_SUCCESS:
+ case READ_ERROR:
+ /* Just because we got an error, that doesn't mean we were unable
+ to read any of the file; we handle what we could get from the
+ file. */
+ break;
+
+ case READ_ABORTED:
+ /* Exit now. */
+ gtk_exit(0);
+ break;
+ }
+ /* Save the name of the containing directory specified in the
+ path name, if any; we can write over cf_name, which is a
+ good thing, given that "get_dirname()" does write over its
+ argument. */
+ s = get_dirname(cf_name);
+ set_last_open_dir(s);
+ } else {
+ if (rfcode != NULL)
+ dfilter_free(rfcode);
+ cfile.rfcode = NULL;
+ }
+ }
+ }
+#ifdef HAVE_LIBPCAP
+ }
+#endif
+
+ /* If the global preferences file exists but we failed to open it,
+ pop up an alert box; we defer that until now, so that the alert
+ box is more likely to come up on top of the main window. */
+ if (gpf_path != NULL) {
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "Could not open global preferences file\n\"%s\": %s.", gpf_path,
+ strerror(gpf_open_errno));
+ }
+
+ /* If the user's preferences file exists but we failed to open it,
+ pop up an alert box; we defer that until now, so that the alert
+ box is more likely to come up on top of the main window. */
+ if (pf_path != NULL) {
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "Could not open your preferences file\n\"%s\": %s.", pf_path,
+ strerror(pf_open_errno));
+ g_free(pf_path);
+ pf_path = NULL;
+ }
+
+ /* If the user's capture filter file exists but we failed to open it,
+ pop up an alert box; we defer that until now, so that the alert
+ box is more likely to come up on top of the main window. */
+ if (cf_path != NULL) {
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "Could not open your capture filter file\n\"%s\": %s.", cf_path,
+ strerror(cf_open_errno));
+ g_free(cf_path);
+ }
+
+ /* If the user's display filter file exists but we failed to open it,
+ pop up an alert box; we defer that until now, so that the alert
+ box is more likely to come up on top of the main window. */
+ if (df_path != NULL) {
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "Could not open your display filter file\n\"%s\": %s.", df_path,
+ strerror(df_open_errno));
+ g_free(df_path);
+ }
+
+#ifdef HAVE_LIBPCAP
+ if (capture_child) {
+ /* This is the child process for a sync mode or fork mode capture,
+ so just do the low-level work of a capture - don't create
+ a temporary file and fork off *another* child process (so don't
+ call "do_capture()"). */
+
+ /* XXX - hand these stats to the parent process */
+ capture(&stats_known, &stats);
+
+ /* The capture is done; there's nothing more for us to do. */
+ gtk_exit(0);
+ } else {
+ if (start_capture) {
+ /* "-k" was specified; start a capture. */
+ do_capture(save_file);
+ }
+ else {
+ set_menus_for_capture_in_progress(FALSE);
+ }
+ }
+#else
+ set_menus_for_capture_in_progress(FALSE);
+#endif
+
+ gtk_main();
+
+ /* Try to save our geometry. GTK+ provides two routines to get a
+ window's position relative to the X root window. If I understand the
+ documentation correctly, gdk_window_get_deskrelative_origin applies
+ mainly to Enlightenment and gdk_window_get_root_origin applies for
+ all other WMs.
+
+ The code below tries both routines, and picks the one that returns
+ the upper-left-most coordinates.
+
+ More info at:
+
+http://mail.gnome.org/archives/gtk-devel-list/2001-March/msg00289.html
+http://www.gtk.org/faq/#AEN600 */
+
+ /* Re-read our saved preferences. */
+ /* XXX - Move all of this into a separate function? */
+ prefs = read_prefs(&gpf_open_errno, &gpf_path, &pf_open_errno, &pf_path);
+
+ if (pf_path == NULL) {
+ if (prefs->gui_geometry_save_position) {
+ if (top_level->window != NULL) {
+ gdk_window_get_root_origin(top_level->window, &root_x, &root_y);
+ if (gdk_window_get_deskrelative_origin(top_level->window,
+ &desk_x, &desk_y)) {
+ if (desk_x <= root_x && desk_y <= root_y) {
+ root_x = desk_x;
+ root_y = desk_y;
+ }
+ }
+ }
+ if (prefs->gui_geometry_main_x != root_x) {
+ prefs->gui_geometry_main_x = root_x;
+ prefs_write_needed = TRUE;
+ }
+ if (prefs->gui_geometry_main_y != root_y) {
+ prefs->gui_geometry_main_y = root_y;
+ prefs_write_needed = TRUE;
+ }
+ }
+
+ if (prefs->gui_geometry_save_size) {
+ if (top_level->window != NULL) {
+ /* XXX - Is this the "approved" method? */
+ gdk_window_get_size(top_level->window, &top_width, &top_height);
+ }
+ if (prefs->gui_geometry_main_width != top_width) {
+ prefs->gui_geometry_main_width = top_width;
+ prefs_write_needed = TRUE;
+ }
+ if (prefs->gui_geometry_main_height != top_height) {
+ prefs->gui_geometry_main_height = top_height;
+ prefs_write_needed = TRUE;
+ }
+ }
+
+ if (prefs_write_needed) {
+ write_prefs(&pf_path);
+ }
+ } else {
+ /* Ignore errors silently */
+ g_free(pf_path);
+ }
+
+ epan_cleanup();
+ g_free(rc_file);
+
+#ifdef WIN32
+ /* Shutdown windows sockets */
+ WSACleanup();
+
+ /* For some unknown reason, the "atexit()" call in "create_console()"
+ doesn't arrange that "destroy_console()" be called when we exit,
+ so we call it here if a console was created. */
+ if (console_was_created)
+ destroy_console();
+#endif
+
+ gtk_exit(0);
+
+ /* This isn't reached, but we need it to keep GCC from complaining
+ that "main()" returns without returning a value - it knows that
+ "exit()" never returns, but it doesn't know that "gtk_exit()"
+ doesn't, as GTK+ doesn't declare it with the attribute
+ "noreturn". */
+ return 0; /* not reached */
+}
+
+#ifdef WIN32
+
+/* We build this as a GUI subsystem application on Win32, so
+ "WinMain()", not "main()", gets called.
+
+ Hack shamelessly stolen from the Win32 port of the GIMP. */
+#ifdef __GNUC__
+#define _stdcall __attribute__((stdcall))
+#endif
+
+int _stdcall
+WinMain (struct HINSTANCE__ *hInstance,
+ struct HINSTANCE__ *hPrevInstance,
+ char *lpszCmdLine,
+ int nCmdShow)
+{
+ has_no_console = TRUE;
+ return main (__argc, __argv);
+}
+
+/*
+ * If this application has no console window to which its standard output
+ * would go, create one.
+ */
+static void
+create_console(void)
+{
+ if (has_no_console) {
+ /* We have no console to which to print the version string, so
+ create one and make it the standard input, output, and error. */
+ if (!AllocConsole())
+ return; /* couldn't create console */
+ freopen("CONIN$", "r", stdin);
+ freopen("CONOUT$", "w", stdout);
+ freopen("CONOUT$", "w", stderr);
+
+ /* Well, we have a console now. */
+ has_no_console = FALSE;
+ console_was_created = TRUE;
+
+ /* Now register "destroy_console()" as a routine to be called just
+ before the application exits, so that we can destroy the console
+ after the user has typed a key (so that the console doesn't just
+ disappear out from under them, giving the user no chance to see
+ the message(s) we put in there). */
+ atexit(destroy_console);
+ }
+}
+
+static void
+destroy_console(void)
+{
+ printf("\n\nPress any key to exit\n");
+ _getch();
+ FreeConsole();
+}
+
+/* This routine should not be necessary, at least as I read the GLib
+ source code, as it looks as if GLib is, on Win32, *supposed* to
+ create a console window into which to display its output.
+
+ That doesn't happen, however. I suspect there's something completely
+ broken about that code in GLib-for-Win32, and that it may be related
+ to the breakage that forces us to just call "printf()" on the message
+ rather than passing the message on to "g_log_default_handler()"
+ (which is the routine that does the aforementioned non-functional
+ console window creation). */
+static void
+console_log_handler(const char *log_domain, GLogLevelFlags log_level,
+ const char *message, gpointer user_data)
+{
+ create_console();
+ if (console_was_created) {
+ /* For some unknown reason, the above doesn't appear to actually cause
+ anything to be sent to the standard output, so we'll just splat the
+ message out directly, just to make sure it gets out. */
+ printf("%s\n", message);
+ } else
+ g_log_default_handler(log_domain, log_level, message, user_data);
+}
+#endif
+
+static void
+create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
+{
+ GtkWidget *main_vbox, *menubar, *u_pane, *l_pane,
+ *stat_hbox, *column_lb,
+ *filter_bt, *filter_cm, *filter_te,
+ *filter_apply,
+ *filter_reset;
+ GList *filter_list = NULL;
+ GtkAccelGroup *accel;
+ GtkStyle *win_style;
+ GdkBitmap *ascend_bm, *descend_bm;
+ GdkPixmap *ascend_pm, *descend_pm;
+ column_arrows *col_arrows;
+ int i;
+ /* Display filter construct dialog has an Apply button, and "OK" not
+ only sets our text widget, it activates it (i.e., it causes us to
+ filter the capture). */
+ static construct_args_t args = {
+ "Ethereal: Display Filter",
+ TRUE,
+ TRUE
+ };
+
+ /* Main window */
+ top_level = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_name(top_level, "main window");
+ g_signal_connect(G_OBJECT(top_level), "delete_event",
+ G_CALLBACK(main_window_delete_event_cb), NULL);
+ g_signal_connect(G_OBJECT(top_level), "realize",
+ G_CALLBACK (window_icon_realize_cb), NULL);
+ gtk_window_set_title(GTK_WINDOW(top_level), "The Ethereal Network Analyzer");
+ if (prefs->gui_geometry_save_position) {
+ gtk_widget_set_uposition(GTK_WIDGET(top_level),
+ prefs->gui_geometry_main_x,
+ prefs->gui_geometry_main_y);
+ }
+ if (prefs->gui_geometry_save_size) {
+ gtk_widget_set_size_request(GTK_WIDGET(top_level),
+ prefs->gui_geometry_main_width,
+ prefs->gui_geometry_main_height);
+ } else {
+ gtk_widget_set_size_request(GTK_WIDGET(top_level), DEF_WIDTH, -1);
+ }
+ gtk_window_set_policy(GTK_WINDOW(top_level), TRUE, TRUE, FALSE);
+
+ /* Container for menu bar, paned windows and progress/info box */
+ main_vbox = gtk_vbox_new(FALSE, 1);
+ gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
+ gtk_container_add(GTK_CONTAINER(top_level), main_vbox);
+ gtk_widget_show(main_vbox);
+
+ /* Menu bar */
+ get_main_menu(&menubar, &accel);
+ gtk_window_add_accel_group(GTK_WINDOW(top_level), accel);
+ gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
+ gtk_widget_show(menubar);
+
+ /* Panes for the packet list, tree, and byte view */
+ u_pane = gtk_vpaned_new();
+ gtk_paned_gutter_size(GTK_PANED(u_pane), (GTK_PANED(u_pane))->handle_size);
+ l_pane = gtk_vpaned_new();
+ gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
+ gtk_container_add(GTK_CONTAINER(main_vbox), u_pane);
+ gtk_widget_show(l_pane);
+ gtk_paned_add2(GTK_PANED(u_pane), l_pane);
+ gtk_widget_show(u_pane);
+
+ /* Packet list */
+ pkt_scrollw = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(pkt_scrollw),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_widget_show(pkt_scrollw);
+ gtk_paned_add1(GTK_PANED(u_pane), pkt_scrollw);
+
+ packet_list = gtk_clist_new(cfile.cinfo.num_cols);
+ /* Column titles are filled in below */
+ gtk_container_add(GTK_CONTAINER(pkt_scrollw), packet_list);
+
+ col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) *
+ cfile.cinfo.num_cols);
+
+ set_plist_sel_browse(prefs->gui_plist_sel_browse);
+ set_plist_font(m_r_font);
+ gtk_widget_set_name(packet_list, "packet list");
+ g_signal_connect(G_OBJECT(packet_list), "click-column",
+ G_CALLBACK(packet_list_click_column_cb), col_arrows);
+ g_signal_connect(G_OBJECT(packet_list), "select-row",
+ G_CALLBACK(packet_list_select_cb), NULL);
+ g_signal_connect(G_OBJECT(packet_list), "unselect-row",
+ G_CALLBACK(packet_list_unselect_cb), NULL);
+ for (i = 0; i < cfile.cinfo.num_cols; i++) {
+ if (get_column_resize_type(cfile.cinfo.col_fmt[i]) != RESIZE_MANUAL)
+ gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, TRUE);
+
+ /* Right-justify the packet number column. */
+ if (cfile.cinfo.col_fmt[i] == COL_NUMBER)
+ gtk_clist_set_column_justification(GTK_CLIST(packet_list), i,
+ GTK_JUSTIFY_RIGHT);
+ }
+ gtk_widget_set_size_request(packet_list, -1, pl_size);
+ g_signal_connect(G_OBJECT(packet_list), "button_press_event",
+ G_CALLBACK(popup_menu_handler),
+ gtk_object_get_data(GTK_OBJECT(popup_menu_object),
+ PM_PACKET_LIST_KEY));
+ g_signal_connect(G_OBJECT(packet_list), "button_press_event",
+ G_CALLBACK(packet_list_button_pressed_cb), NULL);
+ gtk_clist_set_compare_func(GTK_CLIST(packet_list), packet_list_compare);
+ gtk_widget_show(packet_list);
+
+ /* Tree view */
+ create_tree_view(tv_size, prefs, l_pane, &tv_scrollw, &tree_view);
+ g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view))),
+ "changed", G_CALLBACK(tree_view_selection_changed_cb),
+ NULL);
+ g_signal_connect(G_OBJECT(tree_view), "button_press_event",
+ G_CALLBACK(popup_menu_handler),
+ gtk_object_get_data(GTK_OBJECT(popup_menu_object),
+ PM_TREE_VIEW_KEY));
+ gtk_widget_show(tree_view);
+
+ /* Byte view. */
+ byte_nb_ptr = create_byte_view(bv_size, l_pane);
+
+ g_signal_connect(G_OBJECT(byte_nb_ptr), "button_press_event",
+ G_CALLBACK(popup_menu_handler),
+ gtk_object_get_data(GTK_OBJECT(popup_menu_object),
+ PM_HEXDUMP_KEY));
+
+ /* Filter/info box */
+ stat_hbox = gtk_hbox_new(FALSE, 1);
+ gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
+ gtk_box_pack_start(GTK_BOX(main_vbox), stat_hbox, FALSE, TRUE, 0);
+ gtk_widget_show(stat_hbox);
+
+ filter_bt = gtk_button_new_with_label("Filter:");
+ g_signal_connect(G_OBJECT(filter_bt), "clicked",
+ G_CALLBACK(display_filter_construct_cb), &args);
+ gtk_box_pack_start(GTK_BOX(stat_hbox), filter_bt, FALSE, TRUE, 0);
+ gtk_widget_show(filter_bt);
+
+ filter_cm = gtk_combo_new();
+ filter_list = g_list_append (filter_list, "");
+ gtk_combo_set_popdown_strings(GTK_COMBO(filter_cm), filter_list);
+ gtk_combo_disable_activate(GTK_COMBO(filter_cm));
+ gtk_combo_set_case_sensitive(GTK_COMBO(filter_cm), TRUE);
+ gtk_object_set_data(GTK_OBJECT(filter_cm), E_DFILTER_FL_KEY, filter_list);
+ filter_te = GTK_COMBO(filter_cm)->entry;
+ gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
+ gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_CM_KEY, filter_cm);
+ gtk_box_pack_start(GTK_BOX(stat_hbox), filter_cm, TRUE, TRUE, 3);
+ g_signal_connect(G_OBJECT(filter_te), "activate",
+ G_CALLBACK(filter_activate_cb), filter_te);
+ gtk_widget_show(filter_cm);
+
+ filter_reset = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
+ gtk_object_set_data(GTK_OBJECT(filter_reset), E_DFILTER_TE_KEY, filter_te);
+ g_signal_connect(G_OBJECT(filter_reset), "clicked",
+ G_CALLBACK(filter_reset_cb), NULL);
+ gtk_box_pack_start(GTK_BOX(stat_hbox), filter_reset, FALSE, TRUE, 1);
+ gtk_widget_show(filter_reset);
+
+ filter_apply = gtk_button_new_from_stock(GTK_STOCK_APPLY);
+ gtk_object_set_data(GTK_OBJECT(filter_apply), E_DFILTER_CM_KEY, filter_cm);
+ g_signal_connect(G_OBJECT(filter_apply), "clicked",
+ G_CALLBACK(filter_activate_cb), filter_te);
+ gtk_box_pack_start(GTK_BOX(stat_hbox), filter_apply, FALSE, TRUE, 1);
+ gtk_widget_show(filter_apply);
+
+ /* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
+ * of any widget that ends up calling a callback which needs
+ * that text entry pointer */
+ set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
+ set_menu_object_data("/File/Reload", E_DFILTER_TE_KEY, filter_te);
+ set_menu_object_data("/Edit/Filters...", E_FILT_TE_PTR_KEY, filter_te);
+ set_menu_object_data("/Tools/Follow TCP Stream", E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data("/Display/Match/Selected", E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data("/Display/Match/Not Selected", E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data("/Display/Match/And Selected", E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data("/Display/Match/Or Selected", E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data("/Display/Match/And Not Selected", E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data("/Display/Match/Or Not Selected", E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data("/Display/Prepare/Selected", E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data("/Display/Prepare/Not Selected", E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data("/Display/Prepare/And Selected", E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data("/Display/Prepare/Or Selected", E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data("/Display/Prepare/And Not Selected", E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data("/Display/Prepare/Or Not Selected", E_DFILTER_TE_KEY,
+ filter_te);
+ gtk_object_set_data(GTK_OBJECT(popup_menu_object), E_DFILTER_TE_KEY,
+ filter_te);
+ gtk_object_set_data(GTK_OBJECT(popup_menu_object), E_MPACKET_LIST_KEY,
+ packet_list);
+
+ info_bar = gtk_statusbar_new();
+ main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
+ file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
+ help_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "help");
+ gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
+ gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
+ gtk_widget_show(info_bar);
+
+ gtk_widget_show(top_level);
+
+ /* Fill in column titles. This must be done after the top level window
+ is displayed. */
+ win_style = gtk_widget_get_style(top_level);
+ ascend_pm = gdk_pixmap_create_from_xpm_d(top_level->window, &ascend_bm,
+ &win_style->bg[GTK_STATE_NORMAL],
+ (gchar **)clist_ascend_xpm);
+ descend_pm = gdk_pixmap_create_from_xpm_d(top_level->window, &descend_bm,
+ &win_style->bg[GTK_STATE_NORMAL],
+ (gchar **)clist_descend_xpm);
+ for (i = 0; i < cfile.cinfo.num_cols; i++) {
+ col_arrows[i].table = gtk_table_new(2, 2, FALSE);
+ gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
+ column_lb = gtk_label_new(cfile.cinfo.col_title[i]);
+ gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2,
+ GTK_SHRINK, GTK_SHRINK, 0, 0);
+ gtk_widget_show(column_lb);
+ col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
+ gtk_table_attach(GTK_TABLE(col_arrows[i].table),
+ col_arrows[i].ascend_pm,
+ 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
+ if (i == 0) {
+ gtk_widget_show(col_arrows[i].ascend_pm);
+ }
+ col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
+ gtk_table_attach(GTK_TABLE(col_arrows[i].table),
+ col_arrows[i].descend_pm,
+ 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
+ gtk_clist_set_column_widget(GTK_CLIST(packet_list), i,
+ col_arrows[i].table);
+ gtk_widget_show(col_arrows[i].table);
+ }
+ gtk_clist_column_titles_show(GTK_CLIST(packet_list));
+}
+
+
+void
+set_last_open_dir(char *dirname)
+{
+ int len;
+
+ if (last_open_dir) {
+ g_free(last_open_dir);
+ }
+
+ if (dirname) {
+ len = strlen(dirname);
+ if (dirname[len-1] != G_DIR_SEPARATOR) {
+ last_open_dir = g_strconcat(dirname, G_DIR_SEPARATOR_S,
+ NULL);
+ }
+ }
+ else {
+ last_open_dir = NULL;
+ }
+}
diff --git a/gtk2/main.h b/gtk2/main.h
new file mode 100644
index 0000000000..b0a637a061
--- /dev/null
+++ b/gtk2/main.h
@@ -0,0 +1,102 @@
+/* main.h
+ * Global defines, etc.
+ *
+ * $Id: main.h,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifndef __MAIN_H__
+#define __MAIN_H__
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "globals.h"
+
+/*
+ * File under personal preferences directory in which GTK settings for
+ * Ethereal are stored.
+ */
+#define RC_FILE "gtkrc"
+
+#ifdef HAVE_LIBPCAP
+#define DEF_READY_MESSAGE " Ready to load or capture"
+#else
+#define DEF_READY_MESSAGE " Ready to load file"
+#endif
+
+#define MATCH_SELECTED_REPLACE 0
+#define MATCH_SELECTED_AND 1
+#define MATCH_SELECTED_OR 2
+#define MATCH_SELECTED_NOT 3
+#define MATCH_SELECTED_AND_NOT 4
+#define MATCH_SELECTED_OR_NOT 5
+
+#define MATCH_SELECTED_MASK 0x0ff
+#define MATCH_SELECTED_APPLY_NOW 0x100
+
+typedef struct _selection_info {
+ GtkWidget *tree;
+ GtkWidget *text;
+} selection_info;
+
+void about_ethereal( GtkWidget *, gpointer);
+void match_selected_cb_replace_ptree( GtkWidget *, gpointer);
+void match_selected_cb_and_ptree( GtkWidget *, gpointer);
+void match_selected_cb_or_ptree( GtkWidget *, gpointer);
+void match_selected_cb_not_ptree( GtkWidget *, gpointer);
+void match_selected_cb_and_ptree_not( GtkWidget *, gpointer);
+void match_selected_cb_or_ptree_not( GtkWidget *, gpointer);
+void prepare_selected_cb_replace_ptree( GtkWidget *, gpointer);
+void prepare_selected_cb_and_ptree( GtkWidget *, gpointer);
+void prepare_selected_cb_or_ptree( GtkWidget *, gpointer);
+void prepare_selected_cb_not_ptree( GtkWidget *, gpointer);
+void prepare_selected_cb_and_ptree_not( GtkWidget *, gpointer);
+void prepare_selected_cb_or_ptree_not( GtkWidget *, gpointer);
+void match_selected_cb_replace_plist( GtkWidget *, gpointer);
+void match_selected_cb_and_plist( GtkWidget *, gpointer);
+void match_selected_cb_or_plist( GtkWidget *, gpointer);
+void match_selected_cb_not_plist( GtkWidget *, gpointer);
+void match_selected_cb_and_plist_not( GtkWidget *, gpointer);
+void match_selected_cb_or_plist_not( GtkWidget *, gpointer);
+void prepare_selected_cb_replace_plist( GtkWidget *, gpointer);
+void prepare_selected_cb_and_plist( GtkWidget *, gpointer);
+void prepare_selected_cb_or_plist( GtkWidget *, gpointer);
+void prepare_selected_cb_not_plist( GtkWidget *, gpointer);
+void prepare_selected_cb_and_plist_not( GtkWidget *, gpointer);
+void prepare_selected_cb_or_plist_not( GtkWidget *, gpointer);
+void file_quit_cmd_cb(GtkWidget *, gpointer);
+void file_print_cmd_cb(GtkWidget *, gpointer);
+void file_print_packet_cmd_cb(GtkWidget *, gpointer);
+void tools_plugins_cmd_cb(GtkWidget *, gpointer);
+void expand_all_cb(GtkWidget *, gpointer);
+void collapse_all_cb(GtkWidget *, gpointer);
+void resolve_name_cb(GtkWidget *, gpointer);
+void mark_frame_cb(GtkWidget *, gpointer);
+void mark_all_frames_cb(GtkWidget *w, gpointer);
+void unmark_all_frames_cb(GtkWidget *w, gpointer);
+void update_marked_frames(void);
+
+char *boldify(const char *);
+void set_fonts(PangoFontDescription *regular, PangoFontDescription *bold);
+void set_last_open_dir(char *dirname);
+
+#endif /* __MAIN_H__ */
diff --git a/gtk2/menu.c b/gtk2/menu.c
new file mode 100644
index 0000000000..5b109440cc
--- /dev/null
+++ b/gtk2/menu.c
@@ -0,0 +1,527 @@
+/* menu.c
+ * Menu routines
+ *
+ * $Id: menu.c,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <glib.h>
+
+#include <string.h>
+#include <stdio.h>
+
+#include "../menu.h"
+
+#include "main.h"
+#include "menu.h"
+#include <epan/packet.h>
+#include <epan/resolv.h>
+#include "prefs.h"
+#include "capture_dlg.h"
+#include "color_dlg.h"
+#include "file_dlg.h"
+#include "filter_prefs.h"
+#include "find_dlg.h"
+#include "goto_dlg.h"
+#include "summary_dlg.h"
+#include "display_opts.h"
+#include "prefs_dlg.h"
+#include "packet_win.h"
+#include "print.h"
+#include "follow_dlg.h"
+#include "decode_as_dlg.h"
+#include "help_dlg.h"
+#include "proto_dlg.h"
+#include "proto_hier_stats_dlg.h"
+#include "keys.h"
+#include <epan/plugins.h>
+#include "tcp_graph.h"
+#include <epan/epan_dissect.h>
+
+GtkWidget *popup_menu_object;
+
+#define GTK_MENU_FUNC(a) ((GtkItemFactoryCallback)(a))
+
+static void menus_init(void);
+static void set_menu_sensitivity (gchar *, gint);
+
+/* This is the GtkItemFactoryEntry structure used to generate new menus.
+ Item 1: The menu path. The letter after the underscore indicates an
+ accelerator key once the menu is open.
+ Item 2: The accelerator key for the entry
+ Item 3: The callback function.
+ Item 4: The callback action. This changes the parameters with
+ which the function is called. The default is 0.
+ Item 5: The item type, used to define what kind of an item it is.
+ Here are the possible values:
+
+ NULL -> "<Item>"
+ "" -> "<Item>"
+ "<Title>" -> create a title item
+ "<Item>" -> create a simple item
+ "<CheckItem>" -> create a check item
+ "<ToggleItem>" -> create a toggle item
+ "<RadioItem>" -> create a radio item
+ <path> -> path of a radio item to link against
+ "<Separator>" -> create a separator
+ "<Branch>" -> create an item to hold sub items (optional)
+ "<LastBranch>" -> create a right justified branch
+ */
+
+/* main menu */
+static GtkItemFactoryEntry menu_items[] =
+{
+ {"/_File", NULL, NULL, 0, "<Branch>", NULL },
+ {"/File/_Open...", "<control>O", GTK_MENU_FUNC(file_open_cmd_cb), 0, "<StockItem>", GTK_STOCK_OPEN },
+ {"/File/_Close", "<control>W", GTK_MENU_FUNC(file_close_cmd_cb), 0, "<StockItem>", GTK_STOCK_CLOSE },
+ {"/File/_Save", "<control>S", GTK_MENU_FUNC(file_save_cmd_cb), 0, "<StockItem>", GTK_STOCK_SAVE },
+ {"/File/Save _As...", NULL, GTK_MENU_FUNC(file_save_as_cmd_cb), 0, "<StockItem>", GTK_STOCK_SAVE_AS },
+ {"/File/_Reload", "<control>R", GTK_MENU_FUNC(file_reload_cmd_cb), 0, "<StockItem>", GTK_STOCK_REFRESH },
+ {"/File/<separator>", NULL, NULL, 0, "<Separator>", NULL },
+ {"/File/_Print...", NULL, GTK_MENU_FUNC(file_print_cmd_cb), 0, "<StockItem>", GTK_STOCK_PRINT },
+ {"/File/Print Pac_ket", "<control>P", GTK_MENU_FUNC(file_print_packet_cmd_cb), 0, NULL, NULL },
+ {"/File/<separator>", NULL, NULL, 0, "<Separator>", NULL },
+ {"/File/_Quit", "<control>Q", GTK_MENU_FUNC(file_quit_cmd_cb), 0, "<StockItem>", GTK_STOCK_QUIT },
+ {"/_Edit", NULL, NULL, 0, "<Branch>", NULL },
+#if 0
+ /* Un-#if this when we actually implement Cut/Copy/Paste. */
+ {"/Edit/Cut", "<control>X", NULL, 0, NULL},
+ {"/Edit/Copy", "<control>C", NULL, 0, NULL},
+ {"/Edit/Paste", "<control>V", NULL, 0, NULL},
+ {"/Edit/<separator>", NULL, NULL, 0, "<Separator>"},
+#endif
+ {"/Edit/_Find Frame...", "<control>F", GTK_MENU_FUNC(find_frame_cb), 0, "<StockItem>", GTK_STOCK_FIND },
+ {"/Edit/Find _Next", "<control>N", GTK_MENU_FUNC(find_next_cb), 0, "<StockItem>", GTK_STOCK_GO_FORWARD},
+ {"/Edit/Find _Previous", "<control>B", GTK_MENU_FUNC(find_previous_cb), 0, "<StockItem>", GTK_STOCK_GO_BACK},
+ {"/Edit/_Go To Frame...", "<control>G", GTK_MENU_FUNC(goto_frame_cb), 0, "<StockItem>", GTK_STOCK_JUMP_TO },
+ {"/Edit/<separator>", NULL, NULL, 0, "<Separator>", NULL },
+ {"/Edit/_Mark Frame", "<control>M", GTK_MENU_FUNC(mark_frame_cb), 0, NULL, NULL },
+ {"/Edit/Mark _All Frames", NULL, GTK_MENU_FUNC(mark_all_frames_cb), 0, NULL, NULL },
+ {"/Edit/_Unmark All Frames", NULL, GTK_MENU_FUNC(unmark_all_frames_cb), 0, NULL, NULL },
+ {"/Edit/<separator>", NULL, NULL, 0, "<Separator>", NULL },
+ {"/Edit/_Preferences...", NULL, GTK_MENU_FUNC(prefs_cb), 0, "<StockItem>", GTK_STOCK_PREFERENCES },
+#ifdef HAVE_LIBPCAP
+ {"/Edit/_Capture Filters...", NULL, GTK_MENU_FUNC(cfilter_dialog_cb), 0, NULL, NULL },
+#endif
+ {"/Edit/_Display Filters...", NULL, GTK_MENU_FUNC(dfilter_dialog_cb), 0, NULL, NULL },
+ {"/Edit/P_rotocols...", NULL, GTK_MENU_FUNC(proto_cb), 0, NULL, NULL },
+#ifdef HAVE_LIBPCAP
+ {"/_Capture", NULL, NULL, 0, "<Branch>", NULL },
+ {"/Capture/_Start...", "<control>K", GTK_MENU_FUNC(capture_prep_cb), 0, "<StockItem>", GTK_STOCK_EXECUTE },
+ /*
+ * XXX - this doesn't yet work in Win32.
+ */
+#ifndef _WIN32
+ {"/Capture/S_top", "<control>E", GTK_MENU_FUNC(capture_stop_cb), 0, "<StockItem>", GTK_STOCK_STOP },
+#endif /* _WIN32 */
+#endif /* HAVE_LIBPCAP */
+ {"/_Display", NULL, NULL, 0, "<Branch>", NULL },
+ {"/Display/_Options...", NULL, GTK_MENU_FUNC(display_opt_cb), 0, NULL, NULL },
+ {"/Display/_Match", NULL, NULL, 0, "<Branch>", NULL },
+ {"/Display/Match/_Selected", NULL, GTK_MENU_FUNC(match_selected_cb_replace_ptree), 0, NULL, NULL },
+ {"/Display/Match/_Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_not_ptree), 0, NULL, NULL },
+ {"/Display/Match/_And Selected", NULL, GTK_MENU_FUNC(match_selected_cb_and_ptree), 0, NULL, NULL },
+ {"/Display/Match/_Or Selected", NULL, GTK_MENU_FUNC(match_selected_cb_or_ptree), 0, NULL, NULL },
+ {"/Display/Match/A_nd Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_and_ptree_not), 0, NULL, NULL },
+ {"/Display/Match/O_r Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_or_ptree_not), 0, NULL, NULL },
+ {"/Display/_Prepare", NULL, NULL, 0, "<Branch>", NULL },
+ {"/Display/Prepare/_Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_replace_ptree), 0, NULL, NULL },
+ {"/Display/Prepare/_Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_not_ptree), 0, NULL, NULL },
+ {"/Display/Prepare/_And Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_and_ptree), 0, NULL, NULL },
+ {"/Display/Prepare/_Or Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_or_ptree), 0, NULL, NULL },
+ {"/Display/Prepare/A_nd Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_and_ptree_not), 0, NULL, NULL },
+ {"/Display/Prepare/O_r Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_or_ptree_not), 0, NULL, NULL },
+ {"/Display/_Colorize Display...", NULL, GTK_MENU_FUNC(color_display_cb), 0, NULL, NULL },
+ {"/Display/Collapse _All", NULL, GTK_MENU_FUNC(collapse_all_cb), 0, NULL, NULL },
+ {"/Display/_Expand All", NULL, GTK_MENU_FUNC(expand_all_cb), 0, NULL, NULL },
+ {"/Display/_Show Packet In New Window", NULL, GTK_MENU_FUNC(new_window_cb), 0, NULL, NULL },
+ {"/Display/User Specified Decodes...", NULL, GTK_MENU_FUNC(decode_show_cb), 0, NULL, NULL },
+ {"/_Tools", NULL, NULL, 0, "<Branch>", NULL },
+#ifdef HAVE_PLUGINS
+ {"/Tools/_Plugins...", NULL, GTK_MENU_FUNC(tools_plugins_cmd_cb), 0, NULL, NULL },
+#endif
+ {"/Tools/_Follow TCP Stream", NULL, GTK_MENU_FUNC(follow_stream_cb), 0, NULL, NULL },
+ {"/Tools/_Decode As...", NULL, GTK_MENU_FUNC(decode_as_cb), 0, NULL, NULL },
+/* {"/Tools/Graph", NULL, NULL, 0, NULL}, future use */
+ {"/_Tools/TCP Stream Analysis", NULL, NULL, 0, "<Branch>", NULL },
+ {"/_Tools/TCP Stream Analysis/Time-Sequence Graph (Stevens)", NULL, GTK_MENU_FUNC (tcp_graph_cb), 0, NULL, NULL },
+ {"/_Tools/TCP Stream Analysis/Time-Sequence Graph (tcptrace)", NULL, GTK_MENU_FUNC (tcp_graph_cb), 1, NULL, NULL },
+ {"/_Tools/TCP Stream Analysis/Throughput Graph", NULL, GTK_MENU_FUNC (tcp_graph_cb), 2, NULL, NULL },
+ {"/_Tools/TCP Stream Analysis/RTT Graph", NULL, GTK_MENU_FUNC (tcp_graph_cb), 3, NULL, NULL },
+ {"/Tools/_Summary", NULL, GTK_MENU_FUNC(summary_open_cb), 0, NULL, NULL },
+ {"/Tools/Protocol Hierarchy Statistics", NULL, GTK_MENU_FUNC(proto_hier_stats_cb), 0, NULL, NULL },
+ {"/_Help", NULL, NULL, 0, "<LastBranch>", NULL },
+ {"/Help/_Help", NULL, GTK_MENU_FUNC(help_cb), 0, "<StockItem>", GTK_STOCK_HELP },
+ {"/Help/<separator>", NULL, NULL, 0, "<Separator>", NULL },
+ {"/Help/_About Ethereal...", NULL, GTK_MENU_FUNC(about_ethereal), 0, NULL, NULL }
+};
+
+/* calculate the number of menu_items */
+static int nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
+
+/* packet list popup */
+static GtkItemFactoryEntry packet_list_menu_items[] =
+{
+ {"/Follow TCP Stream", NULL, GTK_MENU_FUNC(follow_stream_cb), 0, NULL, NULL },
+ {"/Decode As...", NULL, GTK_MENU_FUNC(decode_as_cb), 0, NULL, NULL },
+ {"/Display Filters...", NULL, GTK_MENU_FUNC(dfilter_dialog_cb), 0, NULL, NULL },
+ {"/<separator>", NULL, NULL, 0, "<Separator>", NULL },
+ {"/Mark Frame", NULL, GTK_MENU_FUNC(mark_frame_cb), 0, NULL, NULL },
+ {"/Match", NULL, NULL, 0, "<Branch>", NULL },
+ {"/Match/_Selected", NULL, GTK_MENU_FUNC(match_selected_cb_replace_plist), 0, NULL, NULL },
+ {"/Match/_Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_not_plist), 0, NULL, NULL },
+ {"/Match/_And Selected", NULL, GTK_MENU_FUNC(match_selected_cb_and_plist), 0, NULL, NULL },
+ {"/Match/_Or Selected", NULL, GTK_MENU_FUNC(match_selected_cb_or_plist), 0, NULL, NULL },
+ {"/Match/A_nd Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_and_plist_not), 0, NULL, NULL },
+ {"/Match/O_r Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_or_plist_not), 0, NULL, NULL },
+ {"/Prepare", NULL, NULL, 0, "<Branch>", NULL },
+ {"/Prepare/_Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_replace_plist), 0, NULL, NULL },
+ {"/Prepare/_Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_not_plist), 0, NULL, NULL },
+ {"/Prepare/_And Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_and_plist), 0, NULL, NULL },
+ {"/Prepare/_Or Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_or_plist), 0, NULL, NULL },
+ {"/Prepare/A_nd Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_and_plist_not), 0, NULL, NULL },
+ {"/Prepare/O_r Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_or_plist_not), 0, NULL, NULL },
+ {"/<separator>", NULL, NULL, 0, "<Separator>", NULL },
+ {"/Colorize Display...", NULL, GTK_MENU_FUNC(color_display_cb), 0, NULL, NULL },
+ {"/Print...", NULL, GTK_MENU_FUNC(file_print_cmd_cb), 0, NULL, NULL },
+ {"/Print Packet", NULL, GTK_MENU_FUNC(file_print_packet_cmd_cb), 0, NULL, NULL },
+ {"/Show Packet In New Window", NULL, GTK_MENU_FUNC(new_window_cb), 0, NULL, NULL },
+};
+
+static GtkItemFactoryEntry tree_view_menu_items[] =
+{
+ {"/Follow TCP Stream", NULL, GTK_MENU_FUNC(follow_stream_cb), 0, NULL, NULL },
+ {"/Decode As...", NULL, GTK_MENU_FUNC(decode_as_cb), 0, NULL, NULL },
+ {"/Display Filters...", NULL, GTK_MENU_FUNC(dfilter_dialog_cb), 0, NULL, NULL },
+ {"/<separator>", NULL, NULL, 0, "<Separator>", NULL },
+ {"/Resolve Name", NULL, GTK_MENU_FUNC(resolve_name_cb), 0, NULL, NULL },
+ {"/Protocol Properties...", NULL, GTK_MENU_FUNC(properties_cb), 0, NULL, NULL },
+ {"/Match", NULL, NULL, 0, "<Branch>", NULL },
+ {"/Match/_Selected", NULL, GTK_MENU_FUNC(match_selected_cb_replace_ptree), 0, NULL, NULL },
+ {"/Match/_Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_not_ptree), 0, NULL, NULL },
+ {"/Match/_And Selected", NULL, GTK_MENU_FUNC(match_selected_cb_and_ptree), 0, NULL, NULL },
+ {"/Match/_Or Selected", NULL, GTK_MENU_FUNC(match_selected_cb_or_ptree), 0, NULL, NULL },
+ {"/Match/A_nd Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_and_ptree_not), 0, NULL, NULL },
+ {"/Match/O_r Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_or_ptree_not), 0, NULL, NULL },
+ {"/Prepare", NULL, NULL, 0, "<Branch>", NULL },
+ {"/Prepare/_Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_replace_ptree), 0, NULL, NULL },
+ {"/Prepare/_Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_not_ptree), 0, NULL, NULL },
+ {"/Prepare/_And Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_and_ptree), 0, NULL, NULL },
+ {"/Prepare/_Or Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_or_ptree), 0, NULL, NULL },
+ {"/Prepare/A_nd Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_and_ptree_not), 0, NULL, NULL },
+ {"/Prepare/O_r Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_or_ptree_not), 0, NULL, NULL },
+ {"/<separator>", NULL, NULL, 0, "<Separator>", NULL },
+ {"/Collapse All", NULL, GTK_MENU_FUNC(collapse_all_cb), 0, NULL, NULL },
+ {"/Expand All", NULL, GTK_MENU_FUNC(expand_all_cb), 0, NULL, NULL }
+};
+
+static GtkItemFactoryEntry hexdump_menu_items[] =
+{
+ {"/Follow TCP Stream", NULL, GTK_MENU_FUNC(follow_stream_cb), 0, NULL, NULL },
+ {"/Decode As...", NULL, GTK_MENU_FUNC(decode_as_cb), 0, NULL, NULL },
+ {"/Display Filters...", NULL, GTK_MENU_FUNC(dfilter_dialog_cb), 0, NULL, NULL }
+};
+
+static int initialize = TRUE;
+static GtkItemFactory *factory = NULL;
+static GtkItemFactory *packet_list_menu_factory = NULL;
+static GtkItemFactory *tree_view_menu_factory = NULL;
+static GtkItemFactory *hexdump_menu_factory = NULL;
+
+static GSList *popup_menu_list = NULL;
+
+static GtkAccelGroup *grp;
+
+void
+get_main_menu(GtkWidget ** menubar, GtkAccelGroup ** table) {
+
+ grp = gtk_accel_group_new();
+
+ if (initialize) {
+ popup_menu_object = gtk_widget_new(GTK_TYPE_WIDGET, NULL);
+ menus_init();
+ }
+
+ if (menubar)
+ *menubar = factory->widget;
+
+ if (table)
+ *table = grp;
+}
+
+static void
+menus_init(void) {
+
+ if (initialize) {
+ initialize = FALSE;
+
+ /* popup */
+
+ packet_list_menu_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
+ gtk_item_factory_create_items_ac(packet_list_menu_factory, sizeof(packet_list_menu_items)/sizeof(packet_list_menu_items[0]), packet_list_menu_items, popup_menu_object, 2);
+ gtk_object_set_data(GTK_OBJECT(popup_menu_object), PM_PACKET_LIST_KEY, packet_list_menu_factory->widget);
+ popup_menu_list = g_slist_append((GSList *)popup_menu_list, packet_list_menu_factory);
+
+ tree_view_menu_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
+ gtk_item_factory_create_items_ac(tree_view_menu_factory, sizeof(tree_view_menu_items)/sizeof(tree_view_menu_items[0]), tree_view_menu_items, popup_menu_object, 2);
+ gtk_object_set_data(GTK_OBJECT(popup_menu_object), PM_TREE_VIEW_KEY, tree_view_menu_factory->widget);
+ popup_menu_list = g_slist_append((GSList *)popup_menu_list, tree_view_menu_factory);
+
+ hexdump_menu_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
+ gtk_item_factory_create_items_ac(hexdump_menu_factory, sizeof(hexdump_menu_items)/sizeof(hexdump_menu_items[0]), hexdump_menu_items, popup_menu_object, 2);
+ gtk_object_set_data(GTK_OBJECT(popup_menu_object), PM_HEXDUMP_KEY, hexdump_menu_factory->widget);
+ popup_menu_list = g_slist_append((GSList *)popup_menu_list, hexdump_menu_factory);
+
+ factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<main>", grp);
+ gtk_item_factory_create_items_ac(factory, nmenu_items, menu_items, NULL,2);
+ set_menus_for_unsaved_capture_file(FALSE);
+ set_menus_for_capture_file(FALSE);
+#if 0
+ /* Un-#if this when we actually implement Cut/Copy/Paste.
+ Then make sure you enable them when they can be done. */
+ set_menu_sensitivity("/Edit/Cut", FALSE);
+ set_menu_sensitivity("/Edit/Copy", FALSE);
+ set_menu_sensitivity("/Edit/Paste", FALSE);
+#endif
+ set_menus_for_captured_packets(FALSE);
+ set_menus_for_selected_packet(FALSE);
+ set_menus_for_selected_tree_row(FALSE);
+ }
+}
+
+void
+set_menu_sensitivity_meat(GtkItemFactory *ifactory, gchar *path, gint val) {
+ GtkWidget *menu = NULL;
+
+ if((menu = gtk_item_factory_get_widget(ifactory, path)) != NULL) {
+ gtk_widget_set_sensitive(menu,val);
+ }
+}
+
+/* Enable/disable menu sensitivity */
+/* /menu/path - old functionality */
+/* <MenuName>/menu/path - new functionality */
+/* MenuName: <Main>, <PacketList>, <TreeView>, <HexDump> */
+static void
+set_menu_sensitivity (gchar *path, gint val) {
+ GSList *menu_list = popup_menu_list;
+ gchar *prefix;
+ gchar *shortpath;
+
+ if ('<' == *path) {
+ /* New functionality => selective enable/disable per menu */
+ prefix=strchr(path, '/');
+ shortpath=strrchr(prefix, '/');
+
+ if (0 == strncmp(path, "<Main>", 6))
+ set_menu_sensitivity_meat(factory, prefix, val);
+ else if (0 == strncmp(path, "<PacketList>", 12))
+ set_menu_sensitivity_meat(packet_list_menu_factory, shortpath, val);
+ else if (0 == strncmp(path, "<TreeView>", 10))
+ set_menu_sensitivity_meat(tree_view_menu_factory, shortpath, val);
+ else if (0 == strncmp(path, "<HexDump>", 9))
+ set_menu_sensitivity_meat(hexdump_menu_factory, shortpath, val);
+ } else {
+ /* Old functionality => enable/disable all menus with same shortpath */
+ shortpath = strrchr(path, '/');
+
+ set_menu_sensitivity_meat(factory, path, val);
+
+ while (menu_list != NULL) {
+ set_menu_sensitivity_meat(menu_list->data, shortpath, val);
+ menu_list = g_slist_next(menu_list);
+ }
+ }
+}
+
+void
+set_menu_object_data_meat(GtkItemFactory *ifactory, gchar *path, gchar *key, gpointer data)
+{
+ GtkWidget *menu = NULL;
+
+ if ((menu = gtk_item_factory_get_widget(ifactory, path)) != NULL)
+ gtk_object_set_data(GTK_OBJECT(menu), key, data);
+}
+
+void
+set_menu_object_data (gchar *path, gchar *key, gpointer data) {
+ GSList *menu_list = popup_menu_list;
+ gchar *shortpath = strrchr(path, '/');
+
+ set_menu_object_data_meat(factory, path, key, data);
+ while (menu_list != NULL) {
+ set_menu_object_data_meat(menu_list->data, shortpath, key, data);
+ menu_list = g_slist_next(menu_list);
+ }
+}
+
+gint
+popup_menu_handler(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ GtkWidget *menu = NULL;
+ GdkEventButton *event_button = NULL;
+ GtkCList *packet_list = NULL;
+ gint row, column;
+
+ if(widget == NULL || event == NULL || data == NULL) {
+ return FALSE;
+ }
+
+ /*
+ * If we ever want to make the menu differ based on what row
+ * and/or column we're above, we'd use "gtk_clist_get_selection_info()"
+ * to find the row and column number for the coordinates; a CTree is,
+ * I guess, like a CList with one column(?) and the expander widget
+ * as a pixmap.
+ */
+ /* Check if we are on packet_list object */
+ if (widget == gtk_object_get_data(GTK_OBJECT(popup_menu_object),
+ E_MPACKET_LIST_KEY)) {
+ packet_list=GTK_CLIST(widget);
+ if (gtk_clist_get_selection_info(GTK_CLIST(packet_list),
+ ((GdkEventButton *)event)->x,
+ ((GdkEventButton *)event)->y,&row,&column)) {
+ gtk_object_set_data(GTK_OBJECT(popup_menu_object),
+ E_MPACKET_LIST_ROW_KEY, (gpointer *)row);
+ gtk_object_set_data(GTK_OBJECT(popup_menu_object),
+ E_MPACKET_LIST_COL_KEY, (gpointer *)column);
+ }
+ }
+ menu = (GtkWidget *)data;
+ if(event->type == GDK_BUTTON_PRESS) {
+ event_button = (GdkEventButton *) event;
+
+ if(event_button->button == 3) {
+ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event_button->button, event_button->time);
+ gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "button_press_event");
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/* Enable or disable menu items based on whether you have a capture file
+ you've finished reading. */
+void
+set_menus_for_capture_file(gboolean have_capture_file)
+{
+ set_menu_sensitivity("/File/Open...", have_capture_file);
+ set_menu_sensitivity("/File/Save As...", have_capture_file);
+ set_menu_sensitivity("/File/Close", have_capture_file);
+ set_menu_sensitivity("/File/Reload", have_capture_file);
+}
+
+/* Enable or disable menu items based on whether you have an unsaved
+ capture file you've finished reading. */
+void
+set_menus_for_unsaved_capture_file(gboolean have_unsaved_capture_file)
+{
+ set_menu_sensitivity("/File/Save", have_unsaved_capture_file);
+}
+
+/* Enable or disable menu items based on whether there's a capture in
+ progress. */
+void
+set_menus_for_capture_in_progress(gboolean capture_in_progress)
+{
+ set_menu_sensitivity("/File/Open...", !capture_in_progress);
+ set_menu_sensitivity("/Capture/Start...", !capture_in_progress);
+ /*
+ * XXX - this doesn't yet work in Win32.
+ */
+#ifndef _WIN32
+ set_menu_sensitivity("/Capture/Stop", capture_in_progress);
+#endif
+}
+
+/* Enable or disable menu items based on whether you have some captured
+ packets. */
+void
+set_menus_for_captured_packets(gboolean have_captured_packets)
+{
+ set_menu_sensitivity("/File/Print...", have_captured_packets);
+ set_menu_sensitivity("/Edit/Find Frame...", have_captured_packets);
+ set_menu_sensitivity("/Edit/Find Next", have_captured_packets);
+ set_menu_sensitivity("/Edit/Find Previous", have_captured_packets);
+ set_menu_sensitivity("/Edit/Go To Frame...", have_captured_packets);
+ set_menu_sensitivity("/Display/Colorize Display...", have_captured_packets);
+ set_menu_sensitivity("/Tools/Summary", have_captured_packets);
+ set_menu_sensitivity("/Tools/Protocol Hierarchy Statistics", have_captured_packets);
+ set_menu_sensitivity("<PacketList>/Display/Match", have_captured_packets);
+ set_menu_sensitivity("<PacketList>/Display/Prepare", have_captured_packets);
+}
+
+/* Enable or disable menu items based on whether a packet is selected. */
+void
+set_menus_for_selected_packet(gboolean have_selected_packet)
+{
+ set_menu_sensitivity("/File/Print Packet", have_selected_packet);
+ set_menu_sensitivity("/Edit/Mark Frame", have_selected_packet);
+ set_menu_sensitivity("/Edit/Mark All Frames", have_selected_packet);
+ set_menu_sensitivity("/Edit/Unmark All Frames", have_selected_packet);
+ set_menu_sensitivity("/Display/Collapse All", have_selected_packet);
+ set_menu_sensitivity("/Display/Expand All", have_selected_packet);
+ set_menu_sensitivity("/Display/Show Packet In New Window", have_selected_packet);
+ set_menu_sensitivity("/Tools/Follow TCP Stream",
+ have_selected_packet ? (cfile.edt->pi.ipproto == 6) : FALSE);
+ set_menu_sensitivity("/Tools/Decode As...",
+ have_selected_packet && decode_as_ok());
+ set_menu_sensitivity("/Resolve Name",
+ have_selected_packet && g_resolv_flags == 0);
+ set_menu_sensitivity("/Tools/TCP Stream Analysis",
+ have_selected_packet ? (cfile.edt->pi.ipproto == 6) : FALSE);
+}
+
+/* Enable or disable menu items based on whether a tree row is selected
+ and and on whether a "Match" can be done. */
+void
+set_menus_for_selected_tree_row(gboolean have_selected_tree)
+{
+ gboolean properties = FALSE;
+
+ if (finfo_selected) {
+ header_field_info *hfinfo = finfo_selected->hfinfo;
+ if (hfinfo->parent == -1) {
+ properties = prefs_is_registered_protocol(hfinfo->abbrev);
+ } else {
+ properties = prefs_is_registered_protocol(proto_registrar_get_abbrev(hfinfo->parent));
+ }
+ set_menu_sensitivity("<Main>/Display/Match",
+ proto_can_match_selected(finfo_selected));
+ set_menu_sensitivity("<TreeView>/Display/Match",
+ proto_can_match_selected(finfo_selected));
+ set_menu_sensitivity("<Main>/Display/Prepare",
+ proto_can_match_selected(finfo_selected));
+ set_menu_sensitivity("<TreeView>/Display/Prepare",
+ proto_can_match_selected(finfo_selected));
+ } else {
+ set_menu_sensitivity("<Main>/Display/Match", FALSE);
+ set_menu_sensitivity("<TreeView>/Display/Match", FALSE);
+ set_menu_sensitivity("<Main>/Display/Prepare", FALSE);
+ set_menu_sensitivity("<TreeView>/Display/Prepare", FALSE);
+ }
+
+ set_menu_sensitivity("/Protocol Properties...", have_selected_tree && properties);
+}
diff --git a/gtk2/menu.h b/gtk2/menu.h
new file mode 100644
index 0000000000..af3f732198
--- /dev/null
+++ b/gtk2/menu.h
@@ -0,0 +1,43 @@
+/* menu.h
+ * Menu definitions
+ *
+ * $Id: menu.h,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+#ifndef __GTKGUIMENU_H__
+#define __GTKGUIMENU_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void get_main_menu (GtkWidget **, GtkAccelGroup **);
+void set_menu_object_data (gchar *path, gchar *key, gpointer data);
+gint popup_menu_handler(GtkWidget *widget, GdkEvent *event, gpointer data);
+
+extern GtkWidget *popup_menu_object;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GTKGUIMENU_H__ */
diff --git a/gtk2/nameres_prefs.c b/gtk2/nameres_prefs.c
new file mode 100644
index 0000000000..11e5c03746
--- /dev/null
+++ b/gtk2/nameres_prefs.c
@@ -0,0 +1,129 @@
+/* nameres_prefs.c
+ * Dialog box for name resolution preferences
+ *
+ * $Id: nameres_prefs.c,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <gtk/gtk.h>
+
+#include "globals.h"
+#include "nameres_prefs.h"
+#include "gtkglobals.h"
+#include <epan/resolv.h>
+#include "prefs.h"
+#include "prefs_dlg.h"
+#include "ui_util.h"
+#include "main.h"
+
+#define M_RESOLVE_KEY "m_resolve"
+#define N_RESOLVE_KEY "n_resolve"
+#define T_RESOLVE_KEY "t_resolve"
+
+#define RESOLV_TABLE_ROWS 3
+GtkWidget*
+nameres_prefs_show(void)
+{
+ GtkWidget *main_tb, *main_vb;
+ GtkWidget *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
+
+ /*
+ * XXX - it would be nice if the current setting of the resolver
+ * flags could be different from the preference flags, so that
+ * the preference flags would represent what the user *typically*
+ * wants, but they could override them for particular captures
+ * without a subsequent editing of the preferences recording the
+ * temporary settings as permanent preferences.
+ */
+ prefs.name_resolve = g_resolv_flags;
+
+ /* Main vertical box */
+ main_vb = gtk_vbox_new(FALSE, 7);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+
+ /* Main table */
+ main_tb = gtk_table_new(RESOLV_TABLE_ROWS, 3, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
+ gtk_widget_show(main_tb);
+
+ /* Resolve MAC addresses */
+ m_resolv_cb = create_preference_check_button(main_tb, 0,
+ "Enable MAC name resolution:", NULL,
+ prefs.name_resolve & RESOLV_MAC);
+ gtk_object_set_data(GTK_OBJECT(main_vb), M_RESOLVE_KEY, m_resolv_cb);
+
+ /* Resolve network addresses */
+ n_resolv_cb = create_preference_check_button(main_tb, 1,
+ "Enable network name resolution:", NULL,
+ prefs.name_resolve & RESOLV_NETWORK);
+ gtk_object_set_data(GTK_OBJECT(main_vb), N_RESOLVE_KEY, n_resolv_cb);
+
+ /* Resolve transport addresses */
+ t_resolv_cb = create_preference_check_button(main_tb, 2,
+ "Enable transport name resolution:", NULL,
+ prefs.name_resolve & RESOLV_TRANSPORT);
+ gtk_object_set_data(GTK_OBJECT(main_vb), T_RESOLVE_KEY, t_resolv_cb);
+
+ /* Show 'em what we got */
+ gtk_widget_show_all(main_vb);
+
+ return(main_vb);
+}
+
+void
+nameres_prefs_fetch(GtkWidget *w)
+{
+ GtkWidget *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
+
+ m_resolv_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(w),
+ M_RESOLVE_KEY);
+ n_resolv_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(w),
+ N_RESOLVE_KEY);
+ t_resolv_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(w),
+ T_RESOLVE_KEY);
+
+ prefs.name_resolve = RESOLV_NONE;
+ prefs.name_resolve |= (GTK_TOGGLE_BUTTON (m_resolv_cb)->active ? RESOLV_MAC : RESOLV_NONE);
+ prefs.name_resolve |= (GTK_TOGGLE_BUTTON (n_resolv_cb)->active ? RESOLV_NETWORK : RESOLV_NONE);
+ prefs.name_resolve |= (GTK_TOGGLE_BUTTON (t_resolv_cb)->active ? RESOLV_TRANSPORT : RESOLV_NONE);
+}
+
+void
+nameres_prefs_apply(GtkWidget *w _U_)
+{
+ /*
+ * XXX - force a regeneration of the protocol list if this has
+ * changed?
+ */
+ g_resolv_flags = prefs.name_resolve;
+}
+
+void
+nameres_prefs_destroy(GtkWidget *w _U_)
+{
+}
diff --git a/gtk2/nameres_prefs.h b/gtk2/nameres_prefs.h
new file mode 100644
index 0000000000..78513ef159
--- /dev/null
+++ b/gtk2/nameres_prefs.h
@@ -0,0 +1,33 @@
+/* nameres_prefs.h
+ * Definitions for name resolution preferences window
+ *
+ * $Id: nameres_prefs.h,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifndef __NAMERES_PREFS_H__
+#define __NAMERES_PREFS_H__
+
+GtkWidget *nameres_prefs_show(void);
+void nameres_prefs_fetch(GtkWidget *w);
+void nameres_prefs_apply(GtkWidget *w);
+void nameres_prefs_destroy(GtkWidget *w);
+
+#endif
diff --git a/gtk2/packet_win.c b/gtk2/packet_win.c
new file mode 100644
index 0000000000..e1cffbdef3
--- /dev/null
+++ b/gtk2/packet_win.c
@@ -0,0 +1,263 @@
+/* packet_win.c
+ * Routines for popping a window to display current packet
+ *
+ * Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com>
+ *
+ * $Id: packet_win.c,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ *
+ * To do:
+ * - Add close button to bottom.
+ * - improve the window Title and allow user to config it
+ * - Add print support ? ( could be a mess)
+ * - Add button to have main window jump to this packet ?
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <epan/epan.h>
+#include "main.h"
+#include <epan/timestamp.h>
+#include <epan/packet.h>
+#include "summary.h"
+#include "file.h"
+#include "prefs.h"
+#include "menu.h"
+#include "../menu.h"
+#include "column.h"
+#include "print.h"
+#include <epan/resolv.h>
+#include "packet_win.h"
+#include "simple_dialog.h"
+#include "proto_draw.h"
+#include "keys.h"
+#include "gtkglobals.h"
+#include <epan/plugins.h>
+#include <epan/epan_dissect.h>
+
+/* Data structure holding information about a packet-detail window. */
+struct PacketWinData {
+ frame_data *frame; /* The frame being displayed */
+ union wtap_pseudo_header pseudo_header; /* Pseudo-header for packet */
+ guint8 *pd; /* Data for packet */
+ GtkWidget *main;
+ GtkWidget *tv_scrollw;
+ GtkWidget *tree_view;
+ GtkWidget *bv_nb_ptr;
+ field_info *finfo_selected;
+ epan_dissect_t *edt;
+};
+
+/* List of all the packet-detail windows popped up. */
+static GList *detail_windows;
+
+static void new_tree_view_selection_changed_cb(GtkTreeSelection *sel,
+ gpointer user_data);
+
+static void destroy_new_window(GtkObject *object, gpointer user_data);
+
+void new_window_cb(GtkWidget *w _U_)
+{
+#define NewWinTitleLen 1000
+ char Title[NewWinTitleLen] = "";
+ char *TextPtr;
+ gint tv_size = 95, bv_size = 75;
+ GtkWidget *main_w, *main_vbox, *pane,
+ *tree_view, *tv_scrollw,
+ *bv_nb_ptr;
+ struct PacketWinData *DataPtr;
+ int i;
+
+ /* Allocate data structure to represent this window. */
+ DataPtr = (struct PacketWinData *) g_malloc(sizeof(struct PacketWinData));
+
+ DataPtr->frame = cfile.current_frame;
+ memcpy(&DataPtr->pseudo_header, &cfile.pseudo_header,
+ sizeof DataPtr->pseudo_header);
+ DataPtr->pd = g_malloc(DataPtr->frame->cap_len);
+ memcpy(DataPtr->pd, cfile.pd, DataPtr->frame->cap_len);
+ DataPtr->edt = epan_dissect_new(TRUE, TRUE);
+ epan_dissect_run(DataPtr->edt, &DataPtr->pseudo_header, DataPtr->pd,
+ DataPtr->frame, &cfile.cinfo);
+ epan_dissect_fill_in_columns(DataPtr->edt);
+
+ main_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+ /*
+ * Build title of window by getting column data constructed when the
+ * frame was dissected.
+ */
+ for (i = 0; i < cfile.cinfo.num_cols; ++i) {
+ TextPtr = cfile.cinfo.col_data[i];
+ if ((strlen(Title) + strlen(TextPtr)) < NewWinTitleLen - 1) {
+ strcat(Title, TextPtr);
+ strcat(Title, " ");
+ }
+ }
+
+ gtk_window_set_title(GTK_WINDOW(main_w), Title);
+ gtk_window_set_default_size(GTK_WINDOW(main_w), DEF_WIDTH, -1);
+
+ /* Container for paned windows */
+ main_vbox = gtk_vbox_new(FALSE, 1);
+ gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
+ gtk_container_add(GTK_CONTAINER(main_w), main_vbox);
+ gtk_widget_show(main_vbox);
+
+ /* Panes for the tree and byte view */
+ pane = gtk_vpaned_new();
+ gtk_paned_gutter_size(GTK_PANED(pane), (GTK_PANED(pane))->handle_size);
+ gtk_container_add(GTK_CONTAINER(main_vbox), pane);
+ gtk_widget_show(pane);
+
+ /* Tree view */
+ create_tree_view(tv_size, &prefs, pane, &tv_scrollw, &tree_view);
+ gtk_widget_show(tree_view);
+
+ /* Byte view */
+ bv_nb_ptr = create_byte_view(bv_size, pane);
+
+ DataPtr->main = main_w;
+ DataPtr->tv_scrollw = tv_scrollw;
+ DataPtr->tree_view = tree_view;
+ DataPtr->bv_nb_ptr = bv_nb_ptr;
+ detail_windows = g_list_append(detail_windows, DataPtr);
+
+ /* load callback handlers */
+ g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view))),
+ "changed", G_CALLBACK(new_tree_view_selection_changed_cb),
+ DataPtr);
+ g_signal_connect(G_OBJECT(main_w), "destroy",
+ G_CALLBACK(destroy_new_window), DataPtr);
+
+ /* draw the protocol tree & print hex data */
+ add_byte_views(DataPtr->edt, tree_view, DataPtr->bv_nb_ptr);
+ proto_tree_draw(DataPtr->edt->tree, tree_view);
+
+ DataPtr->finfo_selected = NULL;
+ gtk_widget_show(main_w);
+}
+
+static void
+destroy_new_window(GtkObject *object _U_, gpointer user_data)
+{
+ struct PacketWinData *DataPtr = user_data;
+
+ detail_windows = g_list_remove(detail_windows, DataPtr);
+ epan_dissect_free(DataPtr->edt);
+ g_free(DataPtr->pd);
+ g_free(DataPtr);
+}
+
+
+/* called when a tree row is (un)selected in the popup packet window */
+static void
+new_tree_view_selection_changed_cb(GtkTreeSelection *sel, gpointer user_data)
+{
+ field_info *finfo;
+ GtkWidget *byte_view;
+ const guint8 *data;
+ guint len;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ struct PacketWinData *DataPtr = (struct PacketWinData*)user_data;
+
+ /* if something is selected */
+ if (gtk_tree_selection_get_selected(sel, &model, &iter))
+ {
+ gtk_tree_model_get(model, &iter, 1, &finfo, -1);
+ if (!finfo) return;
+
+ set_notebook_page(DataPtr->bv_nb_ptr, finfo->ds_tvb);
+ byte_view = get_notebook_bv_ptr(DataPtr->bv_nb_ptr);
+ if (!byte_view) /* exit if no hex window to write in */
+ return;
+
+ data = get_byte_view_data_and_length(byte_view, &len);
+ if (data == NULL) {
+ data = DataPtr->pd;
+ len = DataPtr->frame->cap_len;
+ }
+
+ DataPtr->finfo_selected = finfo;
+ packet_hex_print(GTK_TEXT_VIEW(byte_view), data,
+ DataPtr->frame, finfo, len);
+ }
+ else
+ {
+ DataPtr->finfo_selected = NULL;
+
+ byte_view = get_notebook_bv_ptr(DataPtr->bv_nb_ptr);
+ if (!byte_view) /* exit if no hex window to write in */
+ return;
+
+ data = get_byte_view_data_and_length(byte_view, &len);
+ g_assert(data != NULL);
+ packet_hex_reprint(GTK_TEXT_VIEW(byte_view));
+ }
+}
+
+/* Functions called from elsewhere to act on all popup packet windows. */
+
+/* Destroy all popup packet windows. */
+void
+destroy_packet_wins(void)
+{
+ struct PacketWinData *DataPtr;
+
+ /* Destroying a packet window causes it to be removed from
+ the list of packet windows, so we can't do a "g_list_foreach()"
+ to go through the list of all packet windows and destroy them
+ as we find them; instead, as long as the list is non-empty,
+ we destroy the first window on the list. */
+ while (detail_windows != NULL) {
+ DataPtr = (struct PacketWinData *)(detail_windows->data);
+ gtk_widget_destroy(DataPtr->main);
+ }
+}
+
+static void
+redraw_hex_dump_cb(gpointer data, gpointer user_data _U_)
+{
+ struct PacketWinData *DataPtr = (struct PacketWinData *)data;
+
+ redraw_hex_dump(DataPtr->bv_nb_ptr, DataPtr->frame, DataPtr->finfo_selected);
+}
+
+/* Redraw the hex dump part of all the popup packet windows. */
+void
+redraw_hex_dump_packet_wins(void)
+{
+ g_list_foreach(detail_windows, redraw_hex_dump_cb, NULL);
+}
diff --git a/gtk2/packet_win.h b/gtk2/packet_win.h
new file mode 100644
index 0000000000..b3b15be1f8
--- /dev/null
+++ b/gtk2/packet_win.h
@@ -0,0 +1,39 @@
+/* packet_win.h
+ * Declarations for popping a window to display current packet
+ *
+ * Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com>
+ *
+ * $Id: packet_win.h,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+#ifndef __PACKET_WIN_H__
+#define __PACKET_WIN_H__
+
+/* Create a new packet window. */
+extern void new_window_cb(GtkWidget *w);
+
+/* Destroy all popup packet windows. */
+void destroy_packet_wins(void);
+
+/* Redraw the hex dump panes of all packet windows. */
+void redraw_hex_dump_packet_wins(void);
+
+#endif
diff --git a/gtk2/plugins_dlg.c b/gtk2/plugins_dlg.c
new file mode 100644
index 0000000000..4e0b958d36
--- /dev/null
+++ b/gtk2/plugins_dlg.c
@@ -0,0 +1,164 @@
+/* plugins_dlg.c
+ * Dialog boxes for plugins
+ *
+ * $Id: plugins_dlg.c,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1999 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+
+#include "globals.h"
+#include <epan/plugins.h>
+#include "dlg_utils.h"
+
+#ifdef HAVE_PLUGINS
+
+static void plugins_close_cb(GtkWidget *, gpointer);
+static void plugins_scan(GtkListStore *);
+
+enum
+{
+ COLUMN_NAME,
+ COLUMN_VERSION,
+ NUM_COLUMNS
+};
+
+void
+tools_plugins_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ GtkWidget *plugins_window;
+ GtkWidget *main_vbox;
+ GtkWidget *main_frame;
+ GtkWidget *frame_hbox;
+ GtkWidget *scrolledwindow;
+ GtkWidget *plugins_list;
+ GtkWidget *frame_vbnbox;
+ GtkWidget *main_hbnbox;
+ GtkWidget *close_bn;
+ gchar *titles[] = {"Name", "Version"};
+ GtkListStore *store;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ plugins_window = dlg_window_new("Ethereal: Plugins");
+
+ main_vbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(plugins_window), main_vbox);
+ gtk_widget_show(main_vbox);
+
+ main_frame = gtk_frame_new("Plugins List");
+ gtk_box_pack_start(GTK_BOX(main_vbox), main_frame, TRUE, TRUE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(main_frame), 10);
+ gtk_widget_show(main_frame);
+
+ frame_hbox = gtk_hbox_new(FALSE,0);
+ gtk_container_add(GTK_CONTAINER(main_frame), frame_hbox);
+ gtk_container_set_border_width(GTK_CONTAINER(frame_hbox), 5);
+ gtk_widget_show(frame_hbox);
+
+ scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(frame_hbox), scrolledwindow, TRUE, TRUE, 0);
+ gtk_widget_set_size_request(scrolledwindow, 400, 150);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_widget_show(scrolledwindow);
+
+ store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
+ plugins_scan(store);
+ plugins_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(plugins_list), TRUE);
+ gtk_tree_view_set_search_column(GTK_TREE_VIEW(plugins_list), 0);
+ g_object_unref(G_OBJECT(store));
+ gtk_container_add(GTK_CONTAINER(scrolledwindow), plugins_list);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text",
+ COLUMN_NAME, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, COLUMN_NAME);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(plugins_list), column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Version", renderer,
+ "text", COLUMN_VERSION,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, COLUMN_VERSION);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(plugins_list), column);
+ gtk_widget_show(plugins_list);
+
+ frame_vbnbox = gtk_vbutton_box_new();
+ gtk_box_pack_start(GTK_BOX(frame_hbox), frame_vbnbox, FALSE, TRUE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(frame_vbnbox), 20);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(frame_vbnbox),
+ GTK_BUTTONBOX_SPREAD);
+ gtk_widget_show(frame_vbnbox);
+
+ main_hbnbox = gtk_hbutton_box_new();
+ gtk_box_pack_start(GTK_BOX(main_vbox), main_hbnbox, FALSE, TRUE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(main_hbnbox), 10);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(main_hbnbox),
+ GTK_BUTTONBOX_SPREAD);
+ gtk_widget_show(main_hbnbox);
+
+ close_bn = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_container_add(GTK_CONTAINER(main_hbnbox), close_bn);
+ gtk_widget_show(close_bn);
+ g_signal_connect(G_OBJECT(close_bn), "clicked",
+ G_CALLBACK(plugins_close_cb), GTK_OBJECT(plugins_window));
+
+ gtk_widget_show(plugins_window);
+
+}
+
+/*
+ * Fill the list widget with a list of the plugin modules.
+ */
+static void
+plugins_scan(GtkListStore *store)
+{
+ plugin *pt_plug;
+ GtkTreeIter iter;
+
+ pt_plug = plugin_list;
+ while (pt_plug)
+ {
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, COLUMN_NAME, pt_plug->name,
+ COLUMN_VERSION, pt_plug->version, -1);
+ pt_plug = pt_plug->next;
+ }
+}
+
+static void
+plugins_close_cb(GtkWidget *close_bt _U_, gpointer parent_w)
+{
+ gtk_grab_remove(GTK_WIDGET(parent_w));
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+}
+#endif
diff --git a/gtk2/prefs_dlg.c b/gtk2/prefs_dlg.c
new file mode 100644
index 0000000000..877a556934
--- /dev/null
+++ b/gtk2/prefs_dlg.c
@@ -0,0 +1,1239 @@
+/* prefs_dlg.c
+ * Routines for handling preferences
+ *
+ * $Id: prefs_dlg.c,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include <epan/filesystem.h>
+
+#include "main.h"
+#include <epan/packet.h>
+#include "file.h"
+#include "prefs.h"
+#include "column_prefs.h"
+#include "print.h"
+#include "prefs_dlg.h"
+#include "print_prefs.h"
+#include "stream_prefs.h"
+#include "gui_prefs.h"
+#include "capture_prefs.h"
+#include "nameres_prefs.h"
+#include "ui_util.h"
+#include "dlg_utils.h"
+#include "simple_dialog.h"
+
+#include "prefs-int.h"
+
+#ifdef HAVE_LIBPCAP
+#ifdef WIN32
+#include "capture-wpcap.h"
+#endif /* _WIN32 */
+#endif /* HAVE_LIBPCAP */
+
+static void prefs_main_ok_cb(GtkWidget *, gpointer);
+static void prefs_main_apply_cb(GtkWidget *, gpointer);
+static void prefs_main_save_cb(GtkWidget *, gpointer);
+static void prefs_main_cancel_cb(GtkWidget *, gpointer);
+static gboolean prefs_main_delete_cb(GtkWidget *, gpointer);
+static void prefs_main_destroy_cb(GtkWidget *, gpointer);
+static void prefs_tree_select_cb(GtkTreeSelection *, gpointer);
+
+#define E_PRINT_PAGE_KEY "printer_options_page"
+#define E_COLUMN_PAGE_KEY "column_options_page"
+#define E_STREAM_PAGE_KEY "tcp_stream_options_page"
+#define E_GUI_PAGE_KEY "gui_options_page"
+#define E_CAPTURE_PAGE_KEY "capture_options_page"
+#define E_NAMERES_PAGE_KEY "nameres_options_page"
+#define E_TOOLTIPS_KEY "tooltips"
+
+#define FIRST_PROTO_PREFS_PAGE 6
+
+/*
+ * Keep a static pointer to the notebook to be able to choose the
+ * displayed page.
+ */
+static GtkWidget *notebook;
+
+/*
+ * Keep a static pointer to the current "Preferences" window, if any, so that
+ * if somebody tries to do "Edit:Preferences" while there's already a
+ * "Preferences" window up, we just pop up the existing one, rather than
+ * creating a new one.
+ */
+static GtkWidget *prefs_w;
+
+/*
+ * Save the value of the preferences as of when the preferences dialog
+ * box was first popped up, so we can revert to those values if the
+ * user selects "Cancel".
+ */
+static e_prefs saved_prefs;
+
+struct ct_struct {
+ GtkWidget *notebook;
+ GtkWidget *treeview;
+ GtkTreeIter proto_iter;
+ GtkCTreeNode *node;
+ GtkTooltips *tooltips;
+ gint page;
+};
+
+static void
+pref_show(pref_t *pref, gpointer user_data)
+{
+ GtkWidget *main_tb = user_data;
+ const char *title;
+ char *label_string;
+ char uint_str[10+1];
+
+ /* Give this preference a label which is its title, followed by a colon,
+ and left-align it. */
+ title = pref->title;
+ label_string = g_malloc(strlen(title) + 2);
+ strcpy(label_string, title);
+ strcat(label_string, ":");
+
+ /* Save the current value of the preference, so that we can revert it if
+ the user does "Apply" and then "Cancel", and create the control for
+ editing the preference. */
+ switch (pref->type) {
+
+ case PREF_UINT:
+ pref->saved_val.uint = *pref->varp.uint;
+
+ /* XXX - there are no uint spinbuttons, so we can't use a spinbutton.
+ Even more annoyingly, even if there were, GLib doesn't define
+ G_MAXUINT - but I think ANSI C may define UINT_MAX, so we could
+ use that. */
+ switch (pref->info.base) {
+
+ case 10:
+ sprintf(uint_str, "%u", pref->saved_val.uint);
+ break;
+
+ case 8:
+ sprintf(uint_str, "%o", pref->saved_val.uint);
+ break;
+
+ case 16:
+ sprintf(uint_str, "%x", pref->saved_val.uint);
+ break;
+ }
+ pref->control = create_preference_entry(main_tb, pref->ordinal,
+ label_string, pref->description,
+ uint_str);
+ break;
+
+ case PREF_BOOL:
+ pref->saved_val.boolval = *pref->varp.boolp;
+ pref->control = create_preference_check_button(main_tb, pref->ordinal,
+ label_string, pref->description,
+ pref->saved_val.boolval);
+ break;
+
+ case PREF_ENUM:
+ pref->saved_val.enumval = *pref->varp.enump;
+ if (pref->info.enum_info.radio_buttons) {
+ /* Show it as radio buttons. */
+ pref->control = create_preference_radio_buttons(main_tb, pref->ordinal,
+ label_string, pref->description,
+ pref->info.enum_info.enumvals,
+ pref->saved_val.enumval);
+ } else {
+ /* Show it as an option menu. */
+ pref->control = create_preference_option_menu(main_tb, pref->ordinal,
+ label_string, pref->description,
+ pref->info.enum_info.enumvals,
+ pref->saved_val.enumval);
+ }
+ break;
+
+ case PREF_STRING:
+ if (pref->saved_val.string != NULL)
+ g_free(pref->saved_val.string);
+ pref->saved_val.string = g_strdup(*pref->varp.string);
+ pref->control = create_preference_entry(main_tb, pref->ordinal,
+ label_string, pref->description,
+ pref->saved_val.string);
+ break;
+
+ case PREF_OBSOLETE:
+ g_assert_not_reached();
+ break;
+ }
+ g_free(label_string);
+}
+
+#define MAX_TREE_NODE_NAME_LEN 64
+static void
+module_prefs_show(module_t *module, gpointer user_data)
+{
+ struct ct_struct *cts = user_data;
+ GtkWidget *main_vb, *main_tb, *frame;
+ gchar label_str[MAX_TREE_NODE_NAME_LEN];
+ GtkTreeStore *model;
+ GtkTreeIter iter;
+
+ /* Frame */
+ frame = gtk_frame_new(module->title);
+ gtk_widget_show(frame);
+
+ /* Main vertical box */
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(frame), main_vb);
+
+ /* Main table */
+ main_tb = gtk_table_new(module->numprefs, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
+ gtk_object_set_data(GTK_OBJECT(main_tb), E_TOOLTIPS_KEY, cts->tooltips);
+
+ /* Add items for each of the preferences */
+ prefs_pref_foreach(module, pref_show, main_tb);
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(cts->notebook), frame, NULL);
+ strcpy(label_str, module->title);
+ model = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cts->treeview)));
+ gtk_tree_store_append(model, &iter, &cts->proto_iter);
+ gtk_tree_store_set(model, &iter, 0, label_str, 1, cts->page, -1);
+ cts->page++;
+
+ /* Show 'em what we got */
+ gtk_widget_show_all(main_vb);
+}
+
+void
+prefs_cb(GtkWidget *w _U_, gpointer dummy _U_)
+{
+ GtkWidget *main_vb, *top_hb, *bbox, *prefs_nb, *ct_sb, *frame,
+ *ok_bt, *apply_bt, *save_bt, *cancel_bt;
+ GtkWidget *print_pg, *column_pg, *stream_pg, *gui_pg;
+#ifdef HAVE_LIBPCAP
+ GtkWidget *capture_pg;
+#endif
+ GtkWidget *nameres_pg;
+ gchar label_str[MAX_TREE_NODE_NAME_LEN];
+ struct ct_struct cts;
+ GtkTreeStore *store;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ gint col_offset;
+ GtkTreeIter iter;
+
+ if (prefs_w != NULL) {
+ /* There's already a "Preferences" dialog box; reactivate it. */
+ reactivate_window(prefs_w);
+ return;
+ }
+
+ /* Save the current preferences, so we can revert to those values
+ if the user presses "Cancel". */
+ copy_prefs(&saved_prefs, &prefs);
+
+ prefs_w = dlg_window_new("Ethereal: Preferences");
+ g_signal_connect(G_OBJECT(prefs_w), "delete_event",
+ G_CALLBACK(prefs_main_delete_cb), NULL);
+ g_signal_connect(G_OBJECT(prefs_w), "destroy",
+ G_CALLBACK(prefs_main_destroy_cb), NULL);
+
+ /*
+ * Unfortunately, we can't arrange that a GtkTable widget wrap an event box
+ * around a table row, so the spacing between the preference item's label
+ * and its control widgets is inactive and the tooltip doesn't pop up when
+ * the mouse is over it.
+ */
+ cts.tooltips = gtk_tooltips_new();
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(prefs_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* Top row: Preferences tree and notebook */
+ top_hb = gtk_hbox_new(FALSE, 10);
+ gtk_container_add(GTK_CONTAINER(main_vb), top_hb);
+ gtk_widget_show(top_hb);
+
+ /* Place a Ctree on the left for preference categories */
+ ct_sb = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ct_sb),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(top_hb), ct_sb);
+ gtk_widget_show(ct_sb);
+
+ store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_INT);
+ cts.treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(cts.treeview), FALSE);
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(cts.treeview));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+ renderer = gtk_cell_renderer_text_new();
+ col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(cts.treeview),
+ -1, "Name", renderer,
+ "text", 0, NULL);
+ column = gtk_tree_view_get_column(GTK_TREE_VIEW(cts.treeview),
+ col_offset - 1);
+ gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
+ GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ cts.page = 0;
+ gtk_container_add(GTK_CONTAINER(ct_sb), cts.treeview);
+
+ g_signal_connect(G_OBJECT(selection), "changed",
+ G_CALLBACK(prefs_tree_select_cb), NULL);
+ gtk_widget_show(cts.treeview);
+
+ /* A notebook widget sans tabs is used to flip between prefs */
+ notebook = prefs_nb = gtk_notebook_new();
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
+ gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
+ gtk_container_add(GTK_CONTAINER(top_hb), prefs_nb);
+ gtk_widget_show(prefs_nb);
+
+ /* Printing prefs */
+ frame = gtk_frame_new("Printing");
+ gtk_widget_show(GTK_WIDGET(frame));
+ print_pg = printer_prefs_show();
+ gtk_container_add(GTK_CONTAINER(frame), print_pg);
+ gtk_object_set_data(GTK_OBJECT(prefs_w), E_PRINT_PAGE_KEY, print_pg);
+ gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
+ strcpy(label_str, "Printing");
+ gtk_tree_store_append(store, &iter, NULL);
+ gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
+ cts.page++;
+
+ /* Column prefs */
+ frame = gtk_frame_new("Columns");
+ gtk_widget_show(GTK_WIDGET(frame));
+ column_pg = column_prefs_show();
+ gtk_container_add(GTK_CONTAINER(frame), column_pg);
+ gtk_object_set_data(GTK_OBJECT(prefs_w), E_COLUMN_PAGE_KEY, column_pg);
+ gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
+ strcpy(label_str, "Columns");
+ gtk_tree_store_append(store, &iter, NULL);
+ gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
+ cts.page++;
+
+ /* TCP Streams prefs */
+ frame = gtk_frame_new("TCP Streams");
+ gtk_widget_show(GTK_WIDGET(frame));
+ stream_pg = stream_prefs_show();
+ gtk_container_add(GTK_CONTAINER(frame), stream_pg);
+ gtk_object_set_data(GTK_OBJECT(prefs_w), E_STREAM_PAGE_KEY, stream_pg);
+ gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
+ strcpy(label_str, "TCP Streams");
+ gtk_tree_store_append(store, &iter, NULL);
+ gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
+ cts.page++;
+
+ /* GUI prefs */
+ frame = gtk_frame_new("User Interface");
+ gtk_widget_show(GTK_WIDGET(frame));
+ gui_pg = gui_prefs_show();
+ gtk_container_add(GTK_CONTAINER(frame), gui_pg);
+ gtk_object_set_data(GTK_OBJECT(prefs_w), E_GUI_PAGE_KEY, gui_pg);
+ gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
+ strcpy(label_str, "User Interface");
+ gtk_tree_store_append(store, &iter, NULL);
+ gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
+ cts.page++;
+
+#ifdef HAVE_LIBPCAP
+#ifdef _WIN32
+ /* Is WPcap loaded? */
+ if (has_wpcap) {
+#endif /* _WIN32 */
+ /* capture prefs */
+ frame = gtk_frame_new("Capture");
+ gtk_widget_show(GTK_WIDGET(frame));
+ capture_pg = capture_prefs_show();
+ gtk_container_add(GTK_CONTAINER(frame), capture_pg);
+ gtk_object_set_data(GTK_OBJECT(prefs_w), E_CAPTURE_PAGE_KEY, capture_pg);
+ gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
+ strcpy(label_str, "Capture");
+ gtk_tree_store_append(store, &iter, NULL);
+ gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
+ cts.page++;
+#ifdef _WIN32
+ }
+#endif /* _WIN32 */
+#endif /* HAVE_LIBPCAP */
+
+ /* Name resolution prefs */
+ frame = gtk_frame_new("Name resolution");
+ gtk_widget_show(GTK_WIDGET(frame));
+ nameres_pg = nameres_prefs_show();
+ gtk_container_add(GTK_CONTAINER(frame), nameres_pg);
+ gtk_object_set_data(GTK_OBJECT(prefs_w), E_NAMERES_PAGE_KEY, nameres_pg);
+ gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), frame, NULL);
+ strcpy(label_str, "Name resolution");
+ gtk_tree_store_append(store, &iter, NULL);
+ gtk_tree_store_set(store, &iter, 0, label_str, 1, cts.page, -1);
+ cts.page++;
+
+ /* Registered prefs */
+ cts.notebook = prefs_nb;
+ strcpy(label_str, "Protocols");
+ gtk_tree_store_append(store, &cts.proto_iter, NULL);
+ gtk_tree_store_set(store, &cts.proto_iter, 0, label_str, 1, -1, -1);
+ /* gtk_ctree_node_set_selectable(GTK_CTREE(cts.ctree), cts.node, FALSE); */
+
+ prefs_module_foreach(module_prefs_show, &cts);
+
+
+ /* Button row: OK and cancel buttons */
+ 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(main_vb), bbox);
+ gtk_widget_show(bbox);
+
+ ok_bt = gtk_button_new_from_stock(GTK_STOCK_OK);
+ g_signal_connect(G_OBJECT(ok_bt), "clicked",
+ G_CALLBACK(prefs_main_ok_cb), GTK_OBJECT(prefs_w));
+ GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
+ gtk_widget_grab_default(ok_bt);
+ gtk_widget_show(ok_bt);
+
+ apply_bt = gtk_button_new_from_stock(GTK_STOCK_APPLY);
+ g_signal_connect(G_OBJECT(apply_bt), "clicked",
+ G_CALLBACK(prefs_main_apply_cb), GTK_OBJECT(prefs_w));
+ GTK_WIDGET_SET_FLAGS(apply_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start(GTK_BOX (bbox), apply_bt, TRUE, TRUE, 0);
+ gtk_widget_show(apply_bt);
+
+ save_bt = gtk_button_new_from_stock(GTK_STOCK_SAVE);
+ g_signal_connect(G_OBJECT(save_bt), "clicked",
+ G_CALLBACK(prefs_main_save_cb), GTK_OBJECT(prefs_w));
+ GTK_WIDGET_SET_FLAGS(save_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (bbox), save_bt, TRUE, TRUE, 0);
+ gtk_widget_show(save_bt);
+
+ cancel_bt = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+ g_signal_connect(G_OBJECT(cancel_bt), "clicked",
+ G_CALLBACK(prefs_main_cancel_cb), GTK_OBJECT(prefs_w));
+ GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
+ gtk_widget_show(cancel_bt);
+
+ /* 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(prefs_w, cancel_bt);
+
+ gtk_widget_show(prefs_w);
+
+ g_object_unref(G_OBJECT(store));
+}
+
+static void
+set_option_label(GtkWidget *main_tb, int table_position,
+ const gchar *label_text, const gchar *tooltip_text, GtkTooltips *tooltips)
+{
+ GtkWidget *label;
+ GtkWidget *event_box;
+
+ label = gtk_label_new(label_text);
+ gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+ gtk_widget_show(label);
+
+ event_box = gtk_event_box_new();
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), event_box, 0, 1,
+ table_position, table_position + 1);
+ if (tooltip_text != NULL && tooltips != NULL)
+ gtk_tooltips_set_tip(tooltips, event_box, tooltip_text, NULL);
+ gtk_container_add(GTK_CONTAINER(event_box), label);
+ gtk_widget_show(event_box);
+}
+
+GtkWidget *
+create_preference_check_button(GtkWidget *main_tb, int table_position,
+ const gchar *label_text, const gchar *tooltip_text, gboolean active)
+{
+ GtkTooltips *tooltips;
+ GtkWidget *check_box;
+
+ tooltips = gtk_object_get_data(GTK_OBJECT(main_tb), E_TOOLTIPS_KEY);
+
+ set_option_label(main_tb, table_position, label_text, tooltip_text,
+ tooltips);
+
+ check_box = gtk_check_button_new();
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_box), active);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), check_box, 1, 2,
+ table_position, table_position + 1);
+ if (tooltip_text != NULL && tooltips != NULL)
+ gtk_tooltips_set_tip(tooltips, check_box, tooltip_text, NULL);
+
+ return check_box;
+}
+
+GtkWidget *
+create_preference_radio_buttons(GtkWidget *main_tb, int table_position,
+ const gchar *label_text, const gchar *tooltip_text,
+ const enum_val_t *enumvals, gint current_val)
+{
+ GtkTooltips *tooltips;
+ GtkWidget *radio_button_hbox, *button = NULL;
+ GSList *rb_group;
+ int index;
+ const enum_val_t *enum_valp;
+ GtkWidget *event_box;
+
+ tooltips = gtk_object_get_data(GTK_OBJECT(main_tb), E_TOOLTIPS_KEY);
+
+ set_option_label(main_tb, table_position, label_text, tooltip_text,
+ tooltips);
+
+ radio_button_hbox = gtk_hbox_new(FALSE, 0);
+ rb_group = NULL;
+ for (enum_valp = enumvals, index = 0; enum_valp->name != NULL;
+ enum_valp++, index++) {
+ button = gtk_radio_button_new_with_label(rb_group,
+ enum_valp->name);
+ gtk_widget_show(button);
+ if (rb_group == NULL)
+ rb_group = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
+ gtk_box_pack_start(GTK_BOX(radio_button_hbox), button, FALSE,
+ FALSE, 10);
+ if (enum_valp->value == current_val) {
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button),
+ TRUE);
+ }
+ }
+ gtk_widget_show(radio_button_hbox);
+
+ event_box = gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(event_box), radio_button_hbox);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), event_box, 1, 2,
+ table_position, table_position+1);
+ if (tooltip_text != NULL && tooltips != NULL)
+ gtk_tooltips_set_tip(tooltips, event_box, tooltip_text, NULL);
+ gtk_widget_show(event_box);
+
+ /*
+ * It doesn't matter which of the buttons we return - we fetch
+ * the value by looking at the entire radio button group to
+ * which it belongs, and we can get that from any button.
+ */
+ return button;
+}
+
+static gint
+label_to_enum_val(GtkWidget *label, const enum_val_t *enumvals)
+{
+ char *label_string;
+ gint enumval;
+
+ /* Get the label's text, and translate it to a value. */
+ gtk_label_get(GTK_LABEL(label), &label_string);
+ enumval = find_val_for_string(label_string, enumvals, 1);
+
+ return enumval;
+}
+
+gint
+fetch_preference_radio_buttons_val(GtkWidget *button,
+ const enum_val_t *enumvals)
+{
+ GSList *rb_group;
+ GSList *rb_entry;
+
+ /*
+ * Go through the list of of radio buttons in the button's group,
+ * and find the first one that's active.
+ */
+ rb_group = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
+ button = NULL;
+ for (rb_entry = rb_group; rb_entry != NULL;
+ rb_entry = g_slist_next(rb_entry)) {
+ button = rb_entry->data;
+ if (GTK_TOGGLE_BUTTON(button)->active)
+ break;
+ }
+
+ /* OK, now return the value corresponding to that button's label. */
+ return label_to_enum_val(GTK_BIN(button)->child, enumvals);
+}
+
+GtkWidget *
+create_preference_option_menu(GtkWidget *main_tb, int table_position,
+ const gchar *label_text, const gchar *tooltip_text,
+ const enum_val_t *enumvals, gint current_val)
+{
+ GtkTooltips *tooltips;
+ GtkWidget *menu_box, *menu, *menu_item, *option_menu;
+ int menu_index, index;
+ const enum_val_t *enum_valp;
+ GtkWidget *event_box;
+
+ tooltips = gtk_object_get_data(GTK_OBJECT(main_tb), E_TOOLTIPS_KEY);
+
+ set_option_label(main_tb, table_position, label_text, tooltip_text,
+ tooltips);
+
+ /* Create a menu from the enumvals */
+ menu = gtk_menu_new();
+ if (tooltip_text != NULL && tooltips != NULL)
+ gtk_tooltips_set_tip(tooltips, menu, tooltip_text, NULL);
+ menu_index = -1;
+ for (enum_valp = enumvals, index = 0; enum_valp->name != NULL;
+ enum_valp++, index++) {
+ menu_item = gtk_menu_item_new_with_label(enum_valp->name);
+ gtk_menu_append(GTK_MENU(menu), menu_item);
+ if (enum_valp->value == current_val)
+ menu_index = index;
+ gtk_widget_show(menu_item);
+ }
+
+ /* Create the option menu from the menu */
+ option_menu = gtk_option_menu_new();
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
+
+ /* Set its current value to the variable's current value */
+ if (menu_index != -1)
+ gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu),
+ menu_index);
+
+ /*
+ * Put the option menu in an hbox, so that it's only as wide
+ * as the widest entry, rather than being as wide as the table
+ * space.
+ */
+ menu_box = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(menu_box), option_menu, FALSE, FALSE, 0);
+
+ event_box = gtk_event_box_new();
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), event_box,
+ 1, 2, table_position, table_position + 1);
+ if (tooltip_text != NULL && tooltips != NULL)
+ gtk_tooltips_set_tip(tooltips, event_box, tooltip_text, NULL);
+ gtk_container_add(GTK_CONTAINER(event_box), menu_box);
+
+ return option_menu;
+}
+
+gint
+fetch_preference_option_menu_val(GtkWidget *optmenu, const enum_val_t *enumvals)
+{
+ /*
+ * OK, now return the value corresponding to the label for the
+ * currently active entry in the option menu.
+ *
+ * Yes, this is how you get the label for that entry. See FAQ
+ * 6.8 in the GTK+ FAQ.
+ */
+ return label_to_enum_val(GTK_BIN(optmenu)->child, enumvals);
+}
+
+GtkWidget *
+create_preference_entry(GtkWidget *main_tb, int table_position,
+ const gchar *label_text, const gchar *tooltip_text, char *value)
+{
+ GtkTooltips *tooltips;
+ GtkWidget *entry;
+
+ tooltips = gtk_object_get_data(GTK_OBJECT(main_tb), E_TOOLTIPS_KEY);
+
+ set_option_label(main_tb, table_position, label_text, tooltip_text,
+ tooltips);
+
+ entry = gtk_entry_new();
+ if (value != NULL)
+ gtk_entry_set_text(GTK_ENTRY(entry), value);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), entry, 1, 2,
+ table_position, table_position + 1);
+ if (tooltip_text != NULL && tooltips != NULL)
+ gtk_tooltips_set_tip(tooltips, entry, tooltip_text, NULL);
+ gtk_widget_show(entry);
+
+ return entry;
+}
+
+static void
+pref_fetch(pref_t *pref, gpointer user_data)
+{
+ const char *str_val;
+ char *p;
+ guint uval;
+ gboolean bval;
+ gint enumval;
+ gboolean *pref_changed_p = user_data;
+
+ /* Fetch the value of the preference, and set the appropriate variable
+ to it. */
+ switch (pref->type) {
+
+ case PREF_UINT:
+ str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
+ uval = strtoul(str_val, &p, pref->info.base);
+#if 0
+ if (p == value || *p != '\0')
+ return PREFS_SET_SYNTAX_ERR; /* number was bad */
+#endif
+ if (*pref->varp.uint != uval) {
+ *pref_changed_p = TRUE;
+ *pref->varp.uint = uval;
+ }
+ break;
+
+ case PREF_BOOL:
+ bval = GTK_TOGGLE_BUTTON(pref->control)->active;
+ if (*pref->varp.boolp != bval) {
+ *pref_changed_p = TRUE;
+ *pref->varp.boolp = bval;
+ }
+ break;
+
+ case PREF_ENUM:
+ if (pref->info.enum_info.radio_buttons) {
+ enumval = fetch_preference_radio_buttons_val(pref->control,
+ pref->info.enum_info.enumvals);
+ } else {
+ enumval = fetch_preference_option_menu_val(pref->control,
+ pref->info.enum_info.enumvals);
+ }
+
+ if (*pref->varp.enump != enumval) {
+ *pref_changed_p = TRUE;
+ *pref->varp.enump = enumval;
+ }
+ break;
+
+ case PREF_STRING:
+ str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
+ if (*pref->varp.string == NULL || strcmp(*pref->varp.string, str_val) != 0) {
+ *pref_changed_p = TRUE;
+ if (*pref->varp.string != NULL)
+ g_free(*pref->varp.string);
+ *pref->varp.string = g_strdup(str_val);
+ }
+ break;
+
+ case PREF_OBSOLETE:
+ g_assert_not_reached();
+ break;
+ }
+}
+
+static void
+module_prefs_fetch(module_t *module, gpointer user_data)
+{
+ gboolean *must_redissect_p = user_data;
+
+ /* For all preferences in this module, fetch its value from this
+ module's notebook page. Find out whether any of them changed. */
+ module->prefs_changed = FALSE; /* assume none of them changed */
+ prefs_pref_foreach(module, pref_fetch, &module->prefs_changed);
+
+ /* If any of them changed, indicate that we must redissect and refilter
+ the current capture (if we have one), as the preference change
+ could cause packets to be dissected differently. */
+ if (module->prefs_changed)
+ *must_redissect_p = TRUE;
+}
+
+static void
+pref_clean(pref_t *pref, gpointer user_data _U_)
+{
+ switch (pref->type) {
+
+ case PREF_UINT:
+ break;
+
+ case PREF_BOOL:
+ break;
+
+ case PREF_ENUM:
+ break;
+
+ case PREF_STRING:
+ if (pref->saved_val.string != NULL) {
+ g_free(pref->saved_val.string);
+ pref->saved_val.string = NULL;
+ }
+ break;
+
+ case PREF_OBSOLETE:
+ g_assert_not_reached();
+ break;
+ }
+}
+
+static void
+module_prefs_clean(module_t *module, gpointer user_data _U_)
+{
+ /* For all preferences in this module, clean up any cruft allocated for
+ use by the GUI code. */
+ prefs_pref_foreach(module, pref_clean, NULL);
+}
+
+static void
+prefs_main_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w)
+{
+ gboolean must_redissect = FALSE;
+
+ /* Fetch the preferences (i.e., make sure all the values set in all of
+ the preferences panes have been copied to "prefs" and the registered
+ preferences). */
+ printer_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_PRINT_PAGE_KEY));
+ column_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_COLUMN_PAGE_KEY));
+ stream_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_STREAM_PAGE_KEY));
+ gui_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_GUI_PAGE_KEY));
+#ifdef HAVE_LIBPCAP
+#ifdef _WIN32
+ /* Is WPcap loaded? */
+ if (has_wpcap) {
+#endif /* _WIN32 */
+ capture_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_CAPTURE_PAGE_KEY));
+#ifdef _WIN32
+ }
+#endif /* _WIN32 */
+#endif /* HAVE_LIBPCAP */
+ nameres_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_NAMERES_PAGE_KEY));
+ prefs_module_foreach(module_prefs_fetch, &must_redissect);
+
+ /* Now apply those preferences. */
+ printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_PRINT_PAGE_KEY));
+ column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_COLUMN_PAGE_KEY));
+ stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_STREAM_PAGE_KEY));
+ gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_GUI_PAGE_KEY));
+#ifdef HAVE_LIBPCAP
+#ifdef _WIN32
+ /* Is WPcap loaded? */
+ if (has_wpcap) {
+#endif /* _WIN32 */
+ capture_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_CAPTURE_PAGE_KEY));
+#ifdef _WIN32
+ }
+#endif /* _WIN32 */
+#endif /* HAVE_LIBPCAP */
+ nameres_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_NAMERES_PAGE_KEY));
+ prefs_apply_all();
+
+ /* Now destroy the "Preferences" dialog. */
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+
+ if (must_redissect) {
+ /* Redissect all the packets, and re-evaluate the display filter. */
+ redissect_packets(&cfile);
+ }
+}
+
+static void
+prefs_main_apply_cb(GtkWidget *apply_bt _U_, gpointer parent_w)
+{
+ gboolean must_redissect = FALSE;
+
+ /* Fetch the preferences (i.e., make sure all the values set in all of
+ the preferences panes have been copied to "prefs" and the registered
+ preferences). */
+ printer_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_PRINT_PAGE_KEY));
+ column_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_COLUMN_PAGE_KEY));
+ stream_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_STREAM_PAGE_KEY));
+ gui_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
+#ifdef HAVE_LIBPCAP
+#ifdef _WIN32
+ /* Is WPcap loaded? */
+ if (has_wpcap) {
+#endif /* _WIN32 */
+ capture_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_CAPTURE_PAGE_KEY));
+#ifdef _WIN32
+ }
+#endif /* _WIN32 */
+#endif /* HAVE_LIBPCAP */
+ nameres_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_NAMERES_PAGE_KEY));
+ prefs_module_foreach(module_prefs_fetch, &must_redissect);
+
+ /* Now apply those preferences. */
+ printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_PRINT_PAGE_KEY));
+ column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_COLUMN_PAGE_KEY));
+ stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_STREAM_PAGE_KEY));
+ gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
+#ifdef HAVE_LIBPCAP
+#ifdef _WIN32
+ /* Is WPcap loaded? */
+ if (has_wpcap) {
+#endif /* _WIN32 */
+ capture_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_CAPTURE_PAGE_KEY));
+#ifdef _WIN32
+ }
+#endif /* _WIN32 */
+#endif /* HAVE_LIBPCAP */
+ nameres_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_NAMERES_PAGE_KEY));
+ prefs_apply_all();
+
+ if (must_redissect) {
+ /* Redissect all the packets, and re-evaluate the display filter. */
+ redissect_packets(&cfile);
+ }
+}
+
+static void
+prefs_main_save_cb(GtkWidget *save_bt _U_, gpointer parent_w)
+{
+ gboolean must_redissect = FALSE;
+ int err;
+ char *pf_dir_path;
+ char *pf_path;
+
+ /* Fetch the preferences (i.e., make sure all the values set in all of
+ the preferences panes have been copied to "prefs" and the registered
+ preferences). */
+ printer_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_PRINT_PAGE_KEY));
+ column_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_COLUMN_PAGE_KEY));
+ stream_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_STREAM_PAGE_KEY));
+ gui_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
+#ifdef HAVE_LIBPCAP
+#ifdef _WIN32
+ /* Is WPcap loaded? */
+ if (has_wpcap) {
+#endif /* _WIN32 */
+ capture_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_CAPTURE_PAGE_KEY));
+#ifdef _WIN32
+ }
+#endif /* _WIN32 */
+#endif /* HAVE_LIBPCAP */
+ nameres_prefs_fetch(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_NAMERES_PAGE_KEY));
+ prefs_module_foreach(module_prefs_fetch, &must_redissect);
+
+ /* Create the directory that holds personal configuration files, if
+ necessary. */
+ if (create_persconffile_dir(&pf_dir_path) == -1) {
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "Can't create directory\n\"%s\"\nfor preferences file: %s.", pf_dir_path,
+ strerror(errno));
+ g_free(pf_dir_path);
+ } else {
+ /* Write the preferencs out. */
+ err = write_prefs(&pf_path);
+ if (err != 0) {
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "Can't open preferences file\n\"%s\": %s.", pf_path,
+ strerror(err));
+ g_free(pf_path);
+ }
+ }
+
+ /* Now apply those preferences.
+ XXX - should we do this? The user didn't click "OK" or "Apply".
+ However:
+
+ 1) by saving the preferences they presumably indicate that they
+ like them;
+
+ 2) the next time they fire Ethereal up, those preferences will
+ apply;
+
+ 3) we'd have to buffer "must_redissect" so that if they do
+ "Apply" after this, we know we have to redissect;
+
+ 4) we did apply the protocol preferences, at least, in the past. */
+ printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_PRINT_PAGE_KEY));
+ column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_COLUMN_PAGE_KEY));
+ stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_STREAM_PAGE_KEY));
+ gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
+#ifdef HAVE_LIBPCAP
+#ifdef _WIN32
+ /* Is WPcap loaded? */
+ if (has_wpcap) {
+#endif /* _WIN32 */
+ capture_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_CAPTURE_PAGE_KEY));
+#ifdef _WIN32
+ }
+#endif /* _WIN32 */
+#endif /* HAVE_LIBPCAP */
+ nameres_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_NAMERES_PAGE_KEY));
+ prefs_apply_all();
+
+ if (must_redissect) {
+ /* Redissect all the packets, and re-evaluate the display filter. */
+ redissect_packets(&cfile);
+ }
+}
+
+static void
+pref_revert(pref_t *pref, gpointer user_data)
+{
+ gboolean *pref_changed_p = user_data;
+
+ /* Revert the preference to its saved value. */
+ switch (pref->type) {
+
+ case PREF_UINT:
+ if (*pref->varp.uint != pref->saved_val.uint) {
+ *pref_changed_p = TRUE;
+ *pref->varp.uint = pref->saved_val.uint;
+ }
+ break;
+
+ case PREF_BOOL:
+ if (*pref->varp.boolp != pref->saved_val.boolval) {
+ *pref_changed_p = TRUE;
+ *pref->varp.boolp = pref->saved_val.boolval;
+ }
+ break;
+
+ case PREF_ENUM:
+ if (*pref->varp.enump != pref->saved_val.enumval) {
+ *pref_changed_p = TRUE;
+ *pref->varp.enump = pref->saved_val.enumval;
+ }
+ break;
+
+ case PREF_STRING:
+ if (*pref->varp.string != pref->saved_val.string &&
+ (*pref->varp.string == NULL ||
+ pref->saved_val.string == NULL ||
+ strcmp(*pref->varp.string, pref->saved_val.string) != 0)) {
+ *pref_changed_p = TRUE;
+ if (*pref->varp.string != NULL)
+ g_free(*pref->varp.string);
+ *pref->varp.string = g_strdup(pref->saved_val.string);
+ }
+ break;
+
+ case PREF_OBSOLETE:
+ g_assert_not_reached();
+ break;
+ }
+}
+
+static void
+module_prefs_revert(module_t *module, gpointer user_data)
+{
+ gboolean *must_redissect_p = user_data;
+
+ /* For all preferences in this module, revert its value to the value
+ it had when we popped up the Preferences dialog. Find out whether
+ this changes any of them. */
+ module->prefs_changed = FALSE; /* assume none of them changed */
+ prefs_pref_foreach(module, pref_revert, &module->prefs_changed);
+
+ /* If any of them changed, indicate that we must redissect and refilter
+ the current capture (if we have one), as the preference change
+ could cause packets to be dissected differently. */
+ if (module->prefs_changed)
+ *must_redissect_p = TRUE;
+}
+
+static void
+prefs_main_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
+{
+ gboolean must_redissect = FALSE;
+
+ /* Free up the current preferences and copy the saved preferences to the
+ current preferences. */
+ free_prefs(&prefs);
+ copy_prefs(&prefs, &saved_prefs);
+
+ /* Now revert the registered preferences. */
+ prefs_module_foreach(module_prefs_revert, &must_redissect);
+
+ /* Now apply the reverted-to preferences. */
+ printer_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_PRINT_PAGE_KEY));
+ column_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_COLUMN_PAGE_KEY));
+ stream_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_STREAM_PAGE_KEY));
+ gui_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY));
+ nameres_prefs_apply(gtk_object_get_data(GTK_OBJECT(parent_w),
+ E_NAMERES_PAGE_KEY));
+ prefs_apply_all();
+
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+
+ if (must_redissect) {
+ /* Redissect all the packets, and re-evaluate the display filter. */
+ redissect_packets(&cfile);
+ }
+}
+
+/* Treat this as a cancel, by calling "prefs_main_cancel_cb()".
+ XXX - that'll destroy the Preferences dialog; will that upset
+ a higher-level handler that says "OK, we've been asked to delete
+ this, so destroy it"? */
+static gboolean
+prefs_main_delete_cb(GtkWidget *prefs_w, gpointer dummy _U_)
+{
+ prefs_main_cancel_cb(NULL, prefs_w);
+ return FALSE;
+}
+
+static void
+prefs_main_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Let the preference tabs clean up anything they've done. */
+ printer_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w),
+ E_PRINT_PAGE_KEY));
+ column_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w),
+ E_COLUMN_PAGE_KEY));
+ stream_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w),
+ E_STREAM_PAGE_KEY));
+ gui_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w), E_GUI_PAGE_KEY));
+#ifdef HAVE_LIBPCAP
+#ifdef _WIN32
+ /* Is WPcap loaded? */
+ if (has_wpcap) {
+#endif /* _WIN32 */
+ capture_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w),
+ E_CAPTURE_PAGE_KEY));
+#ifdef _WIN32
+ }
+#endif /* _WIN32 */
+#endif /* HAVE_LIBPCAP */
+ nameres_prefs_destroy(gtk_object_get_data(GTK_OBJECT(prefs_w),
+ E_NAMERES_PAGE_KEY));
+
+ /* Free up the saved preferences (both for "prefs" and for registered
+ preferences). */
+ free_prefs(&saved_prefs);
+ prefs_module_foreach(module_prefs_clean, NULL);
+
+ /* Note that we no longer have a "Preferences" dialog box. */
+ prefs_w = NULL;
+}
+
+struct properties_data {
+ GtkWidget *w;
+ int page_num;
+ char *title;
+};
+
+/* XXX this way of searching the correct page number is really ugly ... */
+static void
+module_search_properties(module_t *module, gpointer user_data)
+{
+ struct properties_data *p = (struct properties_data *)user_data;
+
+ if (p->title == NULL) return;
+ if (strcmp(module->title, p->title) == 0) {
+ /* found it */
+ gtk_notebook_set_page(GTK_NOTEBOOK(p->w), p->page_num);
+ p->title = NULL;
+ } else {
+ p->page_num++;
+ }
+}
+
+void
+properties_cb(GtkWidget *w, gpointer dummy)
+{
+ gchar *title = NULL;
+ struct properties_data p;
+
+ if (finfo_selected) {
+ header_field_info *hfinfo = finfo_selected->hfinfo;
+ if (hfinfo->parent == -1) {
+ title = (gchar *)prefs_get_title_by_name(hfinfo->abbrev);
+ } else {
+ title = (gchar *)
+ prefs_get_title_by_name(proto_registrar_get_abbrev(hfinfo->parent));
+ }
+ } else {
+ return;
+ }
+
+ if (!title) return;
+
+ if (prefs_w != NULL) {
+ reactivate_window(prefs_w);
+ } else {
+ prefs_cb(w, dummy);
+ }
+
+ p.w = notebook;
+ p.page_num = FIRST_PROTO_PREFS_PAGE;
+ p.title = title;
+
+ prefs_module_foreach(module_search_properties, &p);
+
+}
+
+/* Prefs tree selection callback. The node data has been loaded with
+ the proper notebook page to load. */
+static void
+prefs_tree_select_cb(GtkTreeSelection *sel, gpointer dummy _U_)
+{
+ gint page;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ gtk_tree_selection_get_selected(sel, &model, &iter);
+ gtk_tree_model_get(model, &iter, 1, &page, -1);
+ if (page >= 0)
+ gtk_notebook_set_page(GTK_NOTEBOOK(notebook), page);
+}
diff --git a/gtk2/prefs_dlg.h b/gtk2/prefs_dlg.h
new file mode 100644
index 0000000000..7d1d410f62
--- /dev/null
+++ b/gtk2/prefs_dlg.h
@@ -0,0 +1,42 @@
+/* prefs_dlg.h
+ * Definitions for preference handling routines
+ *
+ * $Id: prefs_dlg.h,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifndef __PREFS_DLG_H__
+#define __PREFS_DLG_H__
+
+void prefs_cb(GtkWidget *, gpointer);
+void properties_cb(GtkWidget *, gpointer);
+
+GtkWidget *create_preference_check_button(GtkWidget *, int, const gchar *,
+ const gchar *, gboolean);
+GtkWidget *create_preference_radio_buttons(GtkWidget *, int, const gchar *,
+ const gchar *, const enum_val_t *, gint);
+gint fetch_preference_radio_buttons_val(GtkWidget *, const enum_val_t *);
+GtkWidget *create_preference_option_menu(GtkWidget *, int, const gchar *,
+ const gchar *, const enum_val_t *, gint);
+gint fetch_preference_option_menu_val(GtkWidget *, const enum_val_t *);
+GtkWidget *create_preference_entry(GtkWidget *, int, const gchar *,
+ const gchar *, char *);
+
+#endif
diff --git a/gtk2/print_dlg.c b/gtk2/print_dlg.c
new file mode 100644
index 0000000000..486cd27947
--- /dev/null
+++ b/gtk2/print_dlg.c
@@ -0,0 +1,690 @@
+/* print_dlg.c
+ * Dialog boxes for printing
+ *
+ * $Id: print_dlg.c,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+
+#include <gtk/gtk.h>
+
+#include "globals.h"
+#include "keys.h"
+#include "print.h"
+#include "prefs.h"
+#include "simple_dialog.h"
+#include "ui_util.h"
+#include "dlg_utils.h"
+#include <epan/epan_dissect.h>
+#ifdef _WIN32
+#include <io.h>
+#include "print_mswin.h"
+#endif
+
+/* On Win32, a GUI application apparently can't use "popen()" (it
+ "returns an invalid file handle, if used in a Windows program,
+ that will cause the program to hang indefinitely"), so we can't
+ use a pipe to a print command to print to a printer.
+
+ Eventually, we should try to use the native Win32 printing API
+ for this (and also use various UNIX printing APIs, when present?).
+ */
+static void print_cmd_toggle_dest(GtkWidget *widget, gpointer data);
+static void print_cmd_toggle_detail(GtkWidget *widget, gpointer data);
+static void print_file_cb(GtkWidget *file_bt, gpointer file_te);
+static void print_fs_ok_cb(GtkWidget *w, gpointer data);
+static void print_fs_cancel_cb(GtkWidget *w, gpointer data);
+static void print_fs_destroy_cb(GtkWidget *win, gpointer data);
+static void print_ok_cb(GtkWidget *ok_bt, gpointer parent_w);
+static void print_close_cb(GtkWidget *close_bt, gpointer parent_w);
+static void print_destroy_cb(GtkWidget *win, gpointer user_data);
+
+/*
+ * Remember whether we printed to a printer or a file the last time we
+ * printed something.
+ */
+static int print_to_file;
+
+/*
+ * Remember whether we printed as text or PostScript the last time we
+ * printed something.
+ */
+static gint print_format = PR_FMT_TEXT;
+
+#define PRINT_FORMAT_RB_KEY "printer_format_radio_button"
+#define PRINT_DEST_RB_KEY "printer_destination_radio_button"
+
+#define PRINT_SUMMARY_RB_KEY "printer_summary_radio_button"
+#define PRINT_HEX_CB_KEY "printer_hex_check_button"
+#define PRINT_EXPAND_ALL_RB_KEY "printer_expand_all_radio_button"
+#define PRINT_AS_DISPLAYED_RB_KEY "printer_as_displayed_radio_button"
+#define PRINT_SUPPRESS_UNMARKED_CB_KEY "printer_suppress_unmarked_check_button"
+
+#define E_FS_CALLER_PTR_KEY "fs_caller_ptr"
+#define E_FILE_SEL_DIALOG_PTR_KEY "file_sel_dialog_ptr"
+
+/*
+ * Keep a static pointer to the current "Print" window, if any, so that if
+ * somebody tries to do "File:Print" while there's already a "Print" window
+ * up, we just pop up the existing one, rather than creating a new one.
+ */
+static GtkWidget *print_w;
+
+/* Print the capture */
+void
+file_print_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ GtkAccelGroup *accel_group;
+ GtkWidget *main_vb, *main_tb, *button;
+ GtkWidget *format_rb;
+ GtkWidget *format_hb, *format_lb;
+ GSList *format_grp;
+ GtkWidget *dest_rb;
+ GtkWidget *dest_hb, *dest_lb;
+ GtkWidget *cmd_lb, *cmd_te;
+ GtkWidget *file_bt_hb, *file_bt, *file_te;
+ GSList *dest_grp;
+ GtkWidget *options_hb;
+ GtkWidget *print_type_vb, *summary_rb, *detail_rb, *hex_cb,*marked_cb;
+ GSList *summary_grp;
+ GtkWidget *expand_vb, *expand_all_rb, *as_displayed_rb;
+ GSList *expand_grp;
+ GtkWidget *bbox, *ok_bt, *cancel_bt;
+
+ if (print_w != NULL) {
+ /* There's already a "Print" dialog box; reactivate it. */
+ reactivate_window(print_w);
+ return;
+ }
+
+ print_w = dlg_window_new("Ethereal: Print");
+ g_signal_connect(G_OBJECT(print_w), "destroy",
+ G_CALLBACK(print_destroy_cb), NULL);
+
+ /* Accelerator group for the accelerators (or, as they're called in
+ Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
+ Ctrl+<key> is an accelerator). */
+ accel_group = gtk_accel_group_new();
+ gtk_window_add_accel_group(GTK_WINDOW(print_w), accel_group);
+
+ /* Enclosing containers for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(print_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ main_tb = gtk_table_new(4, 2, FALSE);
+
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
+ gtk_widget_show(main_tb);
+
+ /* Output format */
+ format_lb = gtk_label_new("Format:");
+ gtk_misc_set_alignment(GTK_MISC(format_lb), 1.0, 0.5);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), format_lb, 0, 1, 0, 1);
+ gtk_widget_show(format_lb);
+
+ format_hb = gtk_hbox_new(FALSE, 0);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), format_hb, 1, 2, 0, 1);
+ gtk_widget_show(format_hb);
+
+ button = dlg_radio_button_new_with_label_with_mnemonic(NULL, "Plain _Text",
+ accel_group);
+ if (print_format == PR_FMT_TEXT)
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
+ format_grp = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
+ gtk_box_pack_start(GTK_BOX(format_hb), button, FALSE, FALSE, 10);
+ gtk_widget_show(button);
+
+ format_rb = dlg_radio_button_new_with_label_with_mnemonic(format_grp,
+ "_PostScript", accel_group);
+ if (print_format == PR_FMT_PS)
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(format_rb), TRUE);
+ gtk_box_pack_start(GTK_BOX(format_hb), format_rb, FALSE, FALSE, 10);
+ gtk_widget_show(format_rb);
+
+ /* Output destination */
+ dest_lb = gtk_label_new("Print to:");
+ gtk_misc_set_alignment(GTK_MISC(dest_lb), 1.0, 0.5);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), dest_lb, 0, 1, 1, 2);
+ gtk_widget_show(dest_lb);
+
+ dest_hb = gtk_hbox_new(FALSE, 0);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), dest_hb, 1, 2, 1, 2);
+ gtk_widget_show(dest_hb);
+
+#ifdef _WIN32
+ button = dlg_radio_button_new_with_label_with_mnemonic(NULL, "_Printer"
+ accel_group);
+#else
+ button = dlg_radio_button_new_with_label_with_mnemonic(NULL, "_Command",
+ accel_group);
+#endif
+ if (!print_to_file)
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
+ dest_grp = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
+ gtk_box_pack_start(GTK_BOX(dest_hb), button, FALSE, FALSE, 10);
+ gtk_widget_show(button);
+
+ dest_rb = dlg_radio_button_new_with_label_with_mnemonic(dest_grp, "_File",
+ accel_group);
+ if (print_to_file)
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(dest_rb), TRUE);
+ g_signal_connect(G_OBJECT(dest_rb), "toggled",
+ G_CALLBACK(print_cmd_toggle_dest), NULL);
+ gtk_box_pack_start(GTK_BOX(dest_hb), dest_rb, FALSE, FALSE, 10);
+ gtk_widget_show(dest_rb);
+
+ /* Command text entry */
+
+#ifndef _WIN32
+ cmd_lb = gtk_label_new("Command:");
+
+ gtk_object_set_data(GTK_OBJECT(dest_rb), PRINT_CMD_LB_KEY, cmd_lb);
+ gtk_misc_set_alignment(GTK_MISC(cmd_lb), 1.0, 0.5);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), cmd_lb, 0, 1, 2, 3);
+ gtk_widget_set_sensitive(cmd_lb, !print_to_file);
+ gtk_widget_show(cmd_lb);
+
+ cmd_te = gtk_entry_new();
+
+ gtk_object_set_data(GTK_OBJECT(dest_rb), PRINT_CMD_TE_KEY, cmd_te);
+ if (prefs.pr_cmd)
+ gtk_entry_set_text(GTK_ENTRY(cmd_te), prefs.pr_cmd);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), cmd_te, 1, 2, 2, 3);
+ gtk_widget_set_sensitive(cmd_te, !print_to_file);
+ gtk_widget_show(cmd_te);
+#endif
+
+ /* File button and text entry */
+ file_bt_hb = gtk_hbox_new(FALSE, 0);
+
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), file_bt_hb, 0, 1, 3, 4);
+ gtk_widget_show(file_bt_hb);
+
+ file_bt = gtk_button_new_with_label("File:");
+ gtk_object_set_data(GTK_OBJECT(dest_rb), PRINT_FILE_BT_KEY, file_bt);
+ gtk_box_pack_end(GTK_BOX(file_bt_hb), file_bt, FALSE, FALSE, 0);
+ gtk_widget_set_sensitive(file_bt, print_to_file);
+ gtk_widget_show(file_bt);
+
+ file_te = gtk_entry_new();
+
+ gtk_object_set_data(GTK_OBJECT(dest_rb), PRINT_FILE_TE_KEY, file_te);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), file_te, 1, 2, 3, 4);
+ gtk_widget_set_sensitive(file_te, print_to_file);
+ gtk_widget_show(file_te);
+
+ g_signal_connect(G_OBJECT(file_bt), "clicked",
+ G_CALLBACK(print_file_cb), GTK_OBJECT(file_te));
+
+ /* Horizontal box into which to put two vertical boxes of option
+ buttons. */
+ options_hb = gtk_hbox_new(FALSE, 0);
+ gtk_container_border_width(GTK_CONTAINER(options_hb), 5);
+ gtk_container_add(GTK_CONTAINER(main_vb), options_hb);
+ gtk_widget_show(options_hb);
+
+ /* Vertical box into which to put the "Print summary"/"Print detail"
+ radio buttons and the "Print hex" check button. */
+ print_type_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(print_type_vb), 5);
+ gtk_container_add(GTK_CONTAINER(options_hb), print_type_vb);
+ gtk_widget_show(print_type_vb);
+
+ /* "Print summary"/"Print detail" radio buttons */
+ summary_rb = dlg_radio_button_new_with_label_with_mnemonic(NULL,
+ "Print _summary", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(summary_rb), FALSE);
+ summary_grp = gtk_radio_button_group(GTK_RADIO_BUTTON(summary_rb));
+ gtk_container_add(GTK_CONTAINER(print_type_vb), summary_rb);
+ gtk_widget_show(summary_rb);
+ detail_rb = dlg_radio_button_new_with_label_with_mnemonic(summary_grp,
+ "Print _detail", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(detail_rb), TRUE);
+ g_signal_connect(G_OBJECT(detail_rb), "toggled",
+ G_CALLBACK(print_cmd_toggle_detail), NULL);
+ gtk_container_add(GTK_CONTAINER(print_type_vb), detail_rb);
+ gtk_widget_show(detail_rb);
+
+ /* "Print hex" check button. */
+ hex_cb = dlg_check_button_new_with_label_with_mnemonic("Print _hex data",
+ accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(hex_cb), FALSE);
+ gtk_container_add(GTK_CONTAINER(print_type_vb), hex_cb);
+ gtk_widget_show(hex_cb);
+
+ /* "Suppress Unmarked" check button. */
+ marked_cb = dlg_check_button_new_with_label_with_mnemonic("Suppress _unmarked frames",
+ accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(marked_cb), FALSE);
+ gtk_container_add(GTK_CONTAINER(print_type_vb), marked_cb);
+ gtk_widget_show(marked_cb);
+
+ /* Vertical box into which to put the "Expand all levels"/"Print as displayed"
+ radio buttons. */
+ expand_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(expand_vb), 5);
+ gtk_container_add(GTK_CONTAINER(options_hb), expand_vb);
+ gtk_widget_show(expand_vb);
+
+ /* "Expand all levels"/"Print as displayed" radio buttons */
+ expand_all_rb = dlg_radio_button_new_with_label_with_mnemonic(NULL,
+ "_Expand all levels", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(expand_all_rb), TRUE);
+ expand_grp = gtk_radio_button_group(GTK_RADIO_BUTTON(expand_all_rb));
+ gtk_container_add(GTK_CONTAINER(expand_vb), expand_all_rb);
+ gtk_widget_show(expand_all_rb);
+ as_displayed_rb = dlg_radio_button_new_with_label_with_mnemonic(expand_grp,
+ "Print _as displayed", accel_group);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(as_displayed_rb), FALSE);
+ gtk_container_add(GTK_CONTAINER(expand_vb), as_displayed_rb);
+ gtk_widget_show(as_displayed_rb);
+
+ gtk_object_set_data(GTK_OBJECT(detail_rb), PRINT_EXPAND_ALL_RB_KEY,
+ expand_all_rb);
+ gtk_object_set_data(GTK_OBJECT(detail_rb), PRINT_AS_DISPLAYED_RB_KEY,
+ as_displayed_rb);
+ gtk_object_set_data(GTK_OBJECT(detail_rb), PRINT_HEX_CB_KEY,
+ hex_cb);
+
+ /* Button row: OK and Cancel buttons */
+ 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(main_vb), bbox);
+ gtk_widget_show(bbox);
+
+ ok_bt = gtk_button_new_from_stock(GTK_STOCK_OK);
+ gtk_object_set_data(GTK_OBJECT(ok_bt), PRINT_FORMAT_RB_KEY, format_rb);
+ gtk_object_set_data(GTK_OBJECT(ok_bt), PRINT_DEST_RB_KEY, dest_rb);
+#ifndef _WIN32
+ gtk_object_set_data(GTK_OBJECT(ok_bt), PRINT_CMD_TE_KEY, cmd_te);
+#endif
+
+ gtk_object_set_data(GTK_OBJECT(ok_bt), PRINT_FILE_TE_KEY, file_te);
+ gtk_object_set_data(GTK_OBJECT(ok_bt), PRINT_SUMMARY_RB_KEY, summary_rb);
+ gtk_object_set_data(GTK_OBJECT(ok_bt), PRINT_HEX_CB_KEY, hex_cb);
+ gtk_object_set_data(GTK_OBJECT(ok_bt), PRINT_EXPAND_ALL_RB_KEY, expand_all_rb);
+ gtk_object_set_data(GTK_OBJECT(ok_bt), PRINT_SUPPRESS_UNMARKED_CB_KEY, marked_cb);
+ g_signal_connect(G_OBJECT(ok_bt), "clicked",
+ G_CALLBACK(print_ok_cb), GTK_OBJECT(print_w));
+ GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
+ gtk_widget_grab_default(ok_bt);
+ gtk_widget_show(ok_bt);
+
+ cancel_bt = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+ g_signal_connect(G_OBJECT(cancel_bt), "clicked",
+ G_CALLBACK(print_close_cb), GTK_OBJECT(print_w));
+ GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
+ gtk_widget_show(cancel_bt);
+
+ /* Catch the "activate" signal on the "Command" and "File" text entries,
+ so that if the user types Return there, we act as if the "OK" button
+ had been selected, as happens if Return is typed if some widget
+ that *doesn't* handle the Return key has the input focus. */
+
+#ifndef _WIN32
+ dlg_set_activate(cmd_te, ok_bt);
+#endif
+ dlg_set_activate(file_te, ok_bt);
+
+ /* 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(print_w, cancel_bt);
+
+ gtk_widget_show(print_w);
+}
+
+static void
+print_cmd_toggle_dest(GtkWidget *widget, gpointer data _U_)
+{
+#ifndef _WIN32
+ GtkWidget *cmd_lb, *cmd_te;
+#endif
+ GtkWidget *file_bt, *file_te;
+ int to_file;
+
+#ifndef _WIN32
+ cmd_lb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(widget),
+ PRINT_CMD_LB_KEY));
+ cmd_te = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(widget),
+ PRINT_CMD_TE_KEY));
+#endif
+ file_bt = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(widget),
+ PRINT_FILE_BT_KEY));
+ file_te = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(widget),
+ PRINT_FILE_TE_KEY));
+ if (GTK_TOGGLE_BUTTON (widget)->active) {
+ /* They selected "Print to File" */
+ to_file = TRUE;
+ } else {
+ /* They selected "Print to Command" on UNIX or "Print to Printer"
+ on Windows */
+ to_file = FALSE;
+ }
+#ifndef _WIN32
+ gtk_widget_set_sensitive(cmd_lb, !to_file);
+ gtk_widget_set_sensitive(cmd_te, !to_file);
+#endif
+ gtk_widget_set_sensitive(file_bt, to_file);
+ gtk_widget_set_sensitive(file_te, to_file);
+}
+
+static void
+print_cmd_toggle_detail(GtkWidget *widget, gpointer data _U_)
+{
+ GtkWidget *expand_all_rb, *as_displayed_rb, *hex_cb;
+ gboolean print_detail;
+
+ expand_all_rb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(widget),
+ PRINT_EXPAND_ALL_RB_KEY));
+ as_displayed_rb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(widget),
+ PRINT_AS_DISPLAYED_RB_KEY));
+ hex_cb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(widget),
+ PRINT_HEX_CB_KEY));
+ if (GTK_TOGGLE_BUTTON (widget)->active) {
+ /* They selected "Print detail" */
+ print_detail = TRUE;
+ } else {
+ /* They selected "Print summary" */
+ print_detail = FALSE;
+ }
+ gtk_widget_set_sensitive(expand_all_rb, print_detail);
+ gtk_widget_set_sensitive(as_displayed_rb, print_detail);
+ gtk_widget_set_sensitive(hex_cb, print_detail);
+}
+
+static void
+print_file_cb(GtkWidget *file_bt, gpointer file_te)
+{
+ GtkWidget *caller = gtk_widget_get_toplevel(file_bt);
+ GtkWidget *fs;
+
+ /* Has a file selection dialog box already been opened for that top-level
+ widget? */
+ fs = gtk_object_get_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY);
+
+ if (fs != NULL) {
+ /* Yes. Just re-activate that dialog box. */
+ reactivate_window(fs);
+ return;
+ }
+
+ fs = gtk_file_selection_new ("Ethereal: Print to File");
+ gtk_object_set_data(GTK_OBJECT(fs), PRINT_FILE_TE_KEY, file_te);
+
+ /* Set the E_FS_CALLER_PTR_KEY for the new dialog to point to our caller. */
+ gtk_object_set_data(GTK_OBJECT(fs), E_FS_CALLER_PTR_KEY, caller);
+
+ /* Set the E_FILE_SEL_DIALOG_PTR_KEY for the caller to point to us */
+ gtk_object_set_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY, fs);
+
+ /* Call a handler when the file selection box is destroyed, so we can inform
+ our caller, if any, that it's been destroyed. */
+ g_signal_connect(G_OBJECT(fs), "destroy",
+ G_CALLBACK(print_fs_destroy_cb), NULL);
+
+ g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), "clicked",
+ G_CALLBACK(print_fs_ok_cb), fs);
+
+ /* Connect the cancel_button to destroy the widget */
+ g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(fs)->cancel_button),
+ "clicked", G_CALLBACK(print_fs_cancel_cb), fs);
+
+ /* 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(fs, GTK_FILE_SELECTION(fs)->cancel_button);
+
+ gtk_widget_show(fs);
+}
+
+static void
+print_fs_ok_cb(GtkWidget *w _U_, gpointer data)
+{
+
+ gtk_entry_set_text(GTK_ENTRY(gtk_object_get_data(GTK_OBJECT(data),
+ PRINT_FILE_TE_KEY)),
+ gtk_file_selection_get_filename (GTK_FILE_SELECTION(data)));
+ gtk_widget_destroy(GTK_WIDGET(data));
+}
+
+static void
+print_fs_cancel_cb(GtkWidget *w _U_, gpointer data)
+{
+ gtk_widget_destroy(GTK_WIDGET(data));
+}
+
+static void
+print_fs_destroy_cb(GtkWidget *win, gpointer data _U_)
+{
+ GtkWidget *caller;
+
+ /* Get the widget that requested that we be popped up.
+ (It should arrange to destroy us if it's destroyed, so
+ that we don't get a pointer to a non-existent window here.) */
+ caller = gtk_object_get_data(GTK_OBJECT(win), E_FS_CALLER_PTR_KEY);
+
+ /* Tell it we no longer exist. */
+ gtk_object_set_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY, NULL);
+
+ /* Now nuke this window. */
+ gtk_grab_remove(GTK_WIDGET(win));
+ gtk_widget_destroy(GTK_WIDGET(win));
+}
+
+#ifdef _WIN32
+
+void setup_mswin_print( print_args_t *print_args) {
+
+ /*XXX should use temp file stuff in util routines */
+
+ char *path1;
+
+ path1 = tmpnam(NULL);
+
+ print_args->dest = g_strdup(path1);
+ print_args->to_file = TRUE;
+}
+#endif
+
+static void
+print_ok_cb(GtkWidget *ok_bt, gpointer parent_w)
+{
+ GtkWidget *button;
+ print_args_t print_args;
+#ifdef _WIN32
+ int win_printer_flag = FALSE;
+#endif
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(ok_bt),
+ PRINT_DEST_RB_KEY);
+ print_to_file = GTK_TOGGLE_BUTTON (button)->active;
+ print_args.to_file = print_to_file;
+
+ if (print_args.to_file) {
+ print_args.dest = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_object_get_data(GTK_OBJECT(ok_bt),
+ PRINT_FILE_TE_KEY))));
+ } else {
+#ifdef _WIN32
+ win_printer_flag = TRUE;
+ setup_mswin_print(&print_args);
+#else
+ print_args.dest = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_object_get_data(GTK_OBJECT(ok_bt),
+ PRINT_CMD_TE_KEY))));
+#endif
+ }
+
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(ok_bt),
+ PRINT_FORMAT_RB_KEY);
+ if (GTK_TOGGLE_BUTTON (button)->active)
+ print_format = PR_FMT_PS;
+ else
+ print_format = PR_FMT_TEXT;
+ print_args.format = print_format;
+
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(ok_bt),
+ PRINT_SUMMARY_RB_KEY);
+ print_args.print_summary = GTK_TOGGLE_BUTTON (button)->active;
+
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(ok_bt),
+ PRINT_HEX_CB_KEY);
+ print_args.print_hex = GTK_TOGGLE_BUTTON (button)->active;
+
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(ok_bt),
+ PRINT_EXPAND_ALL_RB_KEY);
+ print_args.expand_all = GTK_TOGGLE_BUTTON (button)->active;
+
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(ok_bt),
+ PRINT_SUPPRESS_UNMARKED_CB_KEY);
+ print_args.suppress_unmarked = GTK_TOGGLE_BUTTON (button)->active;
+
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+
+ /* Now print the packets */
+ if (!print_packets(&cfile, &print_args)) {
+ if (print_args.to_file)
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ file_write_error_message(errno), print_args.dest);
+ else
+ simple_dialog(ESD_TYPE_WARN, NULL, "Couldn't run print command %s.",
+ print_args.dest);
+ }
+
+#ifdef _WIN32
+ if (win_printer_flag) {
+ print_mswin(print_args.dest);
+
+ /* trash temp file */
+ remove(print_args.dest);
+ }
+#endif
+
+ g_free(print_args.dest);
+}
+
+static void
+print_close_cb(GtkWidget *close_bt _U_, gpointer parent_w)
+{
+ gtk_grab_remove(GTK_WIDGET(parent_w));
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+print_destroy_cb(GtkWidget *win, gpointer user_data _U_)
+{
+ GtkWidget *fs;
+
+ /* Is there a file selection dialog associated with this
+ Print File dialog? */
+ fs = gtk_object_get_data(GTK_OBJECT(win), E_FILE_SEL_DIALOG_PTR_KEY);
+
+ if (fs != NULL) {
+ /* Yes. Destroy it. */
+ gtk_widget_destroy(fs);
+ }
+
+ /* Note that we no longer have a "Print" dialog box. */
+ print_w = NULL;
+}
+
+/* Print a packet */
+void
+file_print_packet_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ FILE *fh;
+ print_args_t print_args;
+#ifdef _WIN32
+ int win_printer_flag = FALSE;
+#endif
+
+ switch (prefs.pr_dest) {
+
+ case PR_DEST_CMD:
+#ifdef _WIN32
+ /* "PR_DEST_CMD" means "to printer" on Windows */
+ win_printer_flag = TRUE;
+ setup_mswin_print(&print_args);
+ fh = fopen(print_args.dest, "w");
+ print_args.to_file = TRUE;
+ break;
+#else
+ fh = popen(prefs.pr_cmd, "w");
+ print_args.to_file = FALSE;
+ print_args.dest = prefs.pr_cmd;
+ break;
+#endif
+
+ case PR_DEST_FILE:
+ fh = fopen(prefs.pr_file, "w");
+ print_args.to_file = TRUE;
+ print_args.dest = prefs.pr_file;
+ break;
+
+ default:
+ fh = NULL; /* XXX - "can't happen" */
+ break;
+ }
+ if (fh == NULL) {
+ switch (prefs.pr_dest) {
+
+ case PR_DEST_CMD:
+ simple_dialog(ESD_TYPE_WARN, NULL, "Couldn't run print command %s.",
+ prefs.pr_cmd);
+ break;
+
+ case PR_DEST_FILE:
+ simple_dialog(ESD_TYPE_WARN, NULL, file_write_error_message(errno),
+ prefs.pr_file);
+ break;
+ }
+ return;
+ }
+
+ print_preamble(fh, prefs.pr_format);
+ print_args.format = prefs.pr_format;
+ print_args.print_summary = FALSE;
+ print_args.print_hex = FALSE;
+ print_args.expand_all = TRUE;
+ print_args.suppress_unmarked = FALSE;
+ proto_tree_print(&print_args, cfile.edt, fh);
+ print_finale(fh, prefs.pr_format);
+ close_print_dest(print_args.to_file, fh);
+
+#ifdef _WIN32
+ if (win_printer_flag) {
+ print_mswin(print_args.dest);
+
+ /* trash temp file */
+ remove(print_args.dest);
+ g_free(print_args.dest);
+ }
+#endif
+}
diff --git a/gtk2/print_mswin.c b/gtk2/print_mswin.c
new file mode 100644
index 0000000000..7eff5835eb
--- /dev/null
+++ b/gtk2/print_mswin.c
@@ -0,0 +1,202 @@
+/* print_mswin.c
+ * Printing support for MSWindows
+ *
+ * $$
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 2002, Jeffrey C. Foster <jfoste@woodward.com>
+ *
+ * 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 original code was from the Technet Article Q139652 :
+ * HOWTO: Print a Document
+ */
+
+
+#include "windows.h"
+
+#ifdef __WIN32__
+#include <winspool.h>
+#endif
+
+#include <stdio.h>
+
+BOOL CALLBACK abort_proc( HDC hDC, int Error );
+HDC get_printer_dc(void);
+void init_doc_struct( DOCINFO* di, char* docname);
+void print_file( char* file_name, HDC hdc);
+
+
+
+void print_mswin(char *file_name)
+
+ {
+ HDC hDC;
+ DOCINFO di;
+
+ HWND hWndParent = HWND_DESKTOP; /* would be better to be a real window */
+
+ /* Need a printer DC to print to. */
+ hDC = get_printer_dc();
+
+ /* Did you get a good DC?, Cancel will return NULL also, so what to do? */
+ if( !hDC)
+ {
+ return;
+ }
+
+ /* You always have to use an AbortProc(). */
+ if( SetAbortProc( hDC, abort_proc ) == SP_ERROR )
+ {
+ MessageBox( NULL, "Error setting up AbortProc",
+ "Error", MB_APPLMODAL | MB_OK);
+ return;
+ }
+
+ /* Init the DOCINFO and start the document. */
+ init_doc_struct( &di, "MyDoc");
+ StartDoc( hDC, &di );
+
+ /* Print one page. */
+ StartPage( hDC );
+ print_file(file_name, hDC );
+ EndPage( hDC );
+
+ /* Indicate end of document. */
+ EndDoc( hDC );
+
+ /* Clean up */
+ DeleteDC( hDC );
+ }
+
+ /*===============================*/
+ /* Obtain printer device context */
+ /* ==============================*/
+ HDC get_printer_dc(void)
+ {
+ PRINTDLG pdlg;
+
+ /* Initialize the PRINTDLG structure. */
+ memset( &pdlg, 0, sizeof( PRINTDLG ) );
+ pdlg.lStructSize = sizeof( PRINTDLG );
+ /* Set the flag to return printer DC. */
+ pdlg.Flags = PD_RETURNDC;
+
+ /* Invoke the printer dialog box. */
+ PrintDlg( &pdlg );
+
+ /* hDC member of the PRINTDLG structure contains the printer DC. */
+ return pdlg.hDC;
+ }
+
+ /*===============================*/
+ /* The Abort Procudure */
+ /* ==============================*/
+ BOOL CALLBACK abort_proc( HDC hDC, int Error )
+ {
+ MSG msg;
+ while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
+ {
+ TranslateMessage( &msg );
+ DispatchMessage( &msg );
+ }
+ return TRUE;
+ }
+
+ /*===============================*/
+ /* Initialize DOCINFO structure */
+ /* ==============================*/
+ void init_doc_struct( DOCINFO* di, char* docname)
+ {
+ /* Always zero it before using it. */
+ memset( di, 0, sizeof( DOCINFO ) );
+ /* Fill in the required members. */
+ di->cbSize = sizeof( DOCINFO );
+ di->lpszDocName = docname;
+ }
+
+ /*===============================*/
+ /* Drawing on the DC */
+ /* ==============================*/
+void print_file( char *file_name, HDC hdc) {
+
+ #define max_buf_size 1024
+ #define max_lines 66
+ #define y_offset 5
+ #define x_offset 5
+
+ FILE* fh1;
+ int results, cnt=0, y_pos = y_offset, y_cnt = 0;
+ char buf[ max_buf_size];
+ char ch;
+ TEXTMETRIC tm;
+
+ GetTextMetrics(hdc, &tm);
+ SetMapMode (hdc, MM_TEXT);
+
+
+ fh1 = fopen( file_name, "r" );
+ if( !fh1 )
+ perror( "open failed on input file" );
+
+ else {
+ while ((results = fread( &ch, 1, 1, fh1 )) != 0) {
+
+/* if end of line send buffer and more y position */
+
+ if ( ch == 0x0a){
+ buf[ cnt] = 0;
+ TextOut(hdc, x_offset,y_pos, buf, strlen(buf));
+ y_pos += tm.tmHeight;
+ cnt = 0;
+ if ( ++y_cnt == max_lines){
+ /* Print one page. */
+ EndPage( hdc );
+ StartPage( hdc );
+ y_pos = y_offset;
+ y_cnt = 0;
+ }
+
+/* if line buffer is full, dump it */
+ }else { if ( cnt == ( max_buf_size - 1)) {
+ buf[ cnt] = 0;
+ TextOut(hdc, x_offset, y_pos, buf, strlen(buf));
+ y_pos += tm.tmHeight;
+ cnt = 0;
+
+ if ( ++y_cnt == max_lines){
+ /* Print one page. */
+ EndPage( hdc );
+ StartPage( hdc );
+ y_pos = y_offset;
+ y_cnt = 0;
+ }
+ }
+
+ buf[ cnt++] = ch;
+ }
+ }
+/*XXX need feof test here ? */
+
+/* Print the last text if needed */
+ if ( cnt > 0) {
+ buf[ cnt] = 0;
+ TextOut(hdc, 0,y_pos, buf, strlen(buf));
+ }
+ fclose(fh1);
+}
+}
diff --git a/gtk2/print_mswin.h b/gtk2/print_mswin.h
new file mode 100644
index 0000000000..3c42d97898
--- /dev/null
+++ b/gtk2/print_mswin.h
@@ -0,0 +1,27 @@
+/* print_mswin.h
+ * Printing support for MSWindows
+ *
+ * $$
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 2002, Jeffrey C. Foster <jfoste@woodward.com>
+ *
+ * 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.
+ */
+
+
+
+void print_mswin(char *file_name);
diff --git a/gtk2/print_prefs.c b/gtk2/print_prefs.c
new file mode 100644
index 0000000000..38d18d1292
--- /dev/null
+++ b/gtk2/print_prefs.c
@@ -0,0 +1,250 @@
+/* print_prefs.c
+ * Dialog boxes for preferences for printing
+ *
+ * $Id: print_prefs.c,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <gtk/gtk.h>
+
+#include "globals.h"
+#include "print_prefs.h"
+#include "keys.h"
+#include "print.h"
+#include "prefs.h"
+#include "prefs_dlg.h"
+#include "util.h"
+#include "ui_util.h"
+#include "dlg_utils.h"
+
+static void printer_opts_file_cb(GtkWidget *w, gpointer te);
+static void printer_opts_fs_ok_cb(GtkWidget *w, gpointer data);
+static void printer_opts_fs_cancel_cb(GtkWidget *w, gpointer data);
+static void printer_opts_fs_destroy_cb(GtkWidget *win, gpointer data);
+
+#define E_FS_CALLER_PTR_KEY "fs_caller_ptr"
+#define E_FILE_SEL_DIALOG_PTR_KEY "file_sel_dialog_ptr"
+#define E_PRINT_FORMAT_KEY "print_format"
+#define E_PRINT_DESTINATION_KEY "print_destination"
+
+static const enum_val_t print_format_vals[] = {
+ { "Plain Text", PR_FMT_TEXT },
+ { "Postscript", PR_FMT_PS },
+ { NULL, 0 }
+};
+
+static const enum_val_t print_dest_vals[] = {
+#ifdef _WIN32
+ /* "PR_DEST_CMD" means "to printer" on Windows */
+ { "Printer", PR_DEST_CMD },
+#else
+ { "Command", PR_DEST_CMD },
+#endif
+ { "File", PR_DEST_FILE },
+ { NULL, 0 }
+};
+
+GtkWidget * printer_prefs_show(void)
+{
+ GtkWidget *main_vb, *main_tb, *button;
+#ifndef _WIN32
+ GtkWidget *cmd_te;
+#endif
+ GtkWidget *file_bt_hb, *file_bt, *file_te;
+
+ /* Enclosing containers for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+
+ main_tb = gtk_table_new(4, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
+ gtk_widget_show(main_tb);
+
+ /* Output format */
+ button = create_preference_radio_buttons(main_tb, 0, "Format:",
+ NULL, print_format_vals, prefs.pr_format);
+ gtk_object_set_data(GTK_OBJECT(main_vb), E_PRINT_FORMAT_KEY, button);
+
+ /* Output destination */
+ button = create_preference_radio_buttons(main_tb, 1, "Print to:",
+ NULL, print_dest_vals, prefs.pr_dest);
+ gtk_object_set_data(GTK_OBJECT(main_vb), E_PRINT_DESTINATION_KEY,
+ button);
+
+#ifndef _WIN32
+ /* Command text entry */
+ cmd_te = create_preference_entry(main_tb, 2, "Command:", NULL,
+ prefs.pr_cmd);
+ gtk_object_set_data(GTK_OBJECT(main_vb), PRINT_CMD_TE_KEY, cmd_te);
+#endif
+
+ /* File button and text entry */
+ file_bt_hb = gtk_hbox_new(FALSE, 0);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), file_bt_hb, 0, 1, 3, 4);
+ gtk_widget_show(file_bt_hb);
+
+ file_bt = gtk_button_new_with_label("File:");
+ gtk_box_pack_end(GTK_BOX(file_bt_hb), file_bt, FALSE, FALSE, 0);
+ gtk_widget_show(file_bt);
+
+ file_te = gtk_entry_new();
+ gtk_object_set_data(GTK_OBJECT(main_vb), PRINT_FILE_TE_KEY, file_te);
+ if (prefs.pr_file) gtk_entry_set_text(GTK_ENTRY(file_te), prefs.pr_file);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), file_te, 1, 2, 3, 4);
+ gtk_widget_show(file_te);
+
+ g_signal_connect(G_OBJECT(file_bt), "clicked",
+ G_CALLBACK(printer_opts_file_cb), GTK_OBJECT(file_te));
+
+ gtk_widget_show(main_vb);
+ return(main_vb);
+}
+
+
+static void
+printer_opts_file_cb(GtkWidget *file_bt, gpointer file_te) {
+ GtkWidget *caller = gtk_widget_get_toplevel(file_bt);
+ GtkWidget *fs;
+
+ /* Has a file selection dialog box already been opened for that top-level
+ widget? */
+ fs = gtk_object_get_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY);
+
+ if (fs != NULL) {
+ /* Yes. Just re-activate that dialog box. */
+ reactivate_window(fs);
+ return;
+ }
+
+ fs = gtk_file_selection_new ("Ethereal: Print to a File");
+ gtk_object_set_data(GTK_OBJECT(fs), PRINT_FILE_TE_KEY, file_te);
+
+ /* Set the E_FS_CALLER_PTR_KEY for the new dialog to point to our caller. */
+ gtk_object_set_data(GTK_OBJECT(fs), E_FS_CALLER_PTR_KEY, caller);
+
+ /* Set the E_FILE_SEL_DIALOG_PTR_KEY for the caller to point to us */
+ gtk_object_set_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY, fs);
+
+ /* Call a handler when the file selection box is destroyed, so we can inform
+ our caller, if any, that it's been destroyed. */
+ g_signal_connect(G_OBJECT(fs), "destroy",
+ G_CALLBACK(printer_opts_fs_destroy_cb), NULL);
+
+ g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), "clicked",
+ G_CALLBACK(printer_opts_fs_ok_cb), fs);
+
+ /* Connect the cancel_button to destroy the widget */
+ g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->cancel_button), "clicked",
+ G_CALLBACK(printer_opts_fs_cancel_cb), fs);
+
+ /* 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(fs, GTK_FILE_SELECTION(fs)->cancel_button);
+
+ gtk_widget_show(fs);
+}
+
+static void
+printer_opts_fs_ok_cb(GtkWidget *w, gpointer data) {
+
+ gtk_entry_set_text(GTK_ENTRY(gtk_object_get_data(GTK_OBJECT(data),
+ PRINT_FILE_TE_KEY)),
+ gtk_file_selection_get_filename (GTK_FILE_SELECTION(data)));
+ printer_opts_fs_cancel_cb(w, data);
+}
+
+static void
+printer_opts_fs_cancel_cb(GtkWidget *w _U_, gpointer data) {
+
+ gtk_widget_destroy(GTK_WIDGET(data));
+}
+
+static void
+printer_opts_fs_destroy_cb(GtkWidget *win, gpointer data _U_)
+{
+ GtkWidget *caller;
+
+ /* Get the widget that requested that we be popped up.
+ (It should arrange to destroy us if it's destroyed, so
+ that we don't get a pointer to a non-existent window here.) */
+ caller = gtk_object_get_data(GTK_OBJECT(win), E_FS_CALLER_PTR_KEY);
+
+ /* Tell it we no longer exist. */
+ gtk_object_set_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY, NULL);
+
+ /* Now nuke this window. */
+ gtk_grab_remove(GTK_WIDGET(win));
+ gtk_widget_destroy(GTK_WIDGET(win));
+}
+
+void
+printer_prefs_fetch(GtkWidget *w)
+{
+ prefs.pr_format = fetch_preference_radio_buttons_val(
+ gtk_object_get_data(GTK_OBJECT(w), E_PRINT_FORMAT_KEY),
+ print_format_vals);
+
+ prefs.pr_dest = fetch_preference_radio_buttons_val(
+ gtk_object_get_data(GTK_OBJECT(w), E_PRINT_DESTINATION_KEY),
+ print_dest_vals);
+
+#ifndef _WIN32
+ if (prefs.pr_cmd)
+ g_free(prefs.pr_cmd);
+ prefs.pr_cmd = g_strdup(gtk_entry_get_text(
+ GTK_ENTRY(gtk_object_get_data(GTK_OBJECT(w),
+ PRINT_CMD_TE_KEY))));
+#endif
+
+ if (prefs.pr_file)
+ g_free(prefs.pr_file);
+ prefs.pr_file = g_strdup(gtk_entry_get_text(
+ GTK_ENTRY(gtk_object_get_data(GTK_OBJECT(w),
+ PRINT_FILE_TE_KEY))));
+}
+
+void
+printer_prefs_apply(GtkWidget *w _U_)
+{
+}
+
+void
+printer_prefs_destroy(GtkWidget *w)
+{
+ GtkWidget *caller = gtk_widget_get_toplevel(w);
+ GtkWidget *fs;
+
+ /* Is there a file selection dialog associated with this
+ Preferences dialog? */
+ fs = gtk_object_get_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY);
+
+ if (fs != NULL) {
+ /* Yes. Destroy it. */
+ gtk_widget_destroy(fs);
+ }
+}
diff --git a/gtk2/print_prefs.h b/gtk2/print_prefs.h
new file mode 100644
index 0000000000..2574cab578
--- /dev/null
+++ b/gtk2/print_prefs.h
@@ -0,0 +1,34 @@
+/* print_prefs.h
+ * Definitions for print preferences window
+ *
+ * $Id: print_prefs.h,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+#ifndef __PRINT_PREFS_H__
+#define __PRINT_PREFS_H__
+
+GtkWidget *printer_prefs_show(void);
+void printer_prefs_fetch(GtkWidget *w);
+void printer_prefs_apply(GtkWidget *w);
+void printer_prefs_destroy(GtkWidget *w);
+
+#endif
diff --git a/gtk2/progress_dlg.c b/gtk2/progress_dlg.c
new file mode 100644
index 0000000000..1adb2c9f9e
--- /dev/null
+++ b/gtk2/progress_dlg.c
@@ -0,0 +1,380 @@
+/* progress_dlg.c
+ * Routines for progress-bar (modal) dialog
+ *
+ * $Id: progress_dlg.c,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gtkglobals.h"
+#include "dlg_utils.h"
+#include "progress_dlg.h"
+
+#define PROG_BAR_KEY "progress_bar"
+
+static gint delete_event_cb(GtkWidget *w, GdkEvent *event, gpointer data);
+static void stop_cb(GtkWidget *w, gpointer data);
+
+/*
+ * Define the structure describing a progress dialog.
+ */
+struct progdlg {
+ GtkWidget *dlg_w; /* top-level window widget */
+ GTimeVal start_time;
+ GtkLabel *status_lb;
+ GtkLabel *elapsed_lb;
+ GtkLabel *time_left_lb;
+ GtkLabel *percentage_lb;
+ gchar title[200];
+};
+
+/*
+ * Create and pop up the progress dialog; allocate a "progdlg_t"
+ * and initialize it to contain all information the implementation
+ * needs in order to manipulate the dialog, and return a pointer to
+ * it.
+ *
+ * The first argument is the task to do, e.g. "Loading".
+ * The second argument is the item to do, e.g. "capture.cap".
+ * The third argument is the string to put in the "stop this operation" button.
+ * The fourth argument is a pointer to a Boolean variable that will be
+ * set to TRUE if the user hits that button.
+ *
+ * XXX - provide a way to specify the progress in units, with the total
+ * number of units specified as an argument when the progress dialog
+ * is created; updates would be given in units, with the progress dialog
+ * code computing the percentage, and the progress bar would have a
+ * label "0" on the left and <total number of units> on the right, with
+ * a label in the middle giving the number of units we've processed
+ * so far. This could be used when filtering packets, for example; we
+ * wouldn't always use it, as we have no idea how many packets are to
+ * be read.
+ */
+progdlg_t *
+create_progress_dlg(const gchar *task_title, const gchar *item_title,
+ const gchar *stop_title, gboolean *stop_flag)
+{
+ progdlg_t *dlg;
+ GtkWidget *dlg_w, *main_vb, *title_lb, *status_lb, *elapsed_lb, *time_left_lb, *percentage_lb;
+ GtkWidget *prog_bar, *bbox, *stop_bt;
+ GtkWidget *static_vb, *tmp_lb, *main_hb, *dynamic_vb, *percentage_hb;
+ gchar tmp[100];
+
+ dlg = g_malloc(sizeof (progdlg_t));
+
+ g_snprintf(dlg->title, sizeof(dlg->title), "%s: %s",
+ task_title, item_title);
+
+ dlg_w = dlg_window_new(dlg->title);
+ gtk_window_set_modal(GTK_WINDOW(dlg_w), TRUE);
+
+ /*
+ * Container for dialog widgets.
+ */
+ main_vb = gtk_vbox_new(FALSE, 1);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(dlg_w), main_vb);
+
+ /*
+ * Static labels (left dialog side, labels aligned to the right)
+ */
+ static_vb = gtk_vbox_new(FALSE, 1);
+ g_snprintf (tmp, sizeof(tmp), "%s:", task_title);
+ tmp_lb = gtk_label_new(tmp);
+ gtk_misc_set_alignment(GTK_MISC(tmp_lb), 1.0, 0.0);
+ gtk_box_pack_start(GTK_BOX(static_vb), tmp_lb, FALSE, TRUE, 3);
+ tmp_lb = gtk_label_new("Status:");
+ gtk_misc_set_alignment(GTK_MISC(tmp_lb), 1.0, 0.0);
+ gtk_box_pack_start(GTK_BOX(static_vb), tmp_lb, FALSE, TRUE, 3);
+ tmp_lb = gtk_label_new("Elapsed Time:");
+ gtk_misc_set_alignment(GTK_MISC(tmp_lb), 1.0, 0.0);
+ gtk_box_pack_start(GTK_BOX(static_vb), tmp_lb, FALSE, TRUE, 3);
+ tmp_lb = gtk_label_new("Time Left:");
+ gtk_misc_set_alignment(GTK_MISC(tmp_lb), 1.0, 0.0);
+ gtk_box_pack_start(GTK_BOX(static_vb), tmp_lb, FALSE, TRUE, 3);
+ tmp_lb = gtk_label_new("Progress:");
+ gtk_misc_set_alignment(GTK_MISC(tmp_lb), 1.0, 0.0);
+ gtk_box_pack_start(GTK_BOX(static_vb), tmp_lb, FALSE, TRUE, 3);
+
+
+ /*
+ * Dynamic labels (right dialog side, labels aligned to the left)
+ */
+ dynamic_vb = gtk_vbox_new(FALSE, 1);
+
+ /*
+ * Put the item_title here as a label indicating what we're
+ * doing; set its alignment and padding so it's aligned on the
+ * left.
+ */
+ title_lb = gtk_label_new(item_title);
+ gtk_box_pack_start(GTK_BOX(dynamic_vb), title_lb, FALSE, TRUE, 3);
+ gtk_misc_set_alignment(GTK_MISC(title_lb), 0.0, 0.0);
+ gtk_misc_set_padding(GTK_MISC(title_lb), 0.0, 0.0);
+
+ /* same for "Status" */
+ status_lb = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(dynamic_vb), status_lb, FALSE, TRUE, 3);
+ gtk_misc_set_alignment(GTK_MISC(status_lb), 0.0, 0.0);
+ gtk_misc_set_padding(GTK_MISC(status_lb), 0.0, 0.0);
+ dlg->status_lb = (GtkLabel *) status_lb;
+
+ /* same for "Elapsed Time" */
+ elapsed_lb = gtk_label_new("00:00");
+ gtk_box_pack_start(GTK_BOX(dynamic_vb), elapsed_lb, FALSE, TRUE, 3);
+ gtk_misc_set_alignment(GTK_MISC(elapsed_lb), 0.0, 0.0);
+ gtk_misc_set_padding(GTK_MISC(elapsed_lb), 0.0, 0.0);
+ dlg->elapsed_lb = (GtkLabel *) elapsed_lb;
+
+ /* same for "Time Left" */
+ time_left_lb = gtk_label_new("--:--");
+ gtk_box_pack_start(GTK_BOX(dynamic_vb), time_left_lb, FALSE, TRUE, 3);
+ gtk_misc_set_alignment(GTK_MISC(time_left_lb), 0.0, 0.0);
+ gtk_misc_set_padding(GTK_MISC(time_left_lb), 0.0, 0.0);
+ dlg->time_left_lb = (GtkLabel *) time_left_lb;
+
+ /*
+ * The progress bar (in its own horizontal box, including percentage
+ * value)
+ */
+ percentage_hb = gtk_hbox_new(FALSE, 1);
+ gtk_box_pack_start(GTK_BOX(dynamic_vb), percentage_hb, FALSE, TRUE, 3);
+
+ prog_bar = gtk_progress_bar_new();
+ gtk_box_pack_start(GTK_BOX(percentage_hb), prog_bar, FALSE, TRUE, 3);
+
+ percentage_lb = gtk_label_new(" 0%");
+ gtk_misc_set_alignment(GTK_MISC(percentage_lb), 0.0, 0.0);
+ gtk_box_pack_start(GTK_BOX(percentage_hb), percentage_lb, FALSE, TRUE,
+ 3);
+ dlg->percentage_lb = (GtkLabel *) percentage_lb;
+
+ /*
+ * Attach a pointer to the progress bar widget to the top-level widget.
+ */
+ gtk_object_set_data(GTK_OBJECT(dlg_w), PROG_BAR_KEY, prog_bar);
+
+ /*
+ * Static and dynamic boxes are now complete
+ */
+ main_hb = gtk_hbox_new(FALSE, 1);
+ gtk_box_pack_start(GTK_BOX(main_hb), static_vb, FALSE, TRUE, 3);
+ gtk_box_pack_start(GTK_BOX(main_hb), dynamic_vb, FALSE, TRUE, 3);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_hb, FALSE, TRUE, 3);
+
+ /*
+ * Button row: cancel button.
+ * (We put it in an HButtonBox, even though there's only one
+ * of them, so that it doesn't expand to the width of the window.)
+ */
+ bbox = gtk_hbutton_box_new();
+ gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
+ gtk_container_add(GTK_CONTAINER(main_vb), bbox);
+
+ /*
+ * Allow user to either click a "stop this operation" button, or
+ * the close button on the window, to stop an operation in
+ * progress.
+ */
+ stop_bt = gtk_button_new_with_label(stop_title);
+ gtk_box_pack_start(GTK_BOX (bbox), stop_bt, TRUE, TRUE, 0);
+ g_signal_connect(G_OBJECT(stop_bt), "clicked",
+ G_CALLBACK(stop_cb), (gpointer) stop_flag);
+ g_signal_connect(G_OBJECT(dlg_w), "delete_event",
+ G_CALLBACK(delete_event_cb), (gpointer) stop_flag);
+ GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default(stop_bt);
+ GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default(stop_bt);
+
+ gtk_widget_show_all(dlg_w);
+
+ dlg->dlg_w = dlg_w;
+
+ g_get_current_time(&dlg->start_time);
+
+ return dlg;
+}
+
+progdlg_t *
+delayed_create_progress_dlg(const gchar *task_title, const gchar *item_title,
+ const gchar *stop_title, gboolean *stop_flag,
+ const GTimeVal *start_time, gfloat progress)
+{
+ GTimeVal time_now;
+ gfloat delta_time;
+ gfloat min_display;
+ progdlg_t *dlg;
+
+#define INIT_DELAY 0.5 * 1e6
+#define MIN_DISPLAY_DEFAULT 2.0 * 1e6
+
+ /* Create a progress dialog, but only if it's not likely to disappear
+ * immediately, which can be disconcerting for the user.
+ *
+ * Arguments are as for create_progress_dlg(), plus:
+ *
+ * (a) A pointer to a GTimeVal structure which holds the time at which
+ * the caller started to process the data.
+ * (b) The current progress as a real number between 0 and 1.
+ */
+ g_get_current_time(&time_now);
+
+ /* Get the time elapsed since the caller started processing the data */
+
+ delta_time = (time_now.tv_sec - start_time->tv_sec) * 1e6 +
+ time_now.tv_usec - start_time->tv_usec;
+
+ /* Do nothing for the first INIT_DELAY microseconds */
+ if (delta_time < INIT_DELAY)
+ return NULL;
+
+ /* If we create the progress dialog we want it to be displayed for a
+ * minimum of MIN_DISPLAY_DEFAULT microseconds. However, if we
+ * previously estimated that the progress dialog didn't need to be
+ * created and the caller's processing is slowing down (perhaps due
+ * to the action of the operating system's scheduler on a compute-
+ * intensive task), we tail off the minimum display time such that
+ * the progress dialog will always be created after
+ * 2*MIN_DISPLAY_DEFAULT microseconds.
+ */
+
+ if (delta_time <= INIT_DELAY + MIN_DISPLAY_DEFAULT)
+ min_display = MIN_DISPLAY_DEFAULT;
+ else
+ min_display = 2 * MIN_DISPLAY_DEFAULT - delta_time;
+ /* = MIN_DISPLAY_DEFAULT - (delta_time - MIN_DISPLAY_DEFAULT) */
+
+ /* Assuming the progress increases linearly, see if the progress
+ * dialog would be displayed for at least min_display microseconds if
+ * we created it now.
+ */
+
+ if (progress >= (delta_time / (delta_time + min_display)))
+ return NULL;
+
+ dlg = create_progress_dlg(task_title, item_title, stop_title,
+ stop_flag);
+
+ /* set dialog start_time to the start of processing, not box creation */
+ dlg->start_time = *start_time;
+
+ return dlg;
+}
+
+/*
+ * Called when the dialog box is to be deleted.
+ * Set the "stop" flag to TRUE, and return TRUE - we don't want the dialog
+ * box deleted now, our caller will do so when they see that the
+ * "stop" flag is TRUE and abort the operation.
+ */
+static gint
+delete_event_cb(GtkWidget *w _U_, GdkEvent *event _U_, gpointer data)
+{
+ gboolean *stop_flag = (gboolean *) data;
+
+ *stop_flag = TRUE;
+ return TRUE;
+}
+
+/*
+ * Called when the "stop this operation" button is clicked.
+ * Set the "stop" flag to TRUE; we don't have to destroy the dialog
+ * box, as our caller will do so when they see that the "stop" flag is
+ * true and abort the operation.
+ */
+static void
+stop_cb(GtkWidget *w _U_, gpointer data)
+{
+ gboolean *stop_flag = (gboolean *) data;
+
+ *stop_flag = TRUE;
+}
+
+/*
+ * Update the progress information of the progress dialog box.
+ */
+void
+update_progress_dlg(progdlg_t *dlg, gfloat percentage, char *status)
+{
+ GtkWidget *dlg_w = dlg->dlg_w;
+ GtkWidget *prog_bar;
+ GTimeVal time_now;
+ gfloat delta_time;
+ gulong ul_left;
+ gulong ul_elapsed;
+ gulong ul_percentage;
+ gchar tmp[100];
+
+ /* calculate some timing values */
+ g_get_current_time(&time_now);
+
+ delta_time = (time_now.tv_sec - dlg->start_time.tv_sec) * 1e6 +
+ time_now.tv_usec - dlg->start_time.tv_usec;
+ ul_percentage = percentage * 100;
+ ul_elapsed = delta_time / 1000 / 1000;
+
+ /* update labels */
+ g_snprintf(tmp, sizeof(tmp), "%lu%% of %s", ul_percentage, dlg->title);
+ gtk_window_set_title(GTK_WINDOW(dlg_w), tmp);
+
+ gtk_label_set_text(dlg->status_lb, status);
+
+ g_snprintf(tmp, sizeof(tmp), "%lu%%", ul_percentage);
+ gtk_label_set_text(dlg->percentage_lb, tmp);
+
+ g_snprintf(tmp, sizeof(tmp), "%02lu:%02lu", ul_elapsed / 60,
+ ul_elapsed % 60);
+ gtk_label_set_text(dlg->elapsed_lb, tmp);
+
+ /* show "Time Left" only,
+ * if at least 5% and 3 seconds running (to get a useful estimation) */
+ if (ul_percentage >= 5 && delta_time >= 3 * 1e6) {
+ ul_left = (delta_time / percentage - delta_time) / 1000 / 1000;
+ g_snprintf(tmp, sizeof(tmp), "%02lu:%02lu", ul_left / 60,
+ ul_left % 60);
+ gtk_label_set_text(dlg->time_left_lb, tmp);
+ }
+
+ /* update progress bar */
+ prog_bar = gtk_object_get_data(GTK_OBJECT(dlg_w), PROG_BAR_KEY);
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(prog_bar), percentage);
+
+ /*
+ * Flush out the update and process any input events.
+ */
+ while (gtk_events_pending())
+ gtk_main_iteration();
+}
+
+/*
+ * Destroy the progress dialog.
+ */
+void
+destroy_progress_dlg(progdlg_t *dlg)
+{
+ GtkWidget *dlg_w = dlg->dlg_w;
+
+ gtk_widget_destroy(GTK_WIDGET(dlg_w));
+ g_free(dlg);
+}
diff --git a/gtk2/proto_dlg.c b/gtk2/proto_dlg.c
new file mode 100644
index 0000000000..957f407d8f
--- /dev/null
+++ b/gtk2/proto_dlg.c
@@ -0,0 +1,412 @@
+/* proto_dlg.c
+ *
+ * $Id: proto_dlg.c,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Laurent Deniel <deniel@worldnet.fr>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 2000 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "prefs.h"
+#include "globals.h"
+#include "main.h"
+#include "util.h"
+#include "ui_util.h"
+#include "dlg_utils.h"
+#include "proto_dlg.h"
+
+static gboolean proto_delete_cb(GtkWidget *, gpointer);
+static void proto_ok_cb(GtkWidget *, gpointer);
+static void proto_apply_cb(GtkWidget *, gpointer);
+static void proto_cancel_cb(GtkWidget *, gpointer);
+static void proto_destroy_cb(GtkWidget *, gpointer);
+
+static void show_proto_selection(GtkWidget *main, GtkWidget *container);
+static gboolean set_proto_selection(GtkWidget *);
+static gboolean revert_proto_selection(void);
+
+static void toggle_all_cb(GtkWidget *button, gpointer parent_w);
+static void enable_all_cb(GtkWidget *button, gpointer parent_w);
+static void disable_all_cb(GtkWidget *button, gpointer parent_w);
+
+static GtkWidget *proto_w = NULL;
+
+/* list of protocols */
+static GSList *protocol_list = NULL;
+
+typedef struct protocol_data {
+ char *name;
+ char *abbrev;
+ int hfinfo_index;
+ gboolean was_enabled;
+} protocol_data_t;
+
+void proto_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+
+ GtkWidget *main_vb, *bbox, *proto_nb, *apply_bt, *cancel_bt, *ok_bt,
+ *label, *scrolled_w, *selection_vb, *button;
+
+ if (proto_w != NULL) {
+ reactivate_window(proto_w);
+ return;
+ }
+
+ proto_w = dlg_window_new("Ethereal: Protocol");
+ g_signal_connect(G_OBJECT(proto_w), "delete_event",
+ G_CALLBACK(proto_delete_cb), NULL);
+ g_signal_connect(G_OBJECT(proto_w), "destroy",
+ G_CALLBACK(proto_destroy_cb), NULL);
+ gtk_widget_set_size_request(GTK_WIDGET(proto_w), DEF_WIDTH * 2/3,
+ DEF_HEIGHT * 2/3);
+
+ /* Container for each row of widgets */
+
+ main_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 1);
+ gtk_container_add(GTK_CONTAINER(proto_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* Protocol topics container */
+
+ proto_nb = gtk_notebook_new();
+ gtk_container_add(GTK_CONTAINER(main_vb), proto_nb);
+ /* XXX do not know why I need this to fill all space around buttons */
+ gtk_widget_set_size_request(GTK_WIDGET(proto_nb), DEF_WIDTH * 2/3 - 50,
+ DEF_HEIGHT * 2/3 - 50);
+
+ /* Protocol selection panel ("enable/disable" protocols) */
+
+ selection_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_border_width(GTK_CONTAINER(selection_vb), 1);
+ label = gtk_label_new("Button pressed: protocol decoding is enabled");
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(selection_vb), label, FALSE, FALSE, 0);
+ scrolled_w = scrolled_window_new(NULL, NULL);
+ gtk_container_set_border_width(GTK_CONTAINER(scrolled_w), 1);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_w),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start(GTK_BOX(selection_vb), scrolled_w, TRUE, TRUE, 0);
+ show_proto_selection(proto_w, scrolled_w);
+ gtk_widget_show(scrolled_w);
+ gtk_widget_show(selection_vb);
+ label = gtk_label_new("Decoding");
+ gtk_notebook_append_page(GTK_NOTEBOOK(proto_nb), selection_vb, label);
+ label = gtk_label_new("Note that when a protocol is disabled, "
+ "all linked sub-protocols are as well");
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(selection_vb), label, FALSE, FALSE, 0);
+
+
+ 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_box_pack_start(GTK_BOX(selection_vb), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ /* Toggle All */
+ button = gtk_button_new_with_label("Toggle All");
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(toggle_all_cb), GTK_OBJECT(proto_w));
+ gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ /* Enable All */
+ button = gtk_button_new_with_label("Enable All");
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(enable_all_cb), GTK_OBJECT(proto_w));
+ gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ /* Disable All */
+ button = gtk_button_new_with_label("Disable All");
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(disable_all_cb), GTK_OBJECT(proto_w));
+ gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+
+ /* XXX add other protocol-related panels here ... */
+
+ gtk_widget_show(proto_nb);
+
+ /* Ok, Apply, Cancel Buttons */
+
+ 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(main_vb), bbox);
+ gtk_widget_show(bbox);
+
+ ok_bt = gtk_button_new_from_stock(GTK_STOCK_OK);
+ g_signal_connect(G_OBJECT(ok_bt), "clicked",
+ G_CALLBACK(proto_ok_cb), GTK_OBJECT(proto_w));
+ GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start(GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
+ gtk_widget_grab_default(ok_bt);
+ gtk_widget_show(ok_bt);
+
+ apply_bt = gtk_button_new_from_stock(GTK_STOCK_APPLY);
+ g_signal_connect(G_OBJECT(apply_bt), "clicked",
+ G_CALLBACK(proto_apply_cb), GTK_OBJECT(proto_w));
+ GTK_WIDGET_SET_FLAGS(apply_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start(GTK_BOX (bbox), apply_bt, TRUE, TRUE, 0);
+ gtk_widget_show(apply_bt);
+
+ cancel_bt = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+ g_signal_connect(G_OBJECT(cancel_bt), "clicked",
+ G_CALLBACK(proto_cancel_cb), GTK_OBJECT(proto_w));
+ GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start(GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
+ gtk_widget_show(cancel_bt);
+
+ dlg_set_cancel(proto_w, cancel_bt);
+
+ gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(proto_w));
+ gtk_widget_show(proto_w);
+
+} /* proto_cb */
+
+
+/* Toggle All */
+static void
+toggle_all_cb(GtkWidget *button _U_, gpointer parent_w)
+{
+
+ GSList *entry;
+
+ for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
+ GtkWidget *button;
+ protocol_data_t *p = entry->data;
+
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w),
+ p->abbrev);
+ /* gtk_toggle_button_toggled() didn't work for me... */
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
+ !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)));
+ }
+}
+
+/* Enable/Disable All Helper */
+static void
+set_active_all(gpointer parent_w, gboolean new_state)
+{
+
+ GSList *entry;
+
+ for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
+ GtkWidget *button;
+ protocol_data_t *p = entry->data;
+
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w),
+ p->abbrev);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), new_state);
+ }
+}
+
+/* Enable All */
+static void
+enable_all_cb(GtkWidget *button _U_, gpointer parent_w)
+{
+ set_active_all(parent_w, TRUE);
+}
+
+/* Disable All */
+static void
+disable_all_cb(GtkWidget *button _U_, gpointer parent_w)
+{
+ set_active_all(parent_w, FALSE);
+}
+
+static void proto_destroy_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ GSList *entry;
+
+ if (proto_w)
+ gtk_widget_destroy(proto_w);
+ proto_w = NULL;
+
+ /* remove protocol list */
+ if (protocol_list) {
+ for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
+ g_free(entry->data);
+ }
+ g_slist_free(protocol_list);
+ protocol_list = NULL;
+ }
+}
+
+/* Treat this as a cancel, by calling "proto_cancel_cb()".
+ XXX - that'll destroy the Protocols dialog; will that upset
+ a higher-level handler that says "OK, we've been asked to delete
+ this, so destroy it"? */
+static gboolean proto_delete_cb(GtkWidget *proto_w, gpointer dummy _U_)
+{
+ proto_cancel_cb(NULL, proto_w);
+ return FALSE;
+}
+
+static void proto_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w)
+{
+ gboolean redissect;
+
+ redissect = set_proto_selection(GTK_WIDGET(parent_w));
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+ if (redissect)
+ redissect_packets(&cfile);
+}
+
+static void proto_apply_cb(GtkWidget *apply_bt _U_, gpointer parent_w)
+{
+ if (set_proto_selection(GTK_WIDGET(parent_w)))
+ redissect_packets(&cfile);
+}
+
+static void proto_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
+{
+ gboolean redissect;
+
+ redissect = revert_proto_selection();
+ gtk_widget_destroy(GTK_WIDGET(parent_w));
+ if (redissect)
+ redissect_packets(&cfile);
+}
+
+static gboolean set_proto_selection(GtkWidget *parent_w)
+{
+ GSList *entry;
+ gboolean need_redissect = FALSE;
+
+ for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
+ GtkWidget *button;
+ protocol_data_t *p = entry->data;
+
+ button = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w),
+ p->abbrev);
+ if (proto_is_protocol_enabled(p->hfinfo_index) != GTK_TOGGLE_BUTTON (button)->active) {
+ proto_set_decoding(p->hfinfo_index, GTK_TOGGLE_BUTTON (button)->active);
+ need_redissect = TRUE;
+ }
+ }
+
+ return need_redissect;
+
+} /* set_proto_selection */
+
+static gboolean revert_proto_selection(void)
+{
+ GSList *entry;
+ gboolean need_redissect = FALSE;
+
+ /*
+ * Undo all the changes we've made to protocol enable flags.
+ */
+ for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
+ protocol_data_t *p = entry->data;
+
+ if (proto_is_protocol_enabled(p->hfinfo_index) != p->was_enabled) {
+ proto_set_decoding(p->hfinfo_index, p->was_enabled);
+ need_redissect = TRUE;
+ }
+ }
+
+ return need_redissect;
+
+} /* revert_proto_selection */
+
+gint protocol_data_compare(gconstpointer a, gconstpointer b)
+{
+ return strcmp(((protocol_data_t *)a)->abbrev,
+ ((protocol_data_t *)b)->abbrev);
+}
+
+static void show_proto_selection(GtkWidget *main, GtkWidget *container)
+{
+
+#define NB_COL 7
+
+ GSList *entry;
+ GtkTooltips *tooltips;
+ GtkWidget *table;
+ int i, t = 0, l = 0, nb_line, nb_proto = 0;
+ void *cookie;
+ protocol_data_t *p;
+
+ /* Iterate over all the protocols */
+
+ for (i = proto_get_first_protocol(&cookie); i != -1;
+ i = proto_get_next_protocol(&cookie)) {
+ if (proto_can_disable_protocol(i)) {
+ p = g_malloc(sizeof(protocol_data_t));
+ p->name = proto_get_protocol_name(i);
+ p->abbrev = proto_get_protocol_filter_name(i);
+ p->hfinfo_index = i;
+ p->was_enabled = proto_is_protocol_enabled(i);
+ protocol_list = g_slist_insert_sorted(protocol_list,
+ p, protocol_data_compare);
+ nb_proto ++;
+ }
+ }
+
+ /* create a table (n x NB_COL) of buttons */
+
+ nb_line = (nb_proto % NB_COL) ? nb_proto / NB_COL + 1 : nb_proto / NB_COL;
+ table = gtk_table_new (nb_line, NB_COL, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE (table), 1);
+ gtk_table_set_col_spacings(GTK_TABLE (table), 1);
+ gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(container), table);
+ gtk_widget_show(table);
+
+ tooltips = gtk_tooltips_new();
+
+ nb_proto = 0;
+
+ for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
+ GtkWidget *button;
+
+ p = entry->data;
+ /* button label is the protocol abbrev */
+ button = gtk_toggle_button_new_with_label(p->abbrev);
+ /* tip is the complete protocol name */
+ gtk_tooltips_set_tip(tooltips, button, p->name, NULL);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button),
+ proto_is_protocol_enabled(p->hfinfo_index));
+ gtk_object_set_data(GTK_OBJECT(main), p->abbrev, button);
+ gtk_table_attach_defaults (GTK_TABLE (table), button, l, l+1, t, t+1);
+ gtk_widget_show (button);
+ if (++nb_proto % NB_COL) {
+ l++;
+ }
+ else {
+ l = 0;
+ t++;
+ }
+ }
+
+} /* show_proto_selection */
diff --git a/gtk2/proto_dlg.h b/gtk2/proto_dlg.h
new file mode 100644
index 0000000000..d3d6fdf8c4
--- /dev/null
+++ b/gtk2/proto_dlg.h
@@ -0,0 +1,32 @@
+/* proto_dlg.h
+ *
+ * $Id: proto_dlg.h,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Laurent Deniel <deniel@worldnet.fr>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.org>
+ * Copyright 2000 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.
+ *
+ */
+
+#ifndef __PROTO_DLG_H__
+#define __PROTO_DLG_H__
+
+void proto_cb(GtkWidget *, gpointer);
+
+#endif
diff --git a/gtk2/proto_draw.c b/gtk2/proto_draw.c
new file mode 100644
index 0000000000..7848fe47ef
--- /dev/null
+++ b/gtk2/proto_draw.c
@@ -0,0 +1,1051 @@
+/* proto_draw.c
+ * Routines for GTK+ packet display
+ *
+ * $Id: proto_draw.c,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ *
+ * Jeff Foster, 2001/03/12, added support for displaying named
+ * data sources as tabbed hex windows
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <ctype.h>
+#include <stdarg.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <epan/epan_dissect.h>
+
+#include "main.h"
+#include <epan/packet.h>
+#include "util.h"
+#include "menu.h"
+#include "keys.h"
+
+#include "colors.h"
+#include "prefs.h"
+#include "proto_draw.h"
+#include "packet_win.h"
+#include "ui_util.h"
+#include "gtkglobals.h"
+
+#define BYTE_VIEW_WIDTH 16
+#define BYTE_VIEW_SEP 8
+
+#define E_BYTE_VIEW_TREE_PTR "byte_view_tree_ptr"
+#define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
+#define E_BYTE_VIEW_NDIGITS_KEY "byte_view_ndigits"
+#define E_BYTE_VIEW_TVBUFF_KEY "byte_view_tvbuff"
+#define E_BYTE_VIEW_START_KEY "byte_view_start"
+#define E_BYTE_VIEW_END_KEY "byte_view_end"
+#define E_BYTE_VIEW_ENCODE_KEY "byte_view_encode"
+
+static GtkWidget *
+add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
+ proto_tree *tree, GtkWidget *tree_view);
+
+static void
+proto_tree_draw_node(GNode *node, gpointer data);
+
+/* Get the current text window for the notebook. */
+GtkWidget *
+get_notebook_bv_ptr(GtkWidget *nb_ptr)
+{
+ int num;
+ GtkWidget *bv_page;
+
+ num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
+ bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
+ if (bv_page)
+ return GTK_BIN(bv_page)->child;
+ else
+ return NULL;
+}
+
+/*
+ * Get the data and length for a byte view, given the byte view page.
+ * Return the pointer, or NULL on error, and set "*data_len" to the length.
+ */
+const guint8 *
+get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
+{
+ tvbuff_t *byte_view_tvb;
+ const guint8 *data_ptr;
+
+ byte_view_tvb = gtk_object_get_data(GTK_OBJECT(byte_view),
+ E_BYTE_VIEW_TVBUFF_KEY);
+ if (byte_view_tvb == NULL)
+ return NULL;
+
+ data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
+ *data_len = tvb_length(byte_view_tvb);
+ return data_ptr;
+}
+
+/*
+ * Set the current text window for the notebook to the window that
+ * refers to a particular tvbuff.
+ */
+void
+set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
+{
+ int num;
+ GtkWidget *bv_page, *bv;
+ tvbuff_t *bv_tvb;
+
+ for (num = 0;
+ (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
+ num++) {
+ bv = GTK_BIN(bv_page)->child;
+ bv_tvb = gtk_object_get_data(GTK_OBJECT(bv), E_BYTE_VIEW_TVBUFF_KEY);
+ if (bv_tvb == tvb) {
+ /* Found it. */
+ gtk_notebook_set_page(GTK_NOTEBOOK(nb_ptr), num);
+ break;
+ }
+ }
+}
+
+/* Redraw a given byte view window. */
+void
+redraw_hex_dump(GtkWidget *nb, frame_data *fd, field_info *finfo)
+{
+ GtkWidget *bv;
+ const guint8 *data;
+ guint len;
+
+ bv = get_notebook_bv_ptr(nb);
+ if (bv != NULL) {
+ data = get_byte_view_data_and_length(bv, &len);
+ if (data != NULL)
+ packet_hex_print(GTK_TEXT_VIEW(bv), data, fd, finfo, len);
+ }
+}
+
+/* Redraw all byte view windows. */
+void
+redraw_hex_dump_all(void)
+{
+ if (cfile.current_frame != NULL)
+ redraw_hex_dump( byte_nb_ptr, cfile.current_frame, finfo_selected);
+
+ redraw_hex_dump_packet_wins();
+}
+
+static void
+expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
+ GtkTreePath *path _U_, gpointer user_data _U_)
+{
+ field_info *finfo;
+ GtkTreeModel *model;
+
+ model = gtk_tree_view_get_model(tree_view);
+ gtk_tree_model_get(model, iter, 1, &finfo, -1);
+ g_assert(finfo);
+
+ /*
+ * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
+ * are thus presumably leaf nodes and cannot be expanded.
+ */
+ if (finfo->tree_type != -1) {
+ g_assert(finfo->tree_type >= 0 &&
+ finfo->tree_type < num_tree_types);
+ tree_is_expanded[finfo->tree_type] = TRUE;
+ }
+}
+
+static void
+collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
+ GtkTreePath *path _U_, gpointer user_data _U_)
+{
+ field_info *finfo;
+ GtkTreeModel *model;
+
+ model = gtk_tree_view_get_model(tree_view);
+ gtk_tree_model_get(model, iter, 1, &finfo, -1);
+ g_assert(finfo);
+
+ /*
+ * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
+ * are thus presumably leaf nodes and cannot be collapsed.
+ */
+ if (finfo->tree_type != -1) {
+ g_assert(finfo->tree_type >= 0 &&
+ finfo->tree_type < num_tree_types);
+ tree_is_expanded[finfo->tree_type] = FALSE;
+ }
+}
+
+#define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */
+#define BYTES_PER_LINE 16 /* max byte values in a line */
+#define HEX_DUMP_LEN (BYTES_PER_LINE*3 + 1)
+ /* max number of characters hex dump takes -
+ 2 digits plus trailing blank
+ plus separator between first and
+ second 8 digits */
+#define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
+ /* number of characters those bytes take;
+ 3 characters per byte of hex dump,
+ 2 blanks separating hex from ASCII,
+ 1 character per byte of ASCII dump */
+#define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
+ /* number of characters per line;
+ offset, 2 blanks separating offset
+ from data dump, data dump */
+
+/* Which byte the offset is referring to. Associates
+ * whitespace with the preceding digits. */
+static int
+byte_num(int offset, int start_point)
+{
+ return (offset - start_point) / 3;
+}
+
+struct field_lookup_info {
+ field_info *fi;
+ GtkTreeIter iter;
+};
+
+static gboolean
+lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
+ gpointer data)
+{
+ field_info *fi;
+ struct field_lookup_info *fli = (struct field_lookup_info *)data;
+
+ gtk_tree_model_get(model, iter, 1, &fi, -1);
+ if (fi == fli->fi) {
+ fli->iter = *iter;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* If the user selected a certain byte in the byte view, try to find
+ * the item in the GUI proto_tree that corresponds to that byte, and
+ * select it. */
+static gint
+byte_view_select(GtkWidget *widget, GdkEventButton *event)
+{
+ proto_tree *tree;
+ GtkTreeView *tree_view;
+ GtkTreeModel *model;
+ GtkTreePath *first_path, *path;
+ GtkTreeIter parent;
+ field_info *finfo;
+ GtkTextView *bv = GTK_TEXT_VIEW(widget);
+ gint x, y;
+ GtkTextIter iter;
+ int row, column;
+ int byte;
+ tvbuff_t *tvb;
+ guint ndigits;
+ int digits_start_1;
+ int digits_end_1;
+ int digits_start_2;
+ int digits_end_2;
+ int text_start_1;
+ int text_end_1;
+ int text_start_2;
+ int text_end_2;
+ struct field_lookup_info fli;
+
+ /*
+ * et the number of digits of offset being displayed, and
+ * compute the columns of various parts of the display.
+ */
+ ndigits = GPOINTER_TO_UINT(gtk_object_get_data(GTK_OBJECT(bv),
+ E_BYTE_VIEW_NDIGITS_KEY));
+
+ /*
+ * The column of the first hex digit in the first half.
+ * That starts after "ndigits" digits of offset and two
+ * separating blanks.
+ */
+ digits_start_1 = ndigits + 2;
+
+ /*
+ * The column of the last hex digit in the first half.
+ * There are BYTES_PER_LINE/2 bytes displayed in the first
+ * half; there are 2 characters per byte, plus a separating
+ * blank after all but the last byte's characters.
+ *
+ * Then subtract 1 to get the last column of the first half
+ * rather than the first column after the first half.
+ */
+ digits_end_1 = digits_start_1 + (BYTES_PER_LINE/2)*2 +
+ (BYTES_PER_LINE/2 - 1) - 1;
+
+ /*
+ * The column of the first hex digit in the second half.
+ * Add back the 1 to get the first column after the first
+ * half, and then add 2 for the 2 separating blanks between
+ * the halves.
+ */
+ digits_start_2 = digits_end_1 + 3;
+
+ /*
+ * The column of the last hex digit in the second half.
+ * Add the same value we used to get "digits_end_1" from
+ * "digits_start_1".
+ */
+ digits_end_2 = digits_start_2 + (BYTES_PER_LINE/2)*2 +
+ (BYTES_PER_LINE/2 - 1) - 1;
+
+ /*
+ * The column of the first "text dump" character in the first half.
+ * Add back the 1 to get the first column after the second
+ * half's hex dump, and then add 3 for the 3 separating blanks
+ * between the hex and text dummp.
+ */
+ text_start_1 = digits_end_2 + 4;
+
+ /*
+ * The column of the last "text dump" character in the first half.
+ * There are BYTES_PER_LINE/2 bytes displayed in the first
+ * half; there is 1 character per byte.
+ *
+ * Then subtract 1 to get the last column of the first half
+ * rather than the first column after the first half.
+ */
+ text_end_1 = text_start_1 + BYTES_PER_LINE/2 - 1;
+
+ /*
+ * The column of the first "text dump" character in the second half.
+ * Add back the 1 to get the first column after the first half,
+ * and then add 1 for the separating blank between the halves.
+ */
+ text_start_2 = text_end_1 + 2;
+
+ /*
+ * The column of the last "text dump" character in second half.
+ * Add the same value we used to get "text_end_1" from
+ * "text_start_1".
+ */
+ text_end_2 = text_start_2 + BYTES_PER_LINE/2 - 1;
+
+ tree = gtk_object_get_data(GTK_OBJECT(widget), E_BYTE_VIEW_TREE_PTR);
+ if (tree == NULL) {
+ /*
+ * Somebody clicked on the dummy byte view; do nothing.
+ */
+ return FALSE;
+ }
+ tree_view = GTK_TREE_VIEW(gtk_object_get_data(GTK_OBJECT(widget),
+ E_BYTE_VIEW_TREE_VIEW_PTR));
+
+ /* get the row/column selected */
+ gtk_text_view_window_to_buffer_coords(bv,
+ gtk_text_view_get_window_type(bv, event->window),
+ event->x, event->y, &x, &y);
+ gtk_text_view_get_iter_at_location(bv, &iter, x, y);
+ row = gtk_text_iter_get_line(&iter);
+ column = gtk_text_iter_get_line_offset(&iter);
+
+ /* Given the column and row, determine which byte offset
+ * the user clicked on. */
+ if (column >= digits_start_1 && column <= digits_end_1) {
+ byte = byte_num(column, digits_start_1);
+ if (byte == -1) {
+ return FALSE;
+ }
+ }
+ else if (column >= digits_start_2 && column <= digits_end_2) {
+ byte = byte_num(column, digits_start_2);
+ if (byte == -1) {
+ return FALSE;
+ }
+ byte += 8;
+ }
+ else if (column >= text_start_1 && column <= text_end_1) {
+ byte = column - text_start_1;
+ }
+ else if (column >= text_start_2 && column <= text_end_2) {
+ byte = 8 + column - text_start_2;
+ }
+ else {
+ /* The user didn't select a hex digit or
+ * text-dump character. */
+ return FALSE;
+ }
+
+ /* Add the number of bytes from the previous rows. */
+ byte += row * 16;
+
+ /* Get the data source tvbuff */
+ tvb = gtk_object_get_data(GTK_OBJECT(widget), E_BYTE_VIEW_TVBUFF_KEY);
+
+ /* Find the finfo that corresponds to our byte. */
+ finfo = proto_find_field_from_offset(tree, byte, tvb);
+
+ if (!finfo) {
+ return FALSE;
+ }
+
+ model = gtk_tree_view_get_model(tree_view);
+ fli.fi = finfo;
+ gtk_tree_model_foreach(model, lookup_finfo, &fli);
+
+ /* Expand our field's row */
+ first_path = gtk_tree_model_get_path(model, &fli.iter);
+ gtk_tree_view_expand_row(tree_view, first_path, FALSE);
+ expand_tree(tree_view, &fli.iter, NULL, NULL);
+
+ /* ... and its parents */
+ while (gtk_tree_model_iter_parent(model, &parent, &fli.iter)) {
+ path = gtk_tree_model_get_path(model, &parent);
+ gtk_tree_view_expand_row(tree_view, path, FALSE);
+ expand_tree(tree_view, &parent, NULL, NULL);
+ fli.iter = parent;
+ gtk_tree_path_free(path);
+ }
+
+ /* select our field's row */
+ gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
+ first_path);
+
+ /* And position the window so the selection is visible.
+ * Position the selection in the middle of the viewable
+ * pane. */
+ gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5, 0.0);
+
+ gtk_tree_path_free(first_path);
+
+ return TRUE;
+}
+
+/* Calls functions for different mouse-button presses. */
+static gint
+byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ GdkEventButton *event_button = NULL;
+
+ if(widget == NULL || event == NULL || data == NULL) {
+ return FALSE;
+ }
+
+ if(event->type == GDK_BUTTON_PRESS) {
+ event_button = (GdkEventButton *) event;
+
+ switch(event_button->button) {
+
+ case 1:
+ return byte_view_select(widget, event_button);
+ case 3:
+ return popup_menu_handler(widget, event, data);
+ default:
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+GtkWidget *
+create_byte_view(gint bv_size, GtkWidget *pane)
+{
+ GtkWidget *byte_nb;
+
+ byte_nb = gtk_notebook_new();
+ gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
+
+ gtk_paned_pack2(GTK_PANED(pane), byte_nb, FALSE, FALSE);
+ gtk_widget_set_size_request(byte_nb, -1, bv_size);
+ gtk_widget_show(byte_nb);
+
+ /* Add a placeholder byte view so that there's at least something
+ displayed in the byte view notebook. */
+ add_byte_tab(byte_nb, "", NULL, NULL, NULL);
+
+ return byte_nb;
+}
+
+static void
+byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
+{
+ const guint8 *byte_data;
+ guint byte_len;
+
+ byte_data = get_byte_view_data_and_length(bv, &byte_len);
+ if (byte_data == NULL) {
+ /* This must be the dummy byte view if no packet is selected. */
+ return;
+ }
+ packet_hex_print(GTK_TEXT_VIEW(bv), byte_data, cfile.current_frame, NULL, byte_len);
+}
+
+static GtkWidget *
+add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
+ proto_tree *tree, GtkWidget *tree_view)
+{
+ GtkWidget *byte_view, *byte_scrollw, *label;
+ GtkTextBuffer *buf;
+ GtkStyle *style;
+
+ /* Byte view. Create a scrolled window for the text. */
+ byte_scrollw = scrolled_window_new(NULL, NULL);
+
+ /* Add scrolled pane to tabbed window */
+ label = gtk_label_new(name);
+ gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
+
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(byte_scrollw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_widget_show(byte_scrollw);
+
+ byte_view = gtk_text_view_new();
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(byte_view), FALSE);
+ gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(byte_view), FALSE);
+ buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(byte_view));
+ style = gtk_widget_get_style(GTK_WIDGET(byte_view));
+ gtk_text_buffer_create_tag(buf, "plain", "font-desc", m_r_font, NULL);
+ gtk_text_buffer_create_tag(buf, "reverse",
+ "font-desc", m_r_font,
+ "foreground-gdk", &style->text[GTK_STATE_SELECTED],
+ "background-gdk", &style->base[GTK_STATE_SELECTED],
+ NULL);
+ gtk_text_buffer_create_tag(buf, "bold", "font-desc", m_b_font, NULL);
+ gtk_object_set_data(GTK_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY,
+ (gpointer)tvb);
+ gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
+
+ g_signal_connect(G_OBJECT(byte_view), "show",
+ G_CALLBACK(byte_view_realize_cb), NULL);
+ g_signal_connect(G_OBJECT(byte_view), "button_press_event",
+ G_CALLBACK(byte_view_button_press_cb),
+ gtk_object_get_data(GTK_OBJECT(popup_menu_object),
+ PM_HEXDUMP_KEY));
+
+ gtk_object_set_data(GTK_OBJECT(byte_view), E_BYTE_VIEW_TREE_PTR,
+ tree);
+ gtk_object_set_data(GTK_OBJECT(byte_view), E_BYTE_VIEW_TREE_VIEW_PTR,
+ tree_view);
+
+ gtk_widget_show(byte_view);
+
+ /* no tabs if this is the first page */
+ if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
+ else
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
+
+ return byte_view;
+}
+
+void
+add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
+ GtkWidget *byte_nb_ptr)
+{
+ GSList *src_le;
+ data_source *src;
+
+ /*
+ * Get rid of all the old notebook tabs.
+ */
+ while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
+ gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
+
+ /*
+ * Add to the specified byte view notebook tabs for hex dumps
+ * of all the data sources for the specified frame.
+ */
+ for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
+ src = src_le->data;
+ add_byte_tab(byte_nb_ptr, src->name, src->tvb, edt->tree,
+ tree_view);
+ }
+
+ /*
+ * Initially select the first byte view.
+ */
+ gtk_notebook_set_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
+}
+
+static void
+packet_hex_print_common(GtkTextView *bv, const guint8 *pd, int len, int bstart,
+ int bend, int encoding)
+{
+ int i = 0, j, k, cur;
+ guchar line[MAX_LINE_LEN + 1];
+ static guchar hexchars[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ guchar c = '\0';
+ unsigned int use_digits;
+ gboolean reverse, newreverse;
+ GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(bv));
+ GtkTextIter iter;
+ char *revstyle;
+ gchar *convline;
+ gsize newsize;
+ GtkTextMark *mark = NULL;
+
+ /* Clear out the text */
+ gtk_text_buffer_set_text(buf, "", 0);
+ gtk_text_buffer_get_start_iter(buf, &iter);
+
+ /*
+ * How many of the leading digits of the offset will we supply?
+ * We always supply at least 4 digits, but if the maximum offset
+ * won't fit in 4 digits, we use as many digits as will be needed.
+ */
+ if (((len - 1) & 0xF0000000) != 0)
+ use_digits = 8; /* need all 8 digits */
+ else if (((len - 1) & 0x0F000000) != 0)
+ use_digits = 7; /* need 7 digits */
+ else if (((len - 1) & 0x00F00000) != 0)
+ use_digits = 6; /* need 6 digits */
+ else if (((len - 1) & 0x000F0000) != 0)
+ use_digits = 5; /* need 5 digits */
+ else
+ use_digits = 4; /* we'll supply 4 digits */
+
+ /* Record the number of digits in this text view. */
+ gtk_object_set_data(GTK_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY,
+ GUINT_TO_POINTER(use_digits));
+
+ while (i < len) {
+ /* Print the line number */
+ j = use_digits;
+ cur = 0;
+ do {
+ j--;
+ c = (i >> (j*4)) & 0xF;
+ line[cur++] = hexchars[c];
+ } while (j != 0);
+ line[cur++] = ' ';
+ line[cur++] = ' ';
+ line[cur] = '\0';
+
+ /* Display with inverse video ? */
+ if (prefs.gui_hex_dump_highlight_style)
+ revstyle = "reverse";
+ else
+ revstyle = "bold";
+
+ gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, -1, "plain",
+ NULL);
+ /* Do we start in reverse? */
+ reverse = i >= bstart && i < bend;
+ j = i;
+ k = i + BYTE_VIEW_WIDTH;
+ cur = 0;
+ /* Print the hex bit */
+ while (i < k) {
+ if (i < len) {
+ line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
+ line[cur++] = hexchars[pd[i] & 0x0f];
+ } else {
+ line[cur++] = ' '; line[cur++] = ' ';
+ }
+ i++;
+ newreverse = i >= bstart && i < bend;
+ /* Have we gone from reverse to plain? */
+ if (reverse && (reverse != newreverse)) {
+ gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
+ revstyle, NULL);
+ cur = 0;
+ }
+ /* Inter byte space if not at end of line */
+ if (i < k) {
+ line[cur++] = ' ';
+ /* insert a space every BYTE_VIEW_SEP bytes */
+ if( ( i % BYTE_VIEW_SEP ) == 0 ) {
+ line[cur++] = ' ';
+ }
+ }
+ /* Have we gone from plain to reversed? */
+ if (!reverse && (reverse != newreverse)) {
+ gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
+ "plain", NULL);
+ mark = gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE);
+ cur = 0;
+ }
+ reverse = newreverse;
+ }
+ /* Print remaining part of line */
+ gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
+ reverse ? revstyle : "plain",
+ NULL);
+ cur = 0;
+ /* Print some space at the end of the line */
+ line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
+ gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
+ "plain", NULL);
+ cur = 0;
+
+ /* Print the ASCII bit */
+ i = j;
+ /* Do we start in reverse? */
+ reverse = i >= bstart && i < bend;
+ while (i < k) {
+ if (i < len) {
+ if (encoding == CHAR_ASCII) {
+ c = pd[i];
+ }
+ else if (encoding == CHAR_EBCDIC) {
+ c = EBCDIC_to_ASCII1(pd[i]);
+ }
+ else {
+ g_assert_not_reached();
+ }
+ line[cur++] = isprint(c) ? c : '.';
+ } else {
+ line[cur++] = ' ';
+ }
+ i++;
+ newreverse = i >= bstart && i < bend;
+ /* Have we gone from reverse to plain? */
+ if (reverse && (reverse != newreverse)) {
+ convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
+ gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
+ revstyle, NULL);
+ g_free(convline);
+ cur = 0;
+ }
+ if (i < k) {
+ /* insert a space every BYTE_VIEW_SEP bytes */
+ if( ( i % BYTE_VIEW_SEP ) == 0 ) {
+ line[cur++] = ' ';
+ }
+ }
+ /* Have we gone from plain to reversed? */
+ if (!reverse && (reverse != newreverse)) {
+ convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
+ gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
+ "plain", NULL);
+ g_free(convline);
+ cur = 0;
+ }
+ reverse = newreverse;
+ }
+ /* Print remaining part of line */
+ convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
+ gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
+ reverse ? revstyle : "plain",
+ NULL);
+ g_free(convline);
+ cur = 0;
+ line[cur++] = '\n';
+ gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
+ "plain", NULL);
+ }
+
+ /* scroll text into position */
+ if (mark) {
+ gtk_text_view_scroll_to_mark(bv, mark, 0.0, TRUE, 1.0, 0.0);
+ gtk_text_buffer_delete_mark(buf, mark);
+ }
+}
+
+void
+packet_hex_print(GtkTextView *bv, const guint8 *pd, frame_data *fd,
+ field_info *finfo, guint len)
+{
+ /* do the initial printing and save the information needed */
+ /* to redraw the display if preferences change. */
+
+ int bstart, bend = -1, blen;
+
+ if (finfo != NULL) {
+ bstart = finfo->start;
+ blen = finfo->length;
+ } else {
+ bstart = -1;
+ blen = -1;
+ }
+ if (bstart >= 0 && blen >= 0) {
+ bend = bstart + blen;
+ }
+
+ /* save the information needed to redraw the text */
+ /* should we save the fd & finfo pointers instead ?? */
+ gtk_object_set_data(GTK_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bend));
+ gtk_object_set_data(GTK_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bstart));
+ gtk_object_set_data(GTK_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY, GINT_TO_POINTER(fd->flags.encoding));
+
+ packet_hex_print_common(bv, pd, len, bstart, bend, fd->flags.encoding);
+}
+
+/*
+ * Redraw the text using the saved information; usually called if
+ * the preferences have changed.
+ */
+void
+packet_hex_reprint(GtkTextView *bv)
+{
+ int start, end, encoding;
+ const guint8 *data;
+ guint len;
+
+ start = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(bv),
+ E_BYTE_VIEW_START_KEY));
+ end = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(bv),
+ E_BYTE_VIEW_END_KEY));
+ data = get_byte_view_data_and_length(GTK_WIDGET(bv), &len);
+ g_assert(data != NULL);
+ encoding = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(bv),
+ E_BYTE_VIEW_ENCODE_KEY));
+
+ packet_hex_print_common(bv, data, len, start, end, encoding);
+}
+
+/* List of all protocol tree widgets, so we can globally set the selection
+ mode and font of all of them. */
+static GList *ptree_widgets;
+
+/* Add a protocol tree widget to the list of protocol tree widgets. */
+static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
+
+static void
+remember_ptree_widget(GtkWidget *ptreew)
+{
+ ptree_widgets = g_list_append(ptree_widgets, ptreew);
+
+ /* Catch the "destroy" event on the widget, so that we remove it from
+ the list when it's destroyed. */
+ g_signal_connect(G_OBJECT(ptreew), "destroy",
+ G_CALLBACK(forget_ptree_widget), NULL);
+}
+
+/* Remove a protocol tree widget from the list of protocol tree widgets. */
+static void
+forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
+{
+ ptree_widgets = g_list_remove(ptree_widgets, ptreew);
+}
+
+/* Set the selection mode of a given packet tree window. */
+static void
+set_ptree_sel_browse(GtkWidget *tree, gboolean val)
+{
+ GtkTreeSelection *selection;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
+ /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
+ I think "browse" in Ethereal makes more sense than "SINGLE" in
+ GTK+ */
+ if (val) {
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
+ }
+ else {
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
+ }
+}
+
+static void
+set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
+{
+ set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
+}
+
+/* Set the selection mode of all packet tree windows. */
+void
+set_ptree_sel_browse_all(gboolean val)
+{
+ g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
+}
+
+void
+set_ptree_font_cb(gpointer data, gpointer user_data)
+{
+ gtk_widget_modify_font((GtkWidget *)data,
+ (PangoFontDescription *)user_data);
+}
+
+void
+set_ptree_font_all(PangoFontDescription *font)
+{
+ g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
+}
+
+void
+create_tree_view(gint tv_size, e_prefs *prefs, GtkWidget *pane,
+ GtkWidget **tv_scrollw_p, GtkWidget **tree_view_p)
+{
+ GtkWidget *tv_scrollw, *tree_view;
+ GtkTreeStore *store;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ gint col_offset;
+
+ /* Tree view */
+ tv_scrollw = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(tv_scrollw),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_paned_pack1(GTK_PANED(pane), tv_scrollw, TRUE, TRUE);
+ gtk_widget_set_size_request(tv_scrollw, -1, tv_size);
+ gtk_widget_show(tv_scrollw);
+
+ store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
+ tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+ g_object_unref(G_OBJECT(store));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
+ renderer = gtk_cell_renderer_text_new();
+ col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
+ -1, "Name", renderer,
+ "text", 0, NULL);
+ column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
+ col_offset - 1);
+ gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
+ GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ g_signal_connect(G_OBJECT(tree_view), "row-expanded",
+ G_CALLBACK(expand_tree), NULL);
+ g_signal_connect(GTK_OBJECT(tree_view), "row-collapsed",
+ G_CALLBACK(collapse_tree), NULL);
+ gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
+ set_ptree_sel_browse(tree_view, prefs->gui_ptree_sel_browse);
+ gtk_widget_modify_font(tree_view, m_r_font);
+ remember_ptree_widget(tree_view);
+
+ *tree_view_p = tree_view;
+ *tv_scrollw_p = tv_scrollw;
+}
+
+void expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view) {
+ int i;
+ for(i=0; i < num_tree_types; i++) {
+ tree_is_expanded[i] = TRUE;
+ }
+ /* proto_tree_draw(protocol_tree, tree_view); */
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
+}
+
+void collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view) {
+ int i;
+ for(i=0; i < num_tree_types; i++) {
+ tree_is_expanded[i] = FALSE;
+ }
+ /* proto_tree_draw(protocol_tree, tree_view); */
+ gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
+}
+
+
+struct proto_tree_draw_info {
+ GtkTreeView *tree_view;
+ GtkTreeIter *iter;
+};
+
+void
+proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
+{
+ GtkTreeStore *store;
+ struct proto_tree_draw_info info;
+
+ info.tree_view = GTK_TREE_VIEW(tree_view);
+ info.iter = NULL;
+
+ store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
+
+ /*
+ * Clear out any crud left over in the display of the protocol
+ * tree, by removing all nodes from the tree view.
+ */
+ gtk_tree_store_clear(store);
+
+ g_node_children_foreach((GNode*) protocol_tree, G_TRAVERSE_ALL,
+ proto_tree_draw_node, &info);
+}
+
+static void
+proto_tree_draw_node(GNode *node, gpointer data)
+{
+ struct proto_tree_draw_info info;
+ struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
+
+ field_info *fi = PITEM_FINFO(node);
+ gchar label_str[ITEM_LABEL_LENGTH];
+ gchar *label_ptr;
+ gboolean is_leaf, is_expanded;
+ GtkTreeStore *store;
+ GtkTreeIter iter;
+
+ if (!fi->visible)
+ return;
+
+ /* was a free format label produced? */
+ if (fi->representation) {
+ label_ptr = fi->representation;
+ }
+ else { /* no, make a generic label */
+ label_ptr = label_str;
+ proto_item_fill_label(fi, label_str);
+ }
+
+ if (g_node_n_children(node) > 0) {
+ is_leaf = FALSE;
+ g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
+ if (tree_is_expanded[fi->tree_type]) {
+ is_expanded = TRUE;
+ }
+ else {
+ is_expanded = FALSE;
+ }
+ }
+ else {
+ is_leaf = TRUE;
+ is_expanded = FALSE;
+ }
+
+ info.tree_view = parent_info->tree_view;
+ store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
+ gtk_tree_store_append(store, &iter, parent_info->iter);
+ gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
+
+ if (!is_leaf) {
+ info.iter = &iter;
+ g_node_children_foreach(node, G_TRAVERSE_ALL,
+ proto_tree_draw_node, &info);
+ }
+
+ if (is_expanded == TRUE)
+ {
+ GtkTreePath *path;
+ path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
+ gtk_tree_view_expand_row(info.tree_view, path, FALSE);
+ gtk_tree_path_free(path);
+ }
+}
+
+/*
+ * Clear the hex dump and protocol tree panes.
+ */
+void
+clear_tree_and_hex_views(void)
+{
+ /* Clear the hex dump by getting rid of all the byte views. */
+ while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
+ gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
+
+ /* Add a placeholder byte view so that there's at least something
+ displayed in the byte view notebook. */
+ add_byte_tab(byte_nb_ptr, "", NULL, NULL, tree_view);
+
+ /* Clear the protocol tree */
+ gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view))));
+}
diff --git a/gtk2/proto_draw.h b/gtk2/proto_draw.h
new file mode 100644
index 0000000000..3bab41ebef
--- /dev/null
+++ b/gtk2/proto_draw.h
@@ -0,0 +1,70 @@
+/* proto_draw.h
+ * Definitions for GTK+ packet display structures and routines
+ *
+ * $Id: proto_draw.h,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifndef __GTKPACKET_H__
+#define __GTKPACKET_H__
+
+/* Get the current text window for the notebook. */
+extern GtkWidget *get_notebook_bv_ptr(GtkWidget *nb_ptr);
+
+/*
+ * Get the data and length for a byte view, given the byte view page.
+ * Return the pointer, or NULL on error, and set "*data_len" to the length.
+ */
+extern const guint8 *get_byte_view_data_and_length(GtkWidget *byte_view,
+ guint *data_len);
+
+/*
+ * Set the current text window for the notebook to the window that
+ * refers to a particular tvbuff.
+ */
+extern void set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb);
+
+/* Redraw a given byte view window. */
+extern void redraw_hex_dump(GtkWidget *nb, frame_data *fd, field_info *finfo);
+
+/* Redraw all byte view windows. */
+extern void redraw_hex_dump_all(void);
+
+extern GtkWidget *create_byte_view(gint bv_size, GtkWidget *pane);
+
+extern void add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
+ GtkWidget *byte_nb_ptr);
+
+void packet_hex_print(GtkTextView *, const guint8 *, frame_data *, field_info *,
+ guint);
+void packet_hex_reprint(GtkTextView *);
+
+void create_tree_view(gint tv_size, e_prefs *prefs, GtkWidget *pane,
+ GtkWidget **tv_scrollw_p, GtkWidget **tree_view_p);
+void proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view);
+void expand_all_tree(proto_tree *protocol_tree, GtkWidget *tree_view);
+void collapse_all_tree(proto_tree *protocol_tree, GtkWidget *tree_view);
+
+void set_ptree_sel_browse_all(gboolean);
+void set_ptree_font_all(PangoFontDescription *font);
+
+void clear_tree_and_hex_views(void);
+
+#endif
diff --git a/gtk2/proto_hier_stats_dlg.c b/gtk2/proto_hier_stats_dlg.c
new file mode 100644
index 0000000000..fae58baf4a
--- /dev/null
+++ b/gtk2/proto_hier_stats_dlg.c
@@ -0,0 +1,253 @@
+/* proto_hier_stats_dlg.c
+ *
+ * $Id: proto_hier_stats_dlg.c,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include "proto_hier_stats.h"
+#include "dlg_utils.h"
+#include "ui_util.h"
+#include "main.h"
+
+enum {
+ PROTOCOL_COLUMN,
+ PRCT_PKTS_COLUMN,
+ PKTS_COLUMN,
+ BYTES_COLUMN,
+ END_PKTS_COLUMN,
+ END_BYTES_COLUMN,
+ NUM_STAT_COLUMNS /* must be the last */
+};
+
+typedef struct {
+
+ GtkTreeView *tree_view;
+ GtkTreeIter *iter;
+ ph_stats_t *ps;
+
+} draw_info_t;
+
+
+#define PCT(x,y) (100.0 * (float)(x) / (float)(y))
+
+static void
+fill_in_tree_node(GNode *node, gpointer data)
+{
+ ph_stats_node_t *stats = node->data;
+ draw_info_t *di = data;
+
+ GtkTreeView *tree_view = di->tree_view;
+ GtkTreeIter *iter = di->iter;
+ GtkTreeStore *store;
+ ph_stats_t *ps = di->ps;
+
+ gchar *text[2];
+ gboolean is_leaf;
+ GtkTreeIter new_iter;
+
+ draw_info_t child_di;
+
+ if (g_node_n_children(node) > 0) {
+ is_leaf = FALSE;
+ }
+ else {
+ is_leaf = TRUE;
+ }
+
+ text[0] = stats->hfinfo->name;
+ text[1] = g_strdup_printf("%6.2f%%",
+ PCT(stats->num_pkts_total, ps->tot_packets));
+
+ store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_view));
+ gtk_tree_store_append(store, &new_iter, iter);
+ gtk_tree_store_set(store, &new_iter,
+ PROTOCOL_COLUMN, text[0],
+ PRCT_PKTS_COLUMN, text[1],
+ PKTS_COLUMN, stats->num_pkts_total,
+ BYTES_COLUMN, stats->num_bytes_total,
+ END_PKTS_COLUMN, stats->num_pkts_last,
+ END_BYTES_COLUMN, stats->num_bytes_last,
+ -1);
+
+ g_free(text[1]);
+
+ child_di.tree_view = tree_view;
+ child_di.iter = &new_iter;
+ child_di.ps = ps;
+
+ g_node_children_foreach(node, G_TRAVERSE_ALL,
+ fill_in_tree_node, &child_di);
+}
+
+static void
+fill_in_tree(GtkTreeView *tree, ph_stats_t *ps)
+{
+ draw_info_t di;
+
+ di.tree_view = tree;
+ di.iter = NULL;
+ di.ps = ps;
+
+ g_node_children_foreach(ps->stats_tree, G_TRAVERSE_ALL,
+ fill_in_tree_node, &di);
+}
+
+#define MAX_DLG_HEIGHT 450
+#define DEF_DLG_WIDTH 600
+
+static void
+create_tree(GtkWidget *container, ph_stats_t *ps)
+{
+ GtkWidget *sw, *tree;
+ GtkTreeView *tree_view;
+ GtkTreeStore *store;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ /* Scrolled Window */
+ sw = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(container), sw);
+
+ store = gtk_tree_store_new(NUM_STAT_COLUMNS, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT,
+ G_TYPE_UINT, G_TYPE_UINT);
+ tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+ tree_view = GTK_TREE_VIEW(tree);
+ gtk_tree_view_set_rules_hint(tree_view, TRUE);
+ gtk_tree_view_set_headers_visible(tree_view, TRUE);
+ gtk_tree_view_set_headers_clickable(tree_view, FALSE);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Protocol", renderer,
+ "text", PROTOCOL_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(tree_view, column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("% Packets", renderer,
+ "text", PRCT_PKTS_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(tree_view, column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Packets", renderer,
+ "text", PKTS_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(tree_view, column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Bytes", renderer,
+ "text", BYTES_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(tree_view, column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("End Packets",
+ renderer, "text",
+ END_PKTS_COLUMN, NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(tree_view, column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("End Bytes", renderer,
+ "text", END_BYTES_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(tree_view, column);
+
+ /* XXX - get 'pos' to set vertical scroll-bar placement. */
+
+ /* Right justify numeric columns */
+ /* for (i = 1; i <= 5; i++) {
+ gtk_clist_set_column_justification(GTK_CLIST(tree), i,
+ GTK_JUSTIFY_RIGHT);
+ } */
+
+ /* Fill in the data. */
+ fill_in_tree(tree_view, ps);
+
+ gtk_widget_set_size_request(tree, DEF_DLG_WIDTH, MAX_DLG_HEIGHT);
+
+ gtk_container_add(GTK_CONTAINER(sw), tree);
+ ph_stats_free(ps);
+}
+
+#define WNAME "Protocol Hierarchy Statistics"
+
+void
+proto_hier_stats_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ ph_stats_t *ps;
+ GtkWidget *dlg, *bt, *vbox, *frame, *bbox;
+
+ /* Get the statistics. */
+ ps = ph_stats_new();
+ if (ps == NULL) {
+ /* The user gave up before we finished; don't pop up
+ a statistics window. */
+ return;
+ }
+
+ dlg = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(dlg), "Ethereal: " WNAME);
+ g_signal_connect(G_OBJECT(dlg), "realize",
+ G_CALLBACK(window_icon_realize_cb), NULL);
+
+ vbox = gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(vbox), 5);
+ gtk_container_add(GTK_CONTAINER(dlg), vbox);
+
+ frame = gtk_frame_new(WNAME);
+ /*gtk_container_add(GTK_CONTAINER(vbox), frame);*/
+ gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
+
+
+ /* Data section */
+ create_tree(frame, ps);
+
+ /* Button row. We put it in an HButtonBox to
+ * keep it from expanding to the width of the window. */
+ bbox = gtk_hbutton_box_new();
+ gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
+ /*gtk_container_add(GTK_CONTAINER(vbox), bbox);*/
+ gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ /* Close button */
+ bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_signal_connect_object(GTK_OBJECT(bt), "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT(dlg));
+ gtk_container_add(GTK_CONTAINER(bbox), bt);
+ GTK_WIDGET_SET_FLAGS(bt, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default(bt);
+ dlg_set_cancel(dlg, bt);
+
+ gtk_widget_show_all(dlg);
+
+}
+
diff --git a/gtk2/proto_hier_stats_dlg.h b/gtk2/proto_hier_stats_dlg.h
new file mode 100644
index 0000000000..b88b6016fc
--- /dev/null
+++ b/gtk2/proto_hier_stats_dlg.h
@@ -0,0 +1,27 @@
+/* proto_hier_stats_dlg.h
+ *
+ * $Id: proto_hier_stats_dlg.h,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+
+void
+proto_hier_stats_cb(GtkWidget *w, gpointer d);
diff --git a/gtk2/simple_dialog.c b/gtk2/simple_dialog.c
new file mode 100644
index 0000000000..1930986760
--- /dev/null
+++ b/gtk2/simple_dialog.c
@@ -0,0 +1,179 @@
+/* simple_dialog.c
+ * Simple message dialog box routines.
+ *
+ * $Id: simple_dialog.c,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib.h>
+
+#include <gtk/gtk.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#ifdef NEED_SNPRINTF_H
+# include "snprintf.h"
+#endif
+
+#include "gtkglobals.h"
+#include "simple_dialog.h"
+#include "dlg_utils.h"
+
+#include "image/eexcl3d64.xpm"
+#include "image/eicon3d64.xpm"
+
+static void simple_dialog_cancel_cb(GtkWidget *, gpointer);
+
+static const gchar bm_key[] = "button mask";
+
+/* Simple dialog function - Displays a dialog box with the supplied message
+ * text.
+ *
+ * Args:
+ * type : One of ESD_TYPE_*.
+ * btn_mask : The address of a gint. The value passed in determines if
+ * the 'Cancel' button is displayed. The button pressed by the
+ * user is passed back.
+ * msg_format : Sprintf-style format of the text displayed in the dialog.
+ * ... : Argument list for msg_format
+ *
+ */
+
+#define ESD_MAX_MSG_LEN 2048
+void
+simple_dialog(gint type, gint *btn_mask, gchar *msg_format, ...) {
+ GtkWidget *win, *main_vb, *top_hb, *type_pm, *msg_label,
+ *bbox, *ok_btn, *cancel_btn;
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+ GtkStyle *style;
+ GdkColormap *cmap;
+ va_list ap;
+ gchar message[ESD_MAX_MSG_LEN];
+ gchar **icon;
+
+ /* Main window */
+ switch (type & ~ESD_TYPE_MODAL) {
+ case ESD_TYPE_WARN :
+ icon = eexcl3d64_xpm;
+ win = dlg_window_new("Ethereal: Warning");
+ break;
+ case ESD_TYPE_CRIT :
+ icon = eexcl3d64_xpm;
+ win = dlg_window_new("Ethereal: Error");
+ break;
+ case ESD_TYPE_INFO :
+ default :
+ icon = eicon3d64_xpm;
+ win = dlg_window_new("Ethereal: Information");
+ break;
+ }
+
+ if (type & ESD_TYPE_MODAL)
+ gtk_window_set_modal(GTK_WINDOW(win), TRUE);
+
+ gtk_container_border_width(GTK_CONTAINER(win), 7);
+
+ gtk_object_set_data(GTK_OBJECT(win), bm_key, btn_mask);
+
+ /* Container for our rows */
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(win), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* Top row: Icon and message text */
+ top_hb = gtk_hbox_new(FALSE, 10);
+ gtk_container_add(GTK_CONTAINER(main_vb), top_hb);
+ gtk_widget_show(top_hb);
+
+ style = gtk_widget_get_style(win);
+ cmap = gdk_colormap_get_system();
+ pixmap = gdk_pixmap_colormap_create_from_xpm_d(NULL, cmap, &mask,
+ &style->bg[GTK_STATE_NORMAL], icon);
+ type_pm = gtk_pixmap_new(pixmap, mask);
+ gtk_misc_set_alignment (GTK_MISC (type_pm), 0.5, 0.0);
+ gtk_container_add(GTK_CONTAINER(top_hb), type_pm);
+ gtk_widget_show(type_pm);
+
+ /* Load our vararg list into the message string */
+ va_start(ap, msg_format);
+ vsnprintf(message, ESD_MAX_MSG_LEN, msg_format, ap);
+ va_end(ap);
+
+ msg_label = gtk_label_new(message);
+ gtk_label_set_justify(GTK_LABEL(msg_label), GTK_JUSTIFY_FILL);
+ gtk_container_add(GTK_CONTAINER(top_hb), msg_label);
+ gtk_widget_show(msg_label);
+
+ /* Button row */
+ bbox = gtk_hbutton_box_new();
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
+ gtk_container_add(GTK_CONTAINER(main_vb), bbox);
+ gtk_widget_show(bbox);
+
+ ok_btn = gtk_button_new_from_stock(GTK_STOCK_OK);
+ gtk_signal_connect_object(GTK_OBJECT(ok_btn), "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT(win));
+ gtk_container_add(GTK_CONTAINER(bbox), ok_btn);
+ GTK_WIDGET_SET_FLAGS(ok_btn, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default(ok_btn);
+ gtk_widget_show(ok_btn);
+
+ if (btn_mask && *btn_mask == ESD_BTN_CANCEL) {
+ cancel_btn = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+ g_signal_connect(G_OBJECT(cancel_btn), "clicked",
+ G_CALLBACK(simple_dialog_cancel_cb), (gpointer) win);
+ gtk_container_add(GTK_CONTAINER(bbox), cancel_btn);
+ GTK_WIDGET_SET_FLAGS(cancel_btn, GTK_CAN_DEFAULT);
+ gtk_widget_show(cancel_btn);
+
+ /* 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(win, cancel_btn);
+ } else {
+ /* Catch the "key_press_event" signal in the window, so that we can catch
+ the ESC key being pressed and act as if the "OK" button had
+ been selected. */
+ dlg_set_cancel(win, ok_btn);
+ }
+
+ if (btn_mask)
+ *btn_mask = ESD_BTN_OK;
+
+ gtk_widget_show(win);
+}
+
+static void
+simple_dialog_cancel_cb(GtkWidget *w _U_, gpointer win) {
+ gint *btn_mask = (gint *) gtk_object_get_data(win, bm_key);
+
+ if (btn_mask)
+ *btn_mask = ESD_BTN_CANCEL;
+ gtk_widget_destroy(GTK_WIDGET(win));
+}
diff --git a/gtk2/stream_prefs.c b/gtk2/stream_prefs.c
new file mode 100644
index 0000000000..72163e616e
--- /dev/null
+++ b/gtk2/stream_prefs.c
@@ -0,0 +1,213 @@
+/* stream_prefs.c
+ * Dialog boxes for preferences for the stream window
+ *
+ * $Id: stream_prefs.c,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <gtk/gtk.h>
+
+#include "color.h"
+#include "color_utils.h"
+#include "globals.h"
+#include "stream_prefs.h"
+#include "keys.h"
+#include "print.h"
+#include "prefs.h"
+
+static void update_text_color(GtkWidget *, gpointer);
+static void update_current_color(GtkWidget *, gpointer);
+
+static GdkColor tcolors[4], *curcolor = NULL;
+
+#define SAMPLE_CLIENT_TEXT "Sample client text\n"
+#define SAMPLE_SERVER_TEXT "Sample server text\n"
+#define CFG_IDX 0
+#define CBG_IDX 1
+#define SFG_IDX 2
+#define SBG_IDX 3
+#define STREAM_SAMPLE_KEY "stream_entry"
+#define STREAM_CS_KEY "stream_colorselection"
+#define CS_RED 0
+#define CS_GREEN 1
+#define CS_BLUE 2
+#define CS_OPACITY 3
+
+GtkWidget *
+stream_prefs_show()
+{
+ GtkWidget *main_vb, *main_tb, *label, *optmenu, *menu, *menuitem;
+ GtkWidget *sample, *colorsel;
+ int width, height, i;
+ gchar *mt[] = { "Client foreground", "Client background",
+ "Server foreground", "Server background" };
+ int mcount = sizeof(mt) / sizeof (gchar *);
+ gdouble scolor[4];
+ GtkTextBuffer *buf;
+ GtkTextIter iter;
+
+ color_t_to_gdkcolor(&tcolors[CFG_IDX], &prefs.st_client_fg);
+ color_t_to_gdkcolor(&tcolors[CBG_IDX], &prefs.st_client_bg);
+ color_t_to_gdkcolor(&tcolors[SFG_IDX], &prefs.st_server_fg);
+ color_t_to_gdkcolor(&tcolors[SBG_IDX], &prefs.st_server_bg);
+
+ curcolor = &tcolors[CFG_IDX];
+
+ scolor[CS_RED] = (gdouble) (curcolor->red) / 65535.0;
+ scolor[CS_GREEN] = (gdouble) (curcolor->green) / 65535.0;
+ scolor[CS_BLUE] = (gdouble) (curcolor->blue) / 65535.0;
+ scolor[CS_OPACITY] = 1.0;
+
+ /* Enclosing containers for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+
+ main_tb = gtk_table_new(3, 3, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
+ gtk_widget_show(main_tb);
+
+ label = gtk_label_new("Set:");
+ gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, 0, 1);
+ gtk_widget_show(label);
+
+ /* We have to create this now, and configure it below. */
+ colorsel = gtk_color_selection_new();
+
+ optmenu = gtk_option_menu_new ();
+ menu = gtk_menu_new ();
+ for (i = 0; i < mcount; i++){
+ menuitem = gtk_menu_item_new_with_label (mt[i]);
+ gtk_object_set_data(GTK_OBJECT(menuitem), STREAM_CS_KEY,
+ (gpointer) colorsel);
+ g_signal_connect(G_OBJECT(menuitem), "activate",
+ G_CALLBACK(update_current_color), &tcolors[i]);
+ gtk_widget_show (menuitem);
+ gtk_menu_append (GTK_MENU (menu), menuitem);
+ }
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (optmenu), menu);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), optmenu, 1, 2, 0, 1);
+ gtk_widget_show(optmenu);
+
+ sample = gtk_text_view_new();
+ height = 2 * (gtk_style_get_font(sample->style)->ascent +
+ gtk_style_get_font(sample->style)->descent);
+ width = gdk_string_width(gtk_style_get_font(sample->style),
+ "Sample server text");
+ gtk_widget_set_size_request(GTK_WIDGET(sample), width, height);
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(sample), FALSE);
+ buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(sample));
+ gtk_text_buffer_get_start_iter(buf, &iter);
+ gtk_text_buffer_create_tag(buf, "client",
+ "foreground-gdk", &tcolors[CFG_IDX],
+ "background-gdk", &tcolors[CBG_IDX], NULL);
+ gtk_text_buffer_create_tag(buf, "server",
+ "foreground-gdk", &tcolors[SFG_IDX],
+ "background-gdk", &tcolors[SBG_IDX], NULL);
+ gtk_text_buffer_insert_with_tags_by_name(buf, &iter, SAMPLE_CLIENT_TEXT, -1,
+ "client", NULL);
+ gtk_text_buffer_insert_with_tags_by_name(buf, &iter, SAMPLE_SERVER_TEXT, -1,
+ "server", NULL);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), sample, 2, 3, 0, 2);
+ gtk_widget_show(sample);
+
+ gtk_color_selection_set_color(GTK_COLOR_SELECTION(colorsel), &scolor[CS_RED]);
+ gtk_table_attach(GTK_TABLE(main_tb), colorsel, 0, 3, 2, 3,
+ GTK_SHRINK, GTK_SHRINK, 0, 0);
+
+ gtk_object_set_data(GTK_OBJECT(colorsel), STREAM_SAMPLE_KEY,
+ (gpointer) sample);
+ g_signal_connect(G_OBJECT(colorsel), "color-changed",
+ G_CALLBACK(update_text_color), NULL);
+ gtk_widget_show(colorsel);
+
+ gtk_widget_show(main_vb);
+ return(main_vb);
+}
+
+static void
+update_text_color(GtkWidget *w, gpointer data _U_) {
+ GtkTextView *sample = gtk_object_get_data(GTK_OBJECT(w), STREAM_SAMPLE_KEY);
+ gdouble scolor[4];
+ GtkTextBuffer *buf;
+ GtkTextTag *tag;
+
+
+ gtk_color_selection_get_color(GTK_COLOR_SELECTION(w), &scolor[CS_RED]);
+
+ curcolor->red = (gushort) (scolor[CS_RED] * 65535.0);
+ curcolor->green = (gushort) (scolor[CS_GREEN] * 65535.0);
+ curcolor->blue = (gushort) (scolor[CS_BLUE] * 65535.0);
+
+ buf = gtk_text_view_get_buffer(sample);
+ tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buf), "client");
+ g_object_set(tag, "foreground-gdk", &tcolors[CFG_IDX], "background-gdk",
+ &tcolors[CBG_IDX], NULL);
+ tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buf), "server");
+ g_object_set(tag, "foreground-gdk", &tcolors[SFG_IDX], "background-gdk",
+ &tcolors[SBG_IDX], NULL);
+}
+
+static void
+update_current_color(GtkWidget *w, gpointer data)
+{
+ GtkColorSelection *colorsel = GTK_COLOR_SELECTION(gtk_object_get_data(GTK_OBJECT(w),
+ STREAM_CS_KEY));
+ gdouble scolor[4];
+
+ curcolor = (GdkColor *) data;
+
+ scolor[CS_RED] = (gdouble) (curcolor->red) / 65535.0;
+ scolor[CS_GREEN] = (gdouble) (curcolor->green) / 65535.0;
+ scolor[CS_BLUE] = (gdouble) (curcolor->blue) / 65535.0;
+ scolor[CS_OPACITY] = 1.0;
+
+ gtk_color_selection_set_color(colorsel, &scolor[CS_RED]);
+}
+
+void
+stream_prefs_fetch(GtkWidget *w _U_)
+{
+ gdkcolor_to_color_t(&prefs.st_client_fg, &tcolors[CFG_IDX]);
+ gdkcolor_to_color_t(&prefs.st_client_bg, &tcolors[CBG_IDX]);
+ gdkcolor_to_color_t(&prefs.st_server_fg, &tcolors[SFG_IDX]);
+ gdkcolor_to_color_t(&prefs.st_server_bg, &tcolors[SBG_IDX]);
+}
+
+/* XXX - "gui_prefs_apply()" handles this, as the "Follow TCP Stream"
+ windows may have to be redrawn due to a font change; this means
+ that calling "stream_prefs_apply()" without calling "gui_prefs_apply()"
+ won't work. */
+void
+stream_prefs_apply(GtkWidget *w _U_)
+{
+}
+
+void
+stream_prefs_destroy(GtkWidget *w _U_)
+{
+}
diff --git a/gtk2/stream_prefs.h b/gtk2/stream_prefs.h
new file mode 100644
index 0000000000..06bb58743e
--- /dev/null
+++ b/gtk2/stream_prefs.h
@@ -0,0 +1,34 @@
+/* stream_prefs.h
+ * Definitions for stream preferences window
+ *
+ * $Id: stream_prefs.h,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.org>
+ * Copyright 1999 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.
+ */
+
+#ifndef __STREAM_PREFS_H__
+#define __STREAM_PREFS_H__
+
+GtkWidget *stream_prefs_show(void);
+void stream_prefs_fetch(GtkWidget *w);
+void stream_prefs_apply(GtkWidget *w);
+void stream_prefs_destroy(GtkWidget *w);
+
+#endif
diff --git a/gtk2/summary_dlg.c b/gtk2/summary_dlg.c
new file mode 100644
index 0000000000..1c4c27a47c
--- /dev/null
+++ b/gtk2/summary_dlg.c
@@ -0,0 +1,231 @@
+/* summary_dlg.c
+ * Routines for capture file summary window
+ *
+ * $Id: summary_dlg.c,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <wtap.h>
+
+#ifdef NEED_SNPRINTF_H
+# include "snprintf.h"
+#endif
+
+#include "summary.h"
+#include "summary_dlg.h"
+#include "dlg_utils.h"
+#include "ui_util.h"
+
+#define SUM_STR_MAX 1024
+
+
+static void
+add_string_to_box(gchar *str, GtkWidget *box)
+{
+ GtkWidget *lb;
+ lb = gtk_label_new(str);
+ gtk_misc_set_alignment(GTK_MISC(lb), 0.0, 0.5);
+ gtk_box_pack_start(GTK_BOX(box), lb,FALSE,FALSE, 0);
+ gtk_widget_show(lb);
+}
+
+
+void
+summary_open_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ summary_tally summary;
+ GtkWidget *sum_open_w,
+ *main_vb, *file_fr, *data_fr, *capture_fr, *file_box,
+ *data_box, *capture_box, *bbox, *close_bt;
+
+ gchar string_buff[SUM_STR_MAX];
+
+ double seconds;
+
+ /* initialize the tally */
+ summary_fill_in(&summary);
+
+ /* initial compututations */
+ seconds = summary.stop_time - summary.start_time;
+ sum_open_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(sum_open_w), "Ethereal: Summary");
+ g_signal_connect(G_OBJECT(sum_open_w), "realize",
+ G_CALLBACK(window_icon_realize_cb), NULL);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(sum_open_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* File frame */
+ file_fr = gtk_frame_new("File");
+ gtk_container_add(GTK_CONTAINER(main_vb), file_fr);
+ gtk_widget_show(file_fr);
+
+ file_box = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(file_fr), file_box);
+ gtk_widget_show(file_box);
+
+ /* filename */
+ snprintf(string_buff, SUM_STR_MAX, "Name: %s", summary.filename);
+ add_string_to_box(string_buff, file_box);
+
+ /* length */
+ snprintf(string_buff, SUM_STR_MAX, "Length: %lu", summary.file_length);
+ add_string_to_box(string_buff, file_box);
+
+ /* format */
+ snprintf(string_buff, SUM_STR_MAX, "Format: %s", wtap_file_type_string(summary.encap_type));
+ add_string_to_box(string_buff, file_box);
+
+ if (summary.has_snap) {
+ /* snapshot length */
+ snprintf(string_buff, SUM_STR_MAX, "Snapshot length: %u", summary.snap);
+ add_string_to_box(string_buff, file_box);
+ }
+
+ /* Data frame */
+ data_fr = gtk_frame_new("Data");
+ gtk_container_add(GTK_CONTAINER(main_vb), data_fr);
+ gtk_widget_show(data_fr);
+
+ data_box = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(data_fr), data_box);
+ gtk_widget_show(data_box);
+
+ /* seconds */
+ snprintf(string_buff, SUM_STR_MAX, "Elapsed time: %.3f seconds", summary.elapsed_time);
+ add_string_to_box(string_buff, data_box);
+
+ snprintf(string_buff, SUM_STR_MAX, "Between first and last packet: %.3f seconds", seconds);
+ add_string_to_box(string_buff, data_box);
+
+ /* Packet count */
+ snprintf(string_buff, SUM_STR_MAX, "Packet count: %i", summary.packet_count);
+ add_string_to_box(string_buff, data_box);
+
+ /* Filtered Packet count */
+ snprintf(string_buff, SUM_STR_MAX, "Filtered packet count: %i", summary.filtered_count);
+ add_string_to_box(string_buff, data_box);
+
+ /* Marked Packet count */
+ snprintf(string_buff, SUM_STR_MAX, "Marked packet count: %i", summary.marked_count);
+ add_string_to_box(string_buff, data_box);
+
+ /* Packets per second */
+ if (seconds > 0){
+ snprintf(string_buff, SUM_STR_MAX, "Avg. packets/sec: %.3f", summary.packet_count/seconds);
+ add_string_to_box(string_buff, data_box);
+ }
+
+ /* Dropped count */
+ if (summary.drops_known) {
+ snprintf(string_buff, SUM_STR_MAX, "Dropped packets: %u", summary.drops);
+ add_string_to_box(string_buff, data_box);
+ }
+
+ /* Byte count */
+ snprintf(string_buff, SUM_STR_MAX, "Bytes of traffic: %d", summary.bytes);
+ add_string_to_box(string_buff, data_box);
+
+ /* Bytes per second */
+ if (seconds > 0){
+ snprintf(string_buff, SUM_STR_MAX, "Avg. bytes/sec: %.3f", summary.bytes/seconds);
+ add_string_to_box(string_buff, data_box);
+
+ /* MBit per second */
+ snprintf(string_buff, SUM_STR_MAX, "Avg. Mbit/sec: %.3f",
+ summary.bytes * 8.0 / (seconds * 1000.0 * 1000.0));
+ add_string_to_box(string_buff, data_box);
+ }
+
+ /* Capture frame */
+ capture_fr = gtk_frame_new("Capture");
+ gtk_container_add(GTK_CONTAINER(main_vb), capture_fr);
+ gtk_widget_show(capture_fr);
+
+ capture_box = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(capture_fr), capture_box);
+ gtk_widget_show(capture_box);
+
+
+ /* interface */
+ if (summary.iface) {
+ snprintf(string_buff, SUM_STR_MAX, "Interface: %s", summary.iface);
+ } else {
+ sprintf(string_buff, "Interface: unknown");
+ }
+ add_string_to_box(string_buff, capture_box);
+
+ /* Display filter */
+ if (summary.dfilter) {
+ snprintf(string_buff, SUM_STR_MAX, "Display filter: %s", summary.dfilter);
+ } else {
+ sprintf(string_buff, "Display filter: none");
+ }
+ add_string_to_box(string_buff, capture_box);
+
+#ifdef HAVE_LIBPCAP
+ /* Capture filter */
+ if (summary.cfilter && summary.cfilter[0] != '\0') {
+ snprintf(string_buff, SUM_STR_MAX, "Capture filter: %s", summary.cfilter);
+ } else {
+ sprintf(string_buff, "Capture filter: none");
+ }
+ add_string_to_box(string_buff, capture_box);
+#endif
+
+ /* Button row: close button.
+ (We put it in an HButtonBox, even though there's only one of them,
+ so that it doesn't expand to the width of the window. */
+ bbox = gtk_hbutton_box_new();
+ gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
+ gtk_container_add(GTK_CONTAINER(main_vb), bbox);
+ gtk_widget_show(bbox);
+
+ /* Create Close Button */
+ close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_signal_connect_object(GTK_OBJECT(close_bt), "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT(sum_open_w));
+ GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
+ gtk_box_pack_start(GTK_BOX(bbox), close_bt, FALSE,FALSE, 0);
+ gtk_widget_grab_default(close_bt);
+ gtk_widget_show(close_bt);
+
+ /* Catch the "key_press_event" signal in the window, so that we can catch
+ the ESC key being pressed and act as if the "Close" button had
+ been selected. */
+ dlg_set_cancel(sum_open_w, close_bt);
+
+ gtk_window_set_position(GTK_WINDOW(sum_open_w), GTK_WIN_POS_MOUSE);
+ gtk_widget_show(sum_open_w);
+}
diff --git a/gtk2/summary_dlg.h b/gtk2/summary_dlg.h
new file mode 100644
index 0000000000..90b159a9ca
--- /dev/null
+++ b/gtk2/summary_dlg.h
@@ -0,0 +1,31 @@
+/* summary_dlg.h
+ * Routines for capture file summary window
+ *
+ * $Id: summary_dlg.h,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.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.
+ */
+
+#ifndef __SUMMARY_DLG_H__
+#define __SUMMARY_DLG_H__
+
+void summary_open_cb(GtkWidget *w, gpointer d);
+
+#endif /* __SUMMARY_DLG_H__ */
diff --git a/gtk2/tcp_graph.c b/gtk2/tcp_graph.c
new file mode 100644
index 0000000000..70325ac2e8
--- /dev/null
+++ b/gtk2/tcp_graph.c
@@ -0,0 +1,3772 @@
+/* tcp_graph.c
+ * TCP graph drawing code
+ * By Pavel Mores <pvl@uh.cz>
+ * Win32 port: rwh@unifiedtech.com
+ *
+ * $Id: tcp_graph.c,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h> /* rint() */
+
+#ifdef NEED_SNPRINTF_H
+# include "snprintf.h"
+#endif
+
+#include "ipproto.h"
+#include "globals.h" /* cfile */
+#include <epan/packet.h> /* frame_data */
+#include "gtkglobals.h" /* packet_list */
+#include "simple_dialog.h"
+#include "ui_util.h"
+#include "tcp_graph.h"
+
+/* from <net/ethernet.h> */
+struct ether_header {
+ guint8 ether_dhost[6]; /* destination eth addr */
+ guint8 ether_shost[6]; /* source ether addr */
+ guint16 ether_type; /* packet type ID field */
+};
+#define ETHERTYPE_IP 0x0800
+
+
+/* reverse engineered from capture file, not too difficult :) */
+struct ppp_header {
+ guint8 ppp_type; /* Protocol on PPP connection */
+};
+#define PPPTYPE_IP 0x21
+
+
+/* from <netinet/ip.h> */
+struct iphdr {
+ guint8 version_ihl;
+ guint8 tos;
+ guint16 tot_len;
+ guint16 id;
+ guint16 frag_off;
+ guint8 ttl;
+ guint8 protocol;
+ guint16 check;
+ guint32 saddr;
+ guint32 daddr;
+};
+
+#define IPHDR_IHL_SHIFT 0
+#define IPHDR_IHL_MASK (0xf << IPHDR_IHL_SHIFT)
+#define IHL(iphdrptr) ( ((iphdrptr)->version_ihl & IPHDR_IHL_MASK) >> IPHDR_IHL_SHIFT )
+
+/* from <netinet/tcp.h> */
+struct tcphdr {
+ guint16 source;
+ guint16 dest;
+ guint32 seq;
+ guint32 ack_seq;
+ guint16 flags;
+#define TH_FIN 0x01
+#define TH_SYN 0x02
+#define TH_RST 0x04
+#define TH_PUSH 0x08
+#define TH_ACK 0x10
+#define TH_URG 0x20
+ guint16 window;
+ guint16 check;
+ guint16 urg_ptr;
+};
+
+#define TCP_SYN(tcphdr) ( g_ntohs ((tcphdr).flags) & TH_SYN )
+#define TCP_ACK(tcphdr) ( g_ntohs ((tcphdr).flags) & TH_ACK )
+#define TCP_DOFF_SHIFT 12
+#define TCP_DOFF_MASK (0xf << TCP_DOFF_SHIFT)
+#define DOFF(tcphdr) ( ( g_ntohs ((tcphdr).flags) & TCP_DOFF_MASK) >> TCP_DOFF_SHIFT )
+
+#define TXT_WIDTH 850
+#define TXT_HEIGHT 550
+
+/* for compare_headers() */
+/* segment went the same direction as the currently selected one */
+#define COMPARE_CURR_DIR 0
+#define COMPARE_ANY_DIR 1
+
+/* initalize_axis() */
+#define AXIS_HORIZONTAL 0
+#define AXIS_VERTICAL 1
+
+struct segment {
+ struct segment *next;
+ guint32 num;
+ guint32 rel_secs;
+ guint32 rel_usecs;
+ guint32 abs_secs;
+ guint32 abs_usecs;
+ struct iphdr iphdr;
+ struct tcphdr tcphdr;
+ int data; /* amount of data in this segment */
+};
+
+struct rect {
+ double x, y, width, height;
+};
+
+struct line {
+ double x1, y1, x2, y2;
+};
+
+struct irect {
+ int x, y, width, height;
+};
+
+struct ipoint {
+ int x, y;
+};
+
+typedef enum {
+ ELMT_NONE=0,
+ ELMT_RECT=1,
+ ELMT_LINE=2,
+ ELMT_ARC=3
+} ElementType;
+
+struct rect_params {
+ struct rect dim;
+ gint filled;
+};
+
+struct line_params {
+ struct line dim;
+};
+
+struct arc_params {
+ struct rect dim;
+ gint filled;
+ gint angle1, angle2;
+};
+
+struct element {
+ ElementType type;
+ GdkGC *gc;
+ struct segment *parent;
+ union {
+ struct arc_params arc;
+ struct rect_params rect;
+ struct line_params line;
+ } p;
+};
+
+struct element_list {
+ struct element_list *next;
+ struct element *elements;
+};
+
+struct axis {
+ struct graph *g; /* which graph we belong to */
+ GtkWidget *drawing_area;
+ GdkPixmap *pixmap[2];
+ int displayed;
+#define AXIS_ORIENTATION 1 << 0
+ int flags;
+ /* dim and orig (relative to origin of window) of axis' pixmap */
+ struct irect p;
+ /* dim and orig (relative to origin of axis' pixmap) of scale itself */
+ struct irect s;
+ gdouble min, max;
+ gdouble major, minor; /* major and minor ticks */
+ char **label;
+};
+
+#define HAXIS_INIT_HEIGHT 70
+#define VAXIS_INIT_WIDTH 100
+#define TITLEBAR_HEIGHT 50
+#define RMARGIN_WIDTH 30
+
+struct style_tseq_tcptrace {
+ GdkGC *gc_seq;
+ GdkGC *gc_ack[2];
+ int flags;
+};
+
+struct style_tseq_stevens {
+ int seq_width;
+ int seq_height;
+ int flags;
+};
+
+struct style_tput {
+ int width, height;
+ int nsegs;
+ int flags;
+};
+
+struct style_rtt {
+ int width, height;
+ int flags;
+};
+
+/* style flags */
+#define SEQ_ORIGIN 0x1
+/* show absolute sequence numbers (not differences from isn) */
+#define SEQ_ORIGIN_ZERO 0x1
+#define SEQ_ORIGIN_ISN 0x0
+#define TIME_ORIGIN 0x10
+/* show time from beginning of capture as opposed to time from beginning
+ * of the connection */
+#define TIME_ORIGIN_CAP 0x10
+#define TIME_ORIGIN_CONN 0x0
+
+/* this is used by rtt module only */
+struct unack {
+ struct unack *next;
+ double time;
+ unsigned int seqno;
+};
+
+struct cross {
+ int x, y;
+ int draw; /* indicates whether we should draw cross at all */
+ int erase_needed;
+ GtkToggleButton *on_toggle;
+ GtkToggleButton *off_toggle;
+};
+
+struct bounds {
+ double x0, y0, width, height;
+};
+
+struct zoom {
+ double x, y;
+};
+
+struct zooms {
+ double x, y;
+ double step_x, step_y;
+ struct zoom initial;
+#define ZOOM_OUT (1 << 0)
+#define ZOOM_HLOCK (1 << 1)
+#define ZOOM_VLOCK (1 << 2)
+#define ZOOM_STEPS_SAME (1 << 3)
+#define ZOOM_STEPS_KEEP_RATIO (1 << 4)
+ int flags;
+ /* unfortunately, we need them both because gtk_toggle_button_set_active ()
+ * with second argument FALSE doesn't do anything, somehow */
+ struct {
+ GtkToggleButton *in_toggle;
+ GtkToggleButton *out_toggle;
+ GtkEntry *h_zoom;
+ GtkEntry *v_zoom;
+ GtkSpinButton *h_step;
+ GtkSpinButton *v_step;
+ } widget;
+};
+
+struct grab {
+ int grabbed;
+ int x, y;
+};
+
+struct magnify {
+ int active;
+ int x, y;
+ struct ipoint offset;
+ int width, height;
+ struct zoom zoom;
+ struct graph *g;
+#define MAGZOOMS_SAME (1 << 0)
+#define MAGZOOMS_SAME_RATIO (1 << 1)
+#define MAGZOOMS_IGNORE (1 << 31)
+ int flags;
+ struct {
+ GtkSpinButton *h_zoom, *v_zoom;
+ } widget;
+};
+
+struct graph {
+ struct graph *next;
+#define GRAPH_TSEQ_STEVENS 0
+#define GRAPH_TSEQ_TCPTRACE 1
+#define GRAPH_THROUGHPUT 2
+#define GRAPH_RTT 3
+ int type;
+#define GRAPH_DESTROYED (1 << 0)
+#define GRAPH_INIT_ON_TYPE_CHANGE (1 << 1)
+ int flags;
+ GtkWidget *toplevel; /* keypress handler needs this */
+ GtkWidget *drawing_area;
+ GtkWidget *text; /* text widget for seg list - probably temporary */
+ PangoFontDescription *font; /* font used for annotations etc. */
+ GdkGC *fg_gc;
+ GdkGC *bg_gc;
+ GdkPixmap *title_pixmap;
+ GdkPixmap *pixmap[2];
+ int displayed; /* which of both pixmaps is on screen right now */
+ struct {
+ GtkWidget *control_panel;
+ /* this belongs to style structs of graph types that make use of it */
+ GtkToggleButton *time_orig_conn, *seq_orig_isn;
+ } gui;
+ char **title;
+ /* Next 4 attribs describe the graph in natural units, before any scaling.
+ * For example, if we want to display graph of TCP conversation that
+ * started 112.309845 s after beginning of the capture and ran until
+ * 479.093582 s, 237019 B went through the connection (in one direction)
+ * starting with isn 31934022, then (bounds.x0, bounds.y0)=(112.309845,
+ * 31934022) and (bounds.width, bounds.height)=(366.783737, 237019). */
+ struct bounds bounds;
+ /* dimensions and position of the graph, both expressed already in pixels.
+ * x and y give the position of upper left corner of the graph relative
+ * to origin of the graph window, size is basically bounds*zoom */
+ struct irect geom;
+ /* viewport (=graph window area which is reserved for graph itself), its
+ * size and position relative to origin of the graph window */
+ struct irect wp;
+ struct grab grab;
+ /* If we need to display 237019 sequence numbers (=bytes) onto say 500
+ * pixels, we have to scale the graph down by factor of 0.002109. This
+ * number would be zoom.y. Obviously, both directions have separate zooms.*/
+ struct zooms zoom;
+ struct cross cross;
+ struct magnify magnify;
+ struct axis *x_axis, *y_axis;
+ struct segment *segments;
+ struct segment *current;
+ struct element_list *elists; /* element lists */
+ union {
+ struct style_tseq_stevens tseq_stevens;
+ struct style_tseq_tcptrace tseq_tcptrace;
+ struct style_tput tput;
+ struct style_rtt rtt;
+ } s;
+};
+
+static struct graph *graphs = NULL;
+static GdkGC *xor_gc = NULL;
+static int refnum=0;
+
+#define debug(section) if (debugging & section)
+/* print function entry points */
+#define DBS_FENTRY (1 << 0)
+#define DBS_AXES_TICKS (1 << 1)
+#define DBS_AXES_DRAWING (1 << 2)
+#define DBS_GRAPH_DRAWING (1 << 3)
+#define DBS_TPUT_ELMTS (1 << 4)
+/*int debugging = DBS_FENTRY;*/
+int debugging = 0;
+/*int debugging = DBS_AXES_TICKS;*/
+/*int debugging = DBS_AXES_DRAWING;*/
+/*int debugging = DBS_GRAPH_DRAWING;*/
+/*int debugging = DBS_TPUT_ELMTS;*/
+
+static void create_gui (struct graph * );
+#if 0
+static void create_text_widget (struct graph * );
+static void display_text (struct graph * );
+#endif
+static void create_drawing_area (struct graph * );
+static void control_panel_create (struct graph * );
+static GtkWidget *control_panel_create_zoom_group (struct graph * );
+static GtkWidget *control_panel_create_magnify_group (struct graph * );
+static GtkWidget *control_panel_create_cross_group (struct graph * );
+static GtkWidget *control_panel_create_zoomlock_group (struct graph * );
+static GtkWidget *control_panel_create_graph_type_group (struct graph * );
+static void control_panel_add_zoom_page (struct graph * , GtkWidget * );
+static void control_panel_add_magnify_page (struct graph * , GtkWidget * );
+static void control_panel_add_origin_page (struct graph * , GtkWidget * );
+static void control_panel_add_cross_page (struct graph * , GtkWidget * );
+static void control_panel_add_graph_type_page (struct graph * , GtkWidget * );
+static void callback_toplevel_destroy (GtkWidget * , gpointer );
+static void callback_close (GtkWidget * , gpointer );
+static void callback_time_origin (GtkWidget * , gpointer );
+static void callback_seq_origin (GtkWidget * , gpointer );
+static void callback_zoomlock_h (GtkWidget * , gpointer );
+static void callback_zoomlock_v (GtkWidget * , gpointer );
+static void callback_zoom_inout (GtkWidget * , gpointer );
+static void callback_zoom_step (GtkWidget * , gpointer );
+static void callback_zoom_flags (GtkWidget * , gpointer );
+static void callback_cross_on_off (GtkWidget * , gpointer );
+static void callback_mag_width (GtkWidget * , gpointer );
+static void callback_mag_height (GtkWidget * , gpointer );
+static void callback_mag_x (GtkWidget * , gpointer );
+static void callback_mag_y (GtkWidget * , gpointer );
+static void callback_mag_zoom (GtkWidget * , gpointer );
+static void callback_mag_flags (GtkWidget * , gpointer );
+static void callback_graph_type (GtkWidget * , gpointer );
+static void callback_graph_init_on_typechg (GtkWidget * , gpointer );
+static void callback_create_help (GtkWidget * , gpointer );
+static void callback_close_help (GtkWidget * , gpointer );
+static void update_zoom_spins (struct graph * );
+static int get_headers (frame_data *, char * , struct segment * );
+static int compare_headers (struct segment * , struct segment * , int );
+static int get_num_dsegs (struct graph * );
+static int get_num_acks (struct graph * );
+static void graph_type_dependent_initialize (struct graph * );
+static void graph_put (struct graph * );
+static struct graph *graph_new (void);
+static void graph_destroy (struct graph * );
+static void graph_initialize_values (struct graph * );
+static void graph_init_sequence (struct graph * );
+static void draw_element_line (struct graph * , struct element * );
+static void draw_element_arc (struct graph * , struct element * );
+static void graph_display (struct graph * );
+static void graph_pixmaps_create (struct graph * );
+static void graph_pixmaps_switch (struct graph * );
+static void graph_pixmap_draw (struct graph * );
+static void graph_pixmap_display (struct graph * );
+static void graph_element_lists_make (struct graph * );
+static void graph_element_lists_free (struct graph * );
+static void graph_element_lists_initialize (struct graph * );
+static void graph_title_pixmap_create (struct graph * );
+static void graph_title_pixmap_draw (struct graph * );
+static void graph_title_pixmap_display (struct graph * );
+static void graph_segment_list_get (struct graph * );
+static void graph_segment_list_free (struct graph * );
+static void graph_select_segment (struct graph * , int , int );
+static int line_detect_collision (struct element * , int , int );
+static int arc_detect_collision (struct element * , int , int );
+static void update_packet_list (int );
+static void axis_pixmaps_create (struct axis * );
+static void axis_pixmaps_switch (struct axis * );
+static void axis_display (struct axis * );
+static void v_axis_pixmap_draw (struct axis * );
+static void h_axis_pixmap_draw (struct axis * );
+static void axis_pixmap_display (struct axis * );
+static void axis_compute_ticks (struct axis * , double , double , int );
+static double axis_zoom_get (struct axis * , int );
+static void axis_ticks_up (int * , int * );
+static void axis_ticks_down (int * , int * );
+static void axis_destroy (struct axis * );
+static int get_label_dim (struct axis * , int , double );
+static void toggle_time_origin (struct graph * );
+static void toggle_seq_origin (struct graph * );
+static void cross_xor (struct graph * , int , int );
+static void cross_draw (struct graph * , int , int );
+static void cross_erase (struct graph * );
+static void magnify_create (struct graph * , int , int );
+static void magnify_move (struct graph * , int , int );
+static void magnify_destroy (struct graph * );
+static void magnify_draw (struct graph * );
+static void magnify_get_geom (struct graph * , int , int );
+static gint configure_event (GtkWidget * , GdkEventConfigure * );
+static gint expose_event (GtkWidget * , GdkEventExpose * );
+static gint button_press_event (GtkWidget * , GdkEventButton * );
+static gint button_release_event (GtkWidget * , GdkEventButton * );
+static gint motion_notify_event (GtkWidget * , GdkEventMotion * );
+static gint key_press_event (GtkWidget * , GdkEventKey * );
+static gint key_release_event (GtkWidget * , GdkEventKey * );
+static gint leave_notify_event (GtkWidget * , GdkEventCrossing * );
+static gint enter_notify_event (GtkWidget * , GdkEventCrossing * );
+static void tseq_stevens_initialize (struct graph * );
+static void tseq_stevens_get_bounds (struct graph * );
+static void tseq_stevens_read_config (struct graph * );
+static void tseq_stevens_make_elmtlist (struct graph * );
+static void tseq_stevens_toggle_seq_origin (struct graph * );
+static void tseq_stevens_toggle_time_origin (struct graph * );
+static void tseq_tcptrace_read_config (struct graph * );
+static void tseq_tcptrace_make_elmtlist (struct graph * );
+static void tseq_tcptrace_toggle_seq_origin (struct graph * );
+static void tseq_tcptrace_toggle_time_origin (struct graph * );
+static void tput_initialize (struct graph * );
+static void tput_read_config (struct graph * );
+static void tput_make_elmtlist (struct graph * );
+static void tput_toggle_time_origin (struct graph * );
+static void rtt_read_config (struct graph * );
+static void rtt_initialize (struct graph * );
+static int rtt_is_retrans (struct unack * , unsigned int );
+static struct unack *rtt_get_new_unack (double , unsigned int );
+static void rtt_put_unack_on_list (struct unack ** , struct unack * );
+static void rtt_delete_unack_from_list (struct unack ** , struct unack * );
+static void rtt_make_elmtlist (struct graph * );
+static void rtt_toggle_seq_origin (struct graph * );
+#ifdef WIN32
+static int rint (double ); /* compiler template for Windows */
+#endif
+
+static char helptext[] =
+#ifndef WIN32
+"Here's what you can do:\n\
+- Left Mouse Button selects segment in ethereal's packet list\n\
+- Middle Mouse Button zooms in\n\
+- <shift>-Middle Button zooms out\n\
+- Right Mouse Button moves the graph (if zoomed in)\n\
+- <ctrl>-Right Mouse Button displays a portion of graph magnified\n\
+- Space toggles crosshairs\n\
+- 's' toggles relative/absolute sequence numbers\n\
+- 't' toggles time origin\n\
+";
+#else /* WIN32 */
+"Here's what you can do:\n\
+- <ctrl>-Left Mouse Button selects segment in ethereal's packet list\n\
+- Left Mouse Button zooms in\n\
+- <shift>-Left Mouse Button zooms out\n\
+- Right Mouse Button moves the graph (if zoomed in)\n\
+- <ctrl>-Right Mouse Button displays a portion of graph magnified\n\
+\n\
+- Space bar toggles crosshairs\n\
+- 's' - Toggles relative/absolute sequence numbers\n\
+- 't' - Toggles time origin\n\
+";
+#endif
+
+void tcp_graph_cb (GtkWidget *w _U_, gpointer data _U_, guint graph_type)
+{
+ struct segment current;
+ struct graph *g;
+
+ debug(DBS_FENTRY) puts ("tcp_graph_cb()");
+
+ if (! (g = graph_new()))
+ return;
+
+ refnum++;
+ graph_initialize_values (g);
+ graph_put (g);
+
+ g->type = graph_type;
+ if (!get_headers (cfile.current_frame, cfile.pd, &current)) {
+ /* currently selected packet is neither TCP over IP over Ethernet II/PPP
+ * nor TCP over IP alone - should display some
+ * kind of warning dialog */
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "Selected packet is not a TCP segment");
+ return;
+ }
+
+ graph_segment_list_get(g);
+ create_gui(g);
+ /* display_text(g); */
+ graph_init_sequence(g);
+}
+
+static void create_gui (struct graph *g)
+{
+ debug(DBS_FENTRY) puts ("create_gui()");
+ /* create_text_widget(g); */
+ control_panel_create (g);
+ create_drawing_area(g);
+}
+
+#if 0
+static void create_text_widget (struct graph *g)
+{
+ GtkWidget *streamwindow, *txt_scrollw, *box;
+
+ debug(DBS_FENTRY) puts ("create_text_widget()");
+ streamwindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_name (streamwindow, "Packet chain");
+ gtk_widget_set_size_request(GTK_WIDGET(streamwindow), TXT_WIDTH,
+ TXT_HEIGHT);
+ gtk_container_border_width (GTK_CONTAINER(streamwindow), 2);
+ g_signal_connect(G_OBJECT(streamwindow), "realize",
+ G_CALLBACK(window_icon_realize_cb), NULL);
+
+ box = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (streamwindow), box);
+ gtk_widget_show (box);
+
+ txt_scrollw = scrolled_window_new (NULL, NULL);
+ gtk_box_pack_start (GTK_BOX (box), txt_scrollw, TRUE, TRUE, 0);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (txt_scrollw),
+ GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
+ gtk_widget_show (txt_scrollw);
+
+ g->text = gtk_text_view_new();
+ gtk_text_view_set_editable (GTK_TEXT_VIEW(g->text), FALSE);
+ gtk_container_add (GTK_CONTAINER (txt_scrollw), g->text);
+ gtk_widget_show (g->text);
+ gtk_widget_show (streamwindow);
+}
+static void display_text (struct graph *g)
+{
+ char *line[256];
+ struct segment *ptr;
+ double first_time, prev_time;
+ unsigned int isn_this=0, isn_opposite=0, seq_this_prev, seq_opposite_prev;
+ GdkColor color, *c;
+ GtkTextBuffer *buf;
+ GtkTextIter iter;
+
+ debug(DBS_FENTRY) puts ("display_text()");
+ gdk_color_parse ("SlateGray", &color);
+ snprintf ((char * )line, 256, "%10s%15s%15s%15s%15s%15s%15s%10s\n",
+ "pkt num", "time", "delta first", "delta prev",
+ "seqno", "delta first", "delta prev", "data (B)");
+ buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(g->text));
+ gtk_text_buffer_get_start_iter(buf, &iter);
+ gtk_text_buffer_insert(buf, &iter, (const char *)line, -1);
+
+ first_time = g->segments->rel_secs + g->segments->rel_usecs/1000000.0;
+ prev_time = first_time;
+ /* we have to find Initial Sequence Number for both ends of connection */
+ for (ptr=g->segments; ptr; ptr=ptr->next) {
+ if (compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
+ isn_this = g_ntohl (ptr->tcphdr.seq);
+ break;
+ }
+ }
+ for (ptr=g->segments; ptr; ptr=ptr->next) {
+ if (!compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
+ isn_opposite = g_ntohl (ptr->tcphdr.seq);
+ break;
+ }
+ }
+ seq_this_prev = isn_this;
+ seq_opposite_prev = isn_opposite;
+ for (ptr=g->segments; ptr; ptr=ptr->next) {
+ double time=ptr->rel_secs + ptr->rel_usecs/1000000.0;
+ unsigned int seq = ntohl (ptr->tcphdr.seq);
+ int seq_delta_isn, seq_delta_prev;
+
+ if (compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
+ seq_delta_isn = seq - isn_this;
+ seq_delta_prev = seq - seq_this_prev;
+ seq_this_prev = seq;
+ c = NULL;
+ } else {
+ seq_delta_isn = seq - isn_opposite;
+ seq_delta_prev = seq - seq_opposite_prev;
+ seq_opposite_prev = seq;
+ c = &color;
+ }
+ snprintf ((char *)line, 256, "%10d%15.6f%15.6f%15.6f%15u%15d%15d%10u\n",
+ ptr->num, time, time-first_time, time-prev_time,
+ seq, seq_delta_isn, seq_delta_prev,
+ g_ntohs (ptr->iphdr.tot_len) - 4*IHL(&(ptr->iphdr)) -
+ 4*DOFF(ptr->tcphdr));
+ gtk_text_buffer_insert(buf, &iter, (const char * )line, -1);
+ prev_time = time;
+ }
+}
+#endif
+
+static void create_drawing_area (struct graph *g)
+{
+ GdkColormap *colormap;
+ GdkColor color;
+#define WINDOW_TITLE_LENGTH 64
+ char window_title[WINDOW_TITLE_LENGTH];
+
+ debug(DBS_FENTRY) puts ("create_drawing_area()");
+ g->toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_name (g->toplevel, "Test Graph");
+ g_signal_connect(G_OBJECT(g->toplevel), "realize",
+ G_CALLBACK(window_icon_realize_cb), NULL);
+
+ /* Create the drawing area */
+ g->drawing_area = gtk_drawing_area_new ();
+ g->x_axis->drawing_area = g->y_axis->drawing_area = g->drawing_area;
+ gtk_drawing_area_size (GTK_DRAWING_AREA (g->drawing_area),
+ g->wp.width + g->wp.x + RMARGIN_WIDTH,
+ g->wp.height + g->wp.y + g->x_axis->s.height);
+ gtk_widget_show (g->drawing_area);
+
+ g_signal_connect(GTK_OBJECT(g->drawing_area), "expose_event",
+ G_CALLBACK(expose_event), NULL);
+ /* this has to be done later, after the widget has been shown */
+ /*
+ gtk_signal_connect (GTK_OBJECT(g->drawing_area),"configure_event",
+ (GtkSignalFunc )configure_event, NULL);
+ */
+ g_signal_connect(G_OBJECT(g->drawing_area), "motion_notify_event",
+ G_CALLBACK(motion_notify_event), NULL);
+ g_signal_connect(G_OBJECT(g->drawing_area), "button_press_event",
+ G_CALLBACK(button_press_event), NULL);
+ g_signal_connect(G_OBJECT(g->drawing_area), "button_release_event",
+ G_CALLBACK(button_release_event), NULL);
+ g_signal_connect(G_OBJECT(g->drawing_area), "leave_notify_event",
+ G_CALLBACK(leave_notify_event), NULL);
+ g_signal_connect(G_OBJECT(g->drawing_area), "enter_notify_event",
+ G_CALLBACK(enter_notify_event), NULL);
+ g_signal_connect(G_OBJECT(g->toplevel), "destroy",
+ G_CALLBACK(callback_toplevel_destroy), g);
+ /* why doesn't drawing area send key_press_signals? */
+ g_signal_connect(G_OBJECT(g->toplevel), "key_press_event",
+ G_CALLBACK(key_press_event), NULL);
+ g_signal_connect(G_OBJECT(g->toplevel), "key_release_event",
+ G_CALLBACK(key_release_event), NULL);
+ gtk_widget_set_events (g->toplevel,GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK);
+
+ gtk_widget_set_events (g->drawing_area, GDK_EXPOSURE_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_ENTER_NOTIFY_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK);
+
+#if 0
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
+ gtk_container_add (GTK_CONTAINER (frame), g->drawing_area);
+
+ box = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box), g->gui.control_panel, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box), frame, TRUE, TRUE, 0);
+ gtk_container_add (GTK_CONTAINER (g->toplevel), box);
+ gtk_container_set_border_width (GTK_CONTAINER (g->toplevel), 5);
+ gtk_widget_show (frame);
+ gtk_widget_show (box);
+#endif
+
+ gtk_container_add (GTK_CONTAINER (g->toplevel), g->drawing_area);
+ gtk_widget_show (g->toplevel);
+ snprintf (window_title, WINDOW_TITLE_LENGTH, "TCP Graph %d - Ethereal",
+ refnum);
+ gtk_window_set_title (GTK_WINDOW (g->toplevel), window_title);
+
+ /* in case we didn't get what we asked for */
+ g->wp.width = GTK_WIDGET (g->drawing_area)->allocation.width -
+ g->wp.x - RMARGIN_WIDTH;
+ g->wp.height = GTK_WIDGET (g->drawing_area)->allocation.height -
+ g->wp.y - g->x_axis->s.height;
+
+ g->font = g->drawing_area->style->font_desc;
+
+ colormap = gdk_window_get_colormap (g->drawing_area->window);
+ if (!xor_gc) {
+ xor_gc = gdk_gc_new (g->drawing_area->window);
+ gdk_gc_set_function (xor_gc, GDK_XOR);
+ gdk_color_parse ("gray15", &color);
+ gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
+ gdk_gc_set_foreground (xor_gc, &color);
+ }
+ g->fg_gc = gdk_gc_new (g->drawing_area->window);
+ g->bg_gc = gdk_gc_new (g->drawing_area->window);
+ gdk_color_parse ("white", &color);
+ gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
+ gdk_gc_set_foreground (g->bg_gc, &color);
+
+ /* this is probably quite an ugly way to get rid of the first configure
+ * event
+ * immediatelly after gtk_widget_show (window) drawing_area gets a configure
+ * event which is handled during the next return to gtk_main which is
+ * probably the gdk_gc_new() call. configure handler calls
+ * graph_element_lists_make() which is not good because the graph struct is
+ * not fully set up yet - namely we're not sure about actual geometry
+ * and we don't have the GC's at all. so we just postpone installation
+ * of configure handler until we're ready to deal with it.
+ *
+ * !!! NEMÌLO BY TO BÝT NA KONCI graph_init_sequence()? !!!
+ *
+ */
+ g_signal_connect(G_OBJECT(g->drawing_area),"configure_event",
+ G_CALLBACK(configure_event), NULL);
+
+ /* puts ("exiting create_drawing_area()"); */
+}
+
+static void callback_toplevel_destroy (GtkWidget *widget _U_, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ if (!(g->flags & GRAPH_DESTROYED)) {
+ g->flags |= GRAPH_DESTROYED;
+ graph_destroy ((struct graph * )data);
+ }
+}
+
+static void control_panel_create (struct graph *g)
+{
+ GtkWidget *toplevel, *notebook;
+ GtkWidget *table;
+ GtkWidget *help, *close, *button_box;
+#define WINDOW_TITLE_LENGTH 64
+ char window_title[WINDOW_TITLE_LENGTH];
+
+ debug(DBS_FENTRY) puts ("control_panel_create()");
+
+ notebook = gtk_notebook_new ();
+ control_panel_add_zoom_page (g, notebook);
+ control_panel_add_magnify_page (g, notebook);
+ control_panel_add_origin_page (g, notebook);
+ control_panel_add_cross_page (g, notebook);
+ control_panel_add_graph_type_page (g, notebook);
+
+ /* bottom buttons group */
+ help = gtk_button_new_from_stock(GTK_STOCK_HELP);
+ close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ button_box = gtk_hbox_new (TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (button_box), help, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (button_box), close, TRUE, TRUE, 0);
+
+ toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ g_signal_connect(G_OBJECT(toplevel), "realize",
+ G_CALLBACK(window_icon_realize_cb), NULL);
+
+ table = gtk_table_new (2, 1, FALSE);
+ gtk_container_add (GTK_CONTAINER (toplevel), table);
+
+ gtk_table_attach (GTK_TABLE (table), notebook, 0, 1, 0, 1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+ gtk_table_attach (GTK_TABLE (table), button_box, 0, 1, 1, 2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+
+ g_signal_connect(G_OBJECT(close), "clicked",
+ G_CALLBACK(callback_close), g);
+ g_signal_connect(GTK_OBJECT(help), "clicked",
+ G_CALLBACK(callback_create_help), g);
+
+ /* gtk_widget_show_all (table); */
+ /* g->gui.control_panel = table; */
+ gtk_widget_show_all (toplevel);
+ snprintf (window_title, WINDOW_TITLE_LENGTH,
+ "Graph %d - Control - Ethereal", refnum);
+ gtk_window_set_title (GTK_WINDOW (toplevel), window_title);
+ g->gui.control_panel = toplevel;
+}
+
+static void control_panel_add_zoom_page (struct graph *g, GtkWidget *n)
+{
+ GtkWidget *zoom_frame;
+ GtkWidget *zoom_lock_frame;
+ GtkWidget *label;
+ GtkWidget *box;
+
+ zoom_frame = control_panel_create_zoom_group (g);
+ gtk_container_set_border_width (GTK_CONTAINER (zoom_frame), 5);
+ zoom_lock_frame = control_panel_create_zoomlock_group (g);
+ gtk_container_set_border_width (GTK_CONTAINER (zoom_lock_frame), 5);
+ box = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box), zoom_frame, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (box), zoom_lock_frame, TRUE, TRUE, 0);
+ gtk_widget_show (box);
+ label = gtk_label_new ("Zoom");
+ gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
+}
+
+static void control_panel_add_magnify_page (struct graph *g, GtkWidget *n)
+{
+ GtkWidget *mag_frame, *label;
+
+ mag_frame = control_panel_create_magnify_group (g);
+ gtk_container_set_border_width (GTK_CONTAINER (mag_frame), 5);
+ label = gtk_label_new ("Magnify");
+ gtk_notebook_append_page (GTK_NOTEBOOK (n), mag_frame, label);
+}
+
+static void control_panel_add_origin_page (struct graph *g, GtkWidget *n)
+{
+ GtkWidget *time_orig_cap, *time_orig_conn, *time_orig_box, *time_orig_frame;
+ GtkWidget *seq_orig_isn, *seq_orig_zero, *seq_orig_box, *seq_orig_frame;
+ GtkWidget *box, *label;
+
+ /* time origin box */
+ time_orig_cap =
+ gtk_radio_button_new_with_label (NULL, "beginning of capture");
+ time_orig_conn = gtk_radio_button_new_with_label (
+ gtk_radio_button_group (GTK_RADIO_BUTTON (time_orig_cap)),
+ "beginning of this TCP connection");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (time_orig_conn), TRUE);
+ time_orig_box = gtk_vbox_new (TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_conn, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_cap, TRUE, TRUE, 0);
+ time_orig_frame = gtk_frame_new ("Time origin");
+ gtk_container_set_border_width (GTK_CONTAINER (time_orig_frame), 5);
+ gtk_container_add (GTK_CONTAINER (time_orig_frame), time_orig_box);
+
+ /* sequence number origin group */
+ seq_orig_isn =
+ gtk_radio_button_new_with_label (NULL, "initial sequence number");
+ seq_orig_zero = gtk_radio_button_new_with_label (gtk_radio_button_group (
+ GTK_RADIO_BUTTON (seq_orig_isn)), "0 (=absolute)");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (seq_orig_isn), TRUE);
+ seq_orig_box = gtk_vbox_new (TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_isn, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_zero, TRUE, TRUE, 0);
+ seq_orig_frame = gtk_frame_new ("Sequence number origin");
+ gtk_container_set_border_width (GTK_CONTAINER (seq_orig_frame), 5);
+ gtk_container_add (GTK_CONTAINER (seq_orig_frame), seq_orig_box);
+
+ g->gui.time_orig_conn = (GtkToggleButton * )time_orig_conn;
+ g->gui.seq_orig_isn = (GtkToggleButton * )seq_orig_isn;
+
+ g_signal_connect(G_OBJECT(time_orig_conn), "toggled",
+ G_CALLBACK(callback_time_origin), g);
+ g_signal_connect(G_OBJECT(seq_orig_isn), "toggled",
+ G_CALLBACK(callback_seq_origin), g);
+
+ box = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (box), 5);
+ gtk_box_pack_start (GTK_BOX (box), time_orig_frame, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (box), seq_orig_frame, TRUE, TRUE, 0);
+ gtk_widget_show (box);
+ label = gtk_label_new ("Origin");
+ gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
+}
+
+static void control_panel_add_cross_page (struct graph *g, GtkWidget *n)
+{
+ GtkWidget *cross_frame, *label;
+
+ cross_frame = control_panel_create_cross_group (g);
+ gtk_container_set_border_width (GTK_CONTAINER (cross_frame), 5);
+ label = gtk_label_new ("Cross");
+ gtk_notebook_append_page (GTK_NOTEBOOK (n), cross_frame, label);
+}
+
+static void control_panel_add_graph_type_page (struct graph *g, GtkWidget *n)
+{
+ GtkWidget *frame, *label;
+
+ frame = control_panel_create_graph_type_group (g);
+ gtk_container_set_border_width (GTK_CONTAINER (frame), 5);
+ label = gtk_label_new ("Graph type");
+ gtk_notebook_append_page (GTK_NOTEBOOK (n), frame, label);
+}
+
+static void callback_close (GtkWidget *widget _U_, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ if (!(g->flags & GRAPH_DESTROYED)) {
+ g->flags |= GRAPH_DESTROYED;
+ graph_destroy ((struct graph * )data);
+ }
+}
+
+static void callback_create_help (GtkWidget *widget _U_, gpointer data _U_)
+{
+ GtkWidget *toplevel, *box, *text, *scroll, *close;
+ GtkTextBuffer *buf;
+
+ toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(toplevel), "Help for TCP graphing");
+ gtk_widget_set_size_request(toplevel, 500, 400);
+ g_signal_connect(G_OBJECT(toplevel), "realize",
+ G_CALLBACK(window_icon_realize_cb), NULL);
+
+ box = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (toplevel), box);
+ scroll = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start (GTK_BOX (box), scroll, TRUE, TRUE, 0);
+ text = gtk_text_view_new();
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
+ buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
+ gtk_text_buffer_set_text(buf, helptext, -1);
+ gtk_container_add (GTK_CONTAINER (scroll), text);
+ close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_box_pack_start (GTK_BOX (box), close, FALSE, FALSE, 0);
+ g_signal_connect(G_OBJECT(close), "clicked",
+ G_CALLBACK(callback_close_help), toplevel);
+
+ gtk_widget_show_all (toplevel);
+}
+
+static void callback_close_help (GtkWidget *widget _U_, gpointer data)
+{
+ gtk_widget_destroy ((GtkWidget * )data);
+}
+
+static void callback_time_origin (GtkWidget *toggle _U_, gpointer data)
+{
+ toggle_time_origin ((struct graph * )data);
+}
+
+static void callback_seq_origin (GtkWidget *toggle _U_, gpointer data)
+{
+ toggle_seq_origin ((struct graph * )data);
+}
+
+static GtkWidget *control_panel_create_zoom_group (struct graph *g)
+{
+ GtkWidget *zoom_in, *zoom_out, *zoom_box, *zoom_frame;
+ GtkAdjustment *zoom_h_adj, *zoom_v_adj;
+ GtkWidget *zoom_inout_box, *zoom_h_step_label, *zoom_h_step;
+ GtkWidget *zoom_v_step_label, *zoom_v_step;
+ GtkWidget *zoom_separator1, *zoom_separator2, *zoom_step_table, *zoom_table;
+ GtkWidget *zoom_ratio_toggle, *zoom_same_toggle;
+ GtkWidget *zoom_h_entry, *zoom_v_entry;
+ GtkWidget *zoom_h_label, *zoom_v_label;
+
+ zoom_in = gtk_radio_button_new_with_label (NULL, "in");
+ zoom_out = gtk_radio_button_new_with_label (
+ gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_in)), "out");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_in), TRUE);
+ zoom_inout_box = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_in, FALSE, FALSE, 10);
+ gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_out, FALSE, FALSE, 0);
+
+ zoom_separator1 = gtk_hseparator_new ();
+
+ zoom_h_entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (zoom_h_entry), "1.000");
+ gtk_editable_set_editable (GTK_EDITABLE (zoom_h_entry), FALSE);
+ zoom_h_label = gtk_label_new ("Horizontal:");
+
+ zoom_v_entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (zoom_v_entry), "1.000");
+ gtk_editable_set_editable (GTK_EDITABLE (zoom_v_entry), FALSE);
+ zoom_v_label = gtk_label_new ("Vertical:");
+
+ g->zoom.widget.h_zoom = (GtkEntry * )zoom_h_entry;
+ g->zoom.widget.v_zoom = (GtkEntry * )zoom_v_entry;
+
+ zoom_table = gtk_table_new (2, 2, FALSE);
+ gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_label, 0,1,0,1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_entry, 1, 2, 0, 1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_label, 0,1,1,2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_entry, 1, 2, 1, 2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+
+ zoom_separator2 = gtk_hseparator_new ();
+
+ zoom_h_adj = (GtkAdjustment * )gtk_adjustment_new (1.2, 1.0, 5, 0.1, 1, 0);
+ zoom_h_step = gtk_spin_button_new (zoom_h_adj, 0, 1);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_h_step), TRUE);
+ zoom_h_step_label = gtk_label_new ("Horizontal step:");
+
+ zoom_v_adj = (GtkAdjustment * )gtk_adjustment_new (1.2, 1.0, 5, 0.1, 1, 0);
+ zoom_v_step = gtk_spin_button_new (zoom_v_adj, 0, 1);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_v_step), TRUE);
+ zoom_v_step_label = gtk_label_new ("Vertical step:");
+
+ g->zoom.widget.h_step = (GtkSpinButton * )zoom_h_step;
+ g->zoom.widget.v_step = (GtkSpinButton * )zoom_v_step;
+
+ zoom_same_toggle = gtk_check_button_new_with_label ("Keep them the same");
+ zoom_ratio_toggle = gtk_check_button_new_with_label("Preserve their ratio");
+ gtk_object_set_data (GTK_OBJECT (zoom_same_toggle), "flag",
+ (gpointer )ZOOM_STEPS_SAME);
+ gtk_object_set_data (GTK_OBJECT (zoom_ratio_toggle), "flag",
+ (gpointer )ZOOM_STEPS_KEEP_RATIO);
+ g_signal_connect(G_OBJECT(zoom_same_toggle), "clicked",
+ G_CALLBACK(callback_zoom_flags), g);
+ g_signal_connect(G_OBJECT(zoom_ratio_toggle), "clicked",
+ G_CALLBACK(callback_zoom_flags), g);
+
+ zoom_step_table = gtk_table_new (4, 2, FALSE);
+ gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step_label, 0,1,0,1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step, 1, 2, 0, 1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step_label, 0,1,1,2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step, 1, 2, 1, 2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_same_toggle, 0,2,2,3,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_ratio_toggle, 0,2,3,4,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+
+ zoom_box = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (zoom_box), zoom_inout_box, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator1, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (zoom_box), zoom_table, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator2, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (zoom_box), zoom_step_table, TRUE, TRUE, 0);
+ zoom_frame = gtk_frame_new ("Zoom");
+ gtk_container_add (GTK_CONTAINER (zoom_frame), zoom_box);
+
+ gtk_object_set_data (GTK_OBJECT (zoom_h_step), "direction", (gpointer )0);
+ gtk_object_set_data (GTK_OBJECT (zoom_v_step), "direction", (gpointer )1);
+
+ g_signal_connect(G_OBJECT(zoom_in), "toggled",
+ G_CALLBACK(callback_zoom_inout), g);
+ g_signal_connect(G_OBJECT(zoom_h_step), "changed",
+ G_CALLBACK(callback_zoom_step), g);
+ g_signal_connect(G_OBJECT(zoom_v_step), "changed",
+ G_CALLBACK(callback_zoom_step), g);
+
+ g->zoom.widget.in_toggle = (GtkToggleButton * )zoom_in;
+ g->zoom.widget.out_toggle = (GtkToggleButton * )zoom_out;
+ return zoom_frame;
+}
+
+static void callback_zoom_inout (GtkWidget *toggle, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ if (GTK_TOGGLE_BUTTON (toggle)->active)
+ g->zoom.flags &= ~ZOOM_OUT;
+ else
+ g->zoom.flags |= ZOOM_OUT;
+}
+
+static void callback_zoom_step (GtkWidget *spin, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+ float value;
+ int direction;
+ double *zoom_this, *zoom_other;
+ GtkSpinButton *widget_this, *widget_other;
+ double old_this;
+
+ direction = (int )gtk_object_get_data (GTK_OBJECT (spin), "direction");
+ value = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (spin));
+
+ if (direction) {
+ zoom_this = &g->zoom.step_y;
+ zoom_other = &g->zoom.step_x;
+ widget_this = g->zoom.widget.v_step;
+ widget_other = g->zoom.widget.h_step;
+ } else {
+ zoom_this = &g->zoom.step_x;
+ zoom_other = &g->zoom.step_y;
+ widget_this = g->zoom.widget.h_step;
+ widget_other = g->zoom.widget.v_step;
+ }
+
+ old_this = *zoom_this;
+ *zoom_this = value;
+ if (g->zoom.flags & ZOOM_STEPS_SAME) {
+ *zoom_other = value;
+ gtk_spin_button_set_value (widget_other, *zoom_other);
+ } else if (g->zoom.flags & ZOOM_STEPS_KEEP_RATIO) {
+ double old_other = *zoom_other;
+ *zoom_other *= value / old_this;
+ if (*zoom_other < 1.0) {
+ *zoom_other = 1.0;
+ *zoom_this = old_this * 1.0 / old_other;
+ gtk_spin_button_set_value (widget_this, *zoom_this);
+ } else if (*zoom_other > 5.0) {
+ *zoom_other = 5.0;
+ *zoom_this = old_this * 5.0 / old_other;
+ gtk_spin_button_set_value (widget_this, *zoom_this);
+ }
+ gtk_spin_button_set_value (widget_other, *zoom_other);
+ }
+}
+
+static void callback_zoom_flags (GtkWidget *toggle, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+ int flag = (int )gtk_object_get_data (GTK_OBJECT (toggle), "flag");
+
+ if (GTK_TOGGLE_BUTTON (toggle)->active)
+ g->zoom.flags |= flag;
+ else
+ g->zoom.flags &= ~flag;
+}
+
+static void update_zoom_spins (struct graph *g)
+{
+ char s[32];
+
+ snprintf (s, 32, "%.3f", g->zoom.x / g->zoom.initial.x);
+ gtk_entry_set_text (g->zoom.widget.h_zoom, s);
+ snprintf (s, 32, "%.3f", g->zoom.y / g->zoom.initial.y);
+ gtk_entry_set_text (g->zoom.widget.v_zoom, s);
+}
+
+static GtkWidget *control_panel_create_magnify_group (struct graph *g)
+{
+ GtkWidget *mag_width_label, *mag_width;
+ GtkWidget *mag_height_label, *mag_height;
+ GtkWidget *mag_x_label, *mag_x;
+ GtkWidget *mag_y_label, *mag_y;
+ GtkWidget *mag_wh_table, *mag_zoom_frame, *mag_zoom_table;
+ GtkWidget *mag_h_zoom_label, *mag_h_zoom;
+ GtkWidget *mag_v_zoom_label, *mag_v_zoom;
+ GtkWidget *mag_zoom_same, *mag_zoom_ratio;
+ GtkAdjustment *mag_width_adj, *mag_height_adj, *mag_x_adj, *mag_y_adj;
+ GtkAdjustment *mag_h_zoom_adj, *mag_v_zoom_adj;
+ GtkWidget *mag_box, *mag_frame;
+
+ mag_width_label = gtk_label_new ("Width:");
+ mag_width_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
+ mag_width = gtk_spin_button_new (mag_width_adj, 0, 0);
+
+ mag_height_label = gtk_label_new ("Height:");
+ mag_height_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
+ mag_height = gtk_spin_button_new (mag_height_adj, 0, 0);
+
+ mag_x_label = gtk_label_new ("X:");
+ mag_x_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
+ mag_x = gtk_spin_button_new (mag_x_adj, 0, 0);
+
+ mag_y_label = gtk_label_new ("Y:");
+ mag_y_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
+ mag_y = gtk_spin_button_new (mag_y_adj, 0, 0);
+
+ mag_wh_table = gtk_table_new (4, 2, FALSE);
+ gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width_label, 0,1,0,1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width, 1,2,0,1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height_label, 0,1,1,2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height, 1,2,1,2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x_label, 0,1,2,3,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x, 1,2,2,3,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y_label, 0,1,3,4,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y, 1,2,3,4,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+
+ mag_h_zoom_label = gtk_label_new ("Horizontal:");
+ mag_h_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0,1.0,25.0,0.1,1,0);
+ mag_h_zoom = gtk_spin_button_new (mag_h_zoom_adj, 0, 1);
+
+ mag_v_zoom_label = gtk_label_new ("Vertical:");
+ mag_v_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0,1.0,25.0,0.1,1,0);
+ mag_v_zoom = gtk_spin_button_new (mag_v_zoom_adj, 0, 1);
+
+ mag_zoom_same = gtk_check_button_new_with_label ("Keep them the same");
+ mag_zoom_ratio = gtk_check_button_new_with_label("Preserve their ratio");
+
+ mag_zoom_table = gtk_table_new (4, 2, FALSE);
+ gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom_label, 0,1,0,1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+ gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom, 1,2,0,1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+ gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom_label, 0,1,1,2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+ gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom, 1,2,1,2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+ gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_same, 0,2,2,3,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+ gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_ratio, 0,2,3,4,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+
+ mag_zoom_frame = gtk_frame_new ("Magnify zoom");
+ gtk_container_add (GTK_CONTAINER (mag_zoom_frame), mag_zoom_table);
+ gtk_container_set_border_width (GTK_CONTAINER (mag_zoom_frame), 3);
+
+ mag_box = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (mag_box), mag_wh_table, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (mag_box), mag_zoom_frame, TRUE, TRUE, 0);
+ mag_frame = gtk_frame_new ("Magnify");
+ gtk_container_add (GTK_CONTAINER (mag_frame), mag_box);
+
+ g->magnify.widget.h_zoom = (GtkSpinButton * )mag_h_zoom;
+ g->magnify.widget.v_zoom = (GtkSpinButton * )mag_v_zoom;
+ gtk_object_set_data (GTK_OBJECT (mag_h_zoom), "direction", (gpointer )0);
+ gtk_object_set_data (GTK_OBJECT (mag_v_zoom), "direction", (gpointer )1);
+ gtk_object_set_data (GTK_OBJECT (mag_zoom_same), "flag",
+ (gpointer )MAGZOOMS_SAME);
+ gtk_object_set_data (GTK_OBJECT (mag_zoom_ratio), "flag",
+ (gpointer )MAGZOOMS_SAME_RATIO);
+
+ g_signal_connect(G_OBJECT(mag_width), "changed",
+ G_CALLBACK(callback_mag_width), g);
+ g_signal_connect(G_OBJECT(mag_height), "changed",
+ G_CALLBACK(callback_mag_height), g);
+ g_signal_connect(G_OBJECT(mag_x), "changed",
+ G_CALLBACK(callback_mag_x), g);
+ g_signal_connect(G_OBJECT(mag_y), "changed",
+ G_CALLBACK(callback_mag_y), g);
+ g_signal_connect(G_OBJECT(mag_h_zoom), "changed",
+ G_CALLBACK(callback_mag_zoom), g);
+ g_signal_connect(G_OBJECT(mag_v_zoom), "changed",
+ G_CALLBACK(callback_mag_zoom), g);
+ g_signal_connect(G_OBJECT(mag_zoom_same), "clicked",
+ G_CALLBACK(callback_mag_flags), g);
+ g_signal_connect(G_OBJECT(mag_zoom_ratio), "clicked",
+ G_CALLBACK(callback_mag_flags), g);
+
+ return mag_frame;
+}
+
+static void callback_mag_width (GtkWidget *spin, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ g->magnify.width = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));
+}
+
+static void callback_mag_height (GtkWidget *spin, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ g->magnify.height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
+}
+
+static void callback_mag_x (GtkWidget *spin, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ g->magnify.offset.x=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
+}
+
+static void callback_mag_y (GtkWidget *spin, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ g->magnify.offset.y=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
+}
+
+static void callback_mag_zoom (GtkWidget *spin, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+ float value;
+ int direction;
+ double *zoom_this, *zoom_other;
+ GtkSpinButton *widget_this, *widget_other;
+ double old_this;
+
+ if (g->magnify.flags & MAGZOOMS_IGNORE) {
+ printf ("refusing callback for %s zoom widget.\n", (GtkSpinButton * )spin==g->magnify.widget.h_zoom ? "horizontal" : "vertical");
+ g->magnify.flags &= ~MAGZOOMS_IGNORE;
+ return;
+ }
+ direction = (int )gtk_object_get_data (GTK_OBJECT (spin), "direction");
+ value = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (spin));
+
+ if (direction) {
+ zoom_this = &g->magnify.zoom.y;
+ zoom_other = &g->magnify.zoom.x;
+ widget_this = g->magnify.widget.v_zoom;
+ widget_other = g->magnify.widget.h_zoom;
+ } else {
+ zoom_this = &g->magnify.zoom.x;
+ zoom_other = &g->magnify.zoom.y;
+ widget_this = g->magnify.widget.h_zoom;
+ widget_other = g->magnify.widget.v_zoom;
+ }
+
+ old_this = *zoom_this;
+ *zoom_this = value;
+ if (g->magnify.flags & MAGZOOMS_SAME) {
+ *zoom_other = value;
+ /* g->magnify.flags |= MAGZOOMS_IGNORE; */
+ gtk_spin_button_set_value (widget_other, *zoom_other);
+ } else if (g->magnify.flags & MAGZOOMS_SAME_RATIO) {
+ double old_other = *zoom_other;
+ *zoom_other *= value / old_this;
+ if (*zoom_other < 1.0) {
+ *zoom_other = 1.0;
+ *zoom_this = old_this * 1.0 / old_other;
+ /* g->magnify.flags |= MAGZOOMS_IGNORE; */
+ gtk_spin_button_set_value (widget_this, *zoom_this);
+ } else if (*zoom_other > 25.0) {
+ *zoom_other = 25.0;
+ *zoom_this = old_this * 25.0 / old_other;
+ /* g->magnify.flags |= MAGZOOMS_IGNORE; */
+ gtk_spin_button_set_value (widget_this, *zoom_this);
+ }
+ /* g->magnify.flags |= MAGZOOMS_IGNORE; */
+ gtk_spin_button_set_value (widget_other, *zoom_other);
+ }
+}
+
+static void callback_mag_flags (GtkWidget *toggle, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+ int flag = (int )gtk_object_get_data (GTK_OBJECT (toggle), "flag");
+
+ if (GTK_TOGGLE_BUTTON (toggle)->active)
+ g->magnify.flags |= flag;
+ else
+ g->magnify.flags &= ~flag;
+}
+
+static GtkWidget *control_panel_create_zoomlock_group (struct graph *g)
+{
+ GtkWidget *zoom_lock_h, *zoom_lock_v, *zoom_lock_none, *zoom_lock_box;
+ GtkWidget *zoom_lock_frame;
+
+ zoom_lock_none = gtk_radio_button_new_with_label (NULL, "none");
+ zoom_lock_h = gtk_radio_button_new_with_label (
+ gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_lock_none)),
+ "horizontal");
+ zoom_lock_v = gtk_radio_button_new_with_label (
+ gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_lock_none)),
+ "vertical");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_lock_none), TRUE);
+ zoom_lock_box = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (zoom_lock_box), zoom_lock_none, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (zoom_lock_box), zoom_lock_h, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (zoom_lock_box), zoom_lock_v, TRUE, TRUE, 0);
+ zoom_lock_frame = gtk_frame_new ("Zoom lock:");
+ gtk_container_add (GTK_CONTAINER (zoom_lock_frame), zoom_lock_box);
+
+ g_signal_connect(G_OBJECT(zoom_lock_h), "toggled",
+ G_CALLBACK(callback_zoomlock_h), g);
+ g_signal_connect(G_OBJECT(zoom_lock_v), "toggled",
+ G_CALLBACK(callback_zoomlock_v), g);
+
+ return zoom_lock_frame;
+}
+
+static void callback_zoomlock_h (GtkWidget *toggle, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ if (GTK_TOGGLE_BUTTON (toggle)->active)
+ g->zoom.flags |= ZOOM_HLOCK;
+ else
+ g->zoom.flags &= ~ZOOM_HLOCK;
+}
+
+static void callback_zoomlock_v (GtkWidget *toggle, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ if (GTK_TOGGLE_BUTTON (toggle)->active)
+ g->zoom.flags |= ZOOM_VLOCK;
+ else
+ g->zoom.flags &= ~ZOOM_VLOCK;
+}
+
+static GtkWidget *control_panel_create_cross_group (struct graph *g)
+{
+ GtkWidget *on, *off, *box, *frame, *vbox, *label;
+
+ label = gtk_label_new ("Crosshairs:");
+ off = gtk_radio_button_new_with_label (NULL, "off");
+ on = gtk_radio_button_new_with_label (
+ gtk_radio_button_group (GTK_RADIO_BUTTON (off)), "on");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (off), TRUE);
+ box = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 10);
+ gtk_box_pack_start (GTK_BOX (box), off, FALSE, FALSE, 10);
+ gtk_box_pack_start (GTK_BOX (box), on, FALSE, FALSE, 0);
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 15);
+ /* frame = gtk_frame_new ("Cross:"); */
+ frame = gtk_frame_new (NULL);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ g_signal_connect(G_OBJECT(on), "toggled",
+ G_CALLBACK(callback_cross_on_off), g);
+
+ g->cross.on_toggle = (GtkToggleButton * )on;
+ g->cross.off_toggle = (GtkToggleButton * )off;
+
+ return frame;
+}
+
+static void callback_cross_on_off (GtkWidget *toggle, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ if (GTK_TOGGLE_BUTTON (toggle)->active) {
+ int x, y;
+ g->cross.draw = TRUE;
+ gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
+ cross_draw (g, x, y);
+ } else {
+ g->cross.draw = FALSE;
+ cross_erase (g);
+ }
+}
+
+static GtkWidget *control_panel_create_graph_type_group (struct graph *g)
+{
+ GtkWidget *graph_tseqttrace, *graph_tseqstevens;
+ GtkWidget *graph_tput, *graph_rtt, *graph_sep, *graph_init, *graph_box;
+ GtkWidget *graph_frame;
+
+ graph_tput = gtk_radio_button_new_with_label (NULL, "Throughput");
+ graph_tseqttrace = gtk_radio_button_new_with_label (
+ gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
+ "Time/Sequence (tcptrace-style)");
+ graph_tseqstevens = gtk_radio_button_new_with_label (
+ gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
+ "Time/Sequence (Stevens'-style)");
+ graph_rtt = gtk_radio_button_new_with_label (
+ gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
+ "Round-trip Time");
+ switch (g->type) {
+ case GRAPH_TSEQ_STEVENS:
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tseqstevens),TRUE);
+ break;
+ case GRAPH_TSEQ_TCPTRACE:
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(graph_tseqttrace),TRUE);
+ break;
+ case GRAPH_THROUGHPUT:
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_tput), TRUE);
+ break;
+ case GRAPH_RTT:
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_rtt), TRUE);
+ break;
+ }
+ graph_init = gtk_check_button_new_with_label ("Init on change");
+ graph_sep = gtk_hseparator_new ();
+ graph_box = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqttrace, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqstevens, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (graph_box), graph_tput, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (graph_box), graph_rtt, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (graph_box), graph_sep, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (graph_box), graph_init, TRUE, TRUE, 0);
+ graph_frame = gtk_frame_new ("Graph type:");
+ gtk_container_add (GTK_CONTAINER (graph_frame), graph_box);
+
+ gtk_object_set_data (GTK_OBJECT (graph_tseqstevens), "new-graph-type",
+ (gpointer )0);
+ gtk_object_set_data (GTK_OBJECT (graph_tseqttrace), "new-graph-type",
+ (gpointer )1);
+ gtk_object_set_data (GTK_OBJECT (graph_tput), "new-graph-type",
+ (gpointer )2);
+ gtk_object_set_data (GTK_OBJECT (graph_rtt), "new-graph-type",
+ (gpointer )3);
+
+ g_signal_connect(G_OBJECT(graph_tseqttrace), "toggled",
+ G_CALLBACK(callback_graph_type), g);
+ g_signal_connect(G_OBJECT(graph_tseqstevens), "toggled",
+ G_CALLBACK(callback_graph_type), g);
+ g_signal_connect(G_OBJECT(graph_tput), "toggled",
+ G_CALLBACK(callback_graph_type), g);
+ g_signal_connect(G_OBJECT(graph_rtt), "toggled",
+ G_CALLBACK(callback_graph_type), g);
+ g_signal_connect(G_OBJECT(graph_init), "toggled",
+ G_CALLBACK(callback_graph_init_on_typechg), g);
+
+ return graph_frame;
+}
+
+static void callback_graph_type (GtkWidget *toggle, gpointer data)
+{
+ int old_type, new_type;
+ struct graph *g = (struct graph * )data;
+
+ new_type = (int )gtk_object_get_data (GTK_OBJECT (toggle),"new-graph-type");
+
+ if (!GTK_TOGGLE_BUTTON (toggle)->active)
+ return;
+
+ old_type = g->type;
+ g->type = new_type;
+
+ graph_element_lists_free (g);
+ graph_element_lists_initialize (g);
+
+ if (old_type == GRAPH_THROUGHPUT || new_type == GRAPH_THROUGHPUT) {
+ /* throughput graph uses differently constructed segment list so we
+ * need to recreate it */
+ graph_segment_list_free (g);
+ graph_segment_list_get (g);
+ }
+
+ if (g->flags & GRAPH_INIT_ON_TYPE_CHANGE) {
+ g->geom.width = g->wp.width;
+ g->geom.height = g->wp.height;
+ g->geom.x = g->wp.x;
+ g->geom.y = g->wp.y;
+ }
+ g->x_axis->min = g->y_axis->min = 0;
+ gtk_toggle_button_set_active (g->gui.time_orig_conn, TRUE);
+ gtk_toggle_button_set_active (g->gui.seq_orig_isn, TRUE);
+ graph_init_sequence (g);
+}
+
+static void callback_graph_init_on_typechg (GtkWidget *toggle _U_, gpointer data)
+{
+ ((struct graph * )data)->flags ^= GRAPH_INIT_ON_TYPE_CHANGE;
+}
+
+static struct graph *graph_new (void)
+{
+ struct graph *g;
+
+ g = (struct graph * )calloc (1, sizeof (struct graph));
+ graph_element_lists_initialize (g);
+
+ g->x_axis = (struct axis * )calloc (1, sizeof (struct axis));
+ g->y_axis = (struct axis * )calloc (1, sizeof (struct axis));
+ g->x_axis->g = g;
+ g->x_axis->flags = 0;
+ g->x_axis->flags |= AXIS_ORIENTATION;
+ g->x_axis->s.x = g->x_axis->s.y = 0;
+ g->x_axis->s.height = HAXIS_INIT_HEIGHT;
+ g->x_axis->p.x = VAXIS_INIT_WIDTH;
+ g->x_axis->p.height = HAXIS_INIT_HEIGHT;
+ g->y_axis->g = g;
+ g->y_axis->flags = 0;
+ g->y_axis->flags &= ~AXIS_ORIENTATION;
+ g->y_axis->p.x = g->y_axis->p.y = 0;
+ g->y_axis->p.width = VAXIS_INIT_WIDTH;
+ g->y_axis->s.x = 0;
+ g->y_axis->s.y = TITLEBAR_HEIGHT;
+ g->y_axis->s.width = VAXIS_INIT_WIDTH;
+
+ return g;
+}
+
+static void graph_initialize_values (struct graph *g)
+{
+ g->geom.width = g->wp.width = 750;
+ g->geom.height = g->wp.height = 550;
+ g->geom.x = g->wp.x = VAXIS_INIT_WIDTH;
+ g->geom.y = g->wp.y = TITLEBAR_HEIGHT;
+ g->flags = 0;
+ /* g->zoom.x = g->zoom.y = 1.0; */
+ g->zoom.step_x = g->zoom.step_y = 1.2;
+ g->zoom.flags = 0;
+ g->cross.draw = g->cross.erase_needed = 0;
+ g->grab.grabbed = 0;
+ g->magnify.active = 0;
+ g->magnify.offset.x = g->magnify.offset.y = 0;
+ g->magnify.width = g->magnify.height = 250;
+ g->magnify.zoom.x = g->magnify.zoom.y = 10.0;
+ g->magnify.flags = 0;
+}
+
+static void graph_put (struct graph *graph)
+{
+ struct graph *g;
+ if (graphs) {
+ for (g=graphs; g->next; g=g->next);
+ g->next = graph;
+ } else
+ graphs = graph;
+}
+
+static void graph_init_sequence (struct graph *g)
+{
+ debug(DBS_FENTRY) puts ("graph_init_sequence()");
+
+ graph_type_dependent_initialize (g);
+ g->zoom.initial.x = g->zoom.x;
+ g->zoom.initial.y = g->zoom.y;
+ graph_element_lists_make (g);
+ g->x_axis->s.width = g->wp.width;
+ g->x_axis->p.width = g->x_axis->s.width + RMARGIN_WIDTH;
+ g->x_axis->p.y = TITLEBAR_HEIGHT + g->wp.height;
+ g->x_axis->s.height = g->x_axis->p.height = HAXIS_INIT_HEIGHT;
+ g->y_axis->s.height = g->wp.height;
+ g->y_axis->p.height = g->wp.height + TITLEBAR_HEIGHT;
+ graph_pixmaps_create (g);
+ axis_pixmaps_create (g->y_axis);
+ axis_pixmaps_create (g->x_axis);
+ graph_title_pixmap_create (g);
+ graph_title_pixmap_draw (g);
+ graph_title_pixmap_display (g);
+ graph_display (g);
+ axis_display (g->y_axis);
+ axis_display (g->x_axis);
+}
+
+static void graph_type_dependent_initialize (struct graph *g)
+{
+ switch (g->type) {
+ case GRAPH_TSEQ_STEVENS:
+ case GRAPH_TSEQ_TCPTRACE:
+ tseq_stevens_initialize (g);
+ break;
+ case GRAPH_THROUGHPUT:
+ tput_initialize (g);
+ break;
+ case GRAPH_RTT:
+ rtt_initialize (g);
+ break;
+ default:
+ break;
+ }
+}
+
+static void graph_destroy (struct graph *g)
+{
+ struct graph *gtmp;
+ struct graph *p=NULL;
+ /* struct graph *tmp; */
+
+ debug(DBS_FENTRY) puts ("graph_destroy()");
+
+ for (gtmp=graphs; gtmp; p=gtmp, gtmp=gtmp->next)
+ if (gtmp == g)
+ break;
+
+ axis_destroy (g->x_axis);
+ axis_destroy (g->y_axis);
+ /* gtk_widget_destroy (g->drawing_area); */
+ gtk_widget_destroy (g->gui.control_panel);
+ gtk_widget_destroy (g->toplevel);
+ /* gtk_widget_destroy (g->text); */
+ gdk_gc_unref (g->fg_gc);
+ gdk_gc_unref (g->bg_gc);
+ gdk_pixmap_unref (g->pixmap[0]);
+ gdk_pixmap_unref (g->pixmap[1]);
+ free (g->x_axis);
+ free (g->y_axis);
+ free (g->title);
+ graph_segment_list_free (g);
+ graph_element_lists_free (g);
+#if 0
+ for (tmp=graphs; tmp; tmp=tmp->next)
+ printf ("%p next: %p\n", tmp, tmp->next);
+ printf ("p=%p, g=%p, p->next=%p, g->next=%p\n",
+ p, g, p ? p->next : NULL, g->next);
+#endif
+ if (g==graphs)
+ graphs = g->next;
+ else
+ p->next = g->next;
+ free (g);
+#if 0
+ for (tmp=graphs; tmp; tmp=tmp->next)
+ printf ("%p next: %p\n", tmp, tmp->next);
+#endif
+}
+
+/* here we collect all the external data we will ever need */
+static void graph_segment_list_get (struct graph *g)
+{
+ frame_data *ptr;
+ union wtap_pseudo_header pseudo_header;
+ char pd[WTAP_MAX_PACKET_SIZE];
+ struct segment *segment=NULL, *last=NULL;
+ struct segment current;
+ int condition;
+ int err;
+
+ debug(DBS_FENTRY) puts ("graph_segment_list_get()");
+ get_headers (cfile.current_frame, cfile.pd, &current);
+ if (g->type == GRAPH_THROUGHPUT)
+ condition = COMPARE_CURR_DIR;
+ else
+ condition = COMPARE_ANY_DIR;
+
+ for (ptr=cfile.plist; ptr; ptr=ptr->next) {
+ /* XXX - do something with "err" */
+ wtap_seek_read (cfile.wth, ptr->file_off, &pseudo_header,
+ pd, ptr->cap_len, &err);
+ if (!segment)
+ segment = (struct segment * )malloc (sizeof (struct segment));
+ if (!segment)
+ perror ("malloc failed");
+ if (!get_headers (ptr, pd, segment))
+ continue; /* not TCP over IP over Ethernet II */
+ if (compare_headers (&current, segment, condition)) {
+ segment->next = NULL;
+ segment->num = ptr->num;
+ segment->rel_secs = ptr->rel_secs;
+ segment->rel_usecs = ptr->rel_usecs;
+ segment->abs_secs = ptr->abs_secs;
+ segment->abs_usecs = ptr->abs_usecs;
+ segment->data = g_ntohs (segment->iphdr.tot_len) -
+ 4*IHL(&(segment->iphdr)) - 4*DOFF(segment->tcphdr);
+ if (g->segments) {
+ last->next = segment;
+ } else {
+ g->segments = segment;
+ }
+ last = segment;
+ if (ptr==cfile.current_frame)
+ g->current = segment;
+ }
+ segment = NULL;
+ }
+}
+
+static int get_headers (frame_data *fd, char *pd, struct segment *hdrs)
+{
+ struct ether_header *e;
+ struct ppp_header *p;
+ void *ip;
+ void *tcp;
+
+ /*
+ * XXX - on Alpha, even fetching one-byte fields from structures
+ * pointed to by unaligned pointers may be risky, as, unless
+ * the BWX instructions are being used, a one-byte load is done
+ * by loading the word containing the byte and then extracting
+ * the byte.
+ *
+ * This means that the references to "p->ppp_type" and
+ * "((struct iphdr *)ip)->protocol" may turn into a load of
+ * an unaligned word.
+ */
+ switch (fd->lnk_t) {
+
+ case WTAP_ENCAP_ETHERNET:
+ /* It's Ethernet */
+ e = (struct ether_header *)pd;
+ if (pntohs (&e->ether_type) != ETHERTYPE_IP)
+ return FALSE; /* not IP */
+ ip = e + 1;
+ break;
+
+ case WTAP_ENCAP_PPP:
+ /* It's PPP */
+ p = (struct ppp_header *)pd;
+ if (p->ppp_type != PPPTYPE_IP)
+ return FALSE; /* not IP */
+ ip = p + 1;
+ break;
+
+ case WTAP_ENCAP_RAW_IP:
+ /* Raw IP */
+ ip = pd;
+ break;
+
+ default:
+ /* Those are the only encapsulation types we handle */
+ return FALSE;
+ }
+ if (((struct iphdr *)ip)->protocol != IP_PROTO_TCP) {
+ /* printf ("transport protocol not TCP: %#1x\n", ip->protocol); */
+ return FALSE;
+ }
+ tcp = (struct tcphdr *)((guint8 *)ip + 4*IHL((struct iphdr *)ip));
+
+ memcpy(&hdrs->iphdr, ip, sizeof (struct iphdr));
+ memcpy(&hdrs->tcphdr, tcp, sizeof (struct tcphdr));
+ return TRUE;
+}
+
+static int compare_headers (struct segment *h1, struct segment *h2, int dir)
+{
+ if (dir == COMPARE_CURR_DIR)
+ return h1->iphdr.saddr == h2->iphdr.saddr &&
+ h1->iphdr.daddr == h2->iphdr.daddr &&
+ h1->tcphdr.source == h2->tcphdr.source &&
+ h1->tcphdr.dest == h2->tcphdr.dest;
+ else
+ return (h1->iphdr.saddr == h2->iphdr.saddr &&
+ h1->iphdr.daddr == h2->iphdr.daddr &&
+ h1->tcphdr.source == h2->tcphdr.source &&
+ h1->tcphdr.dest == h2->tcphdr.dest) ||
+ (h1->iphdr.saddr == h2->iphdr.daddr &&
+ h1->iphdr.daddr == h2->iphdr.saddr &&
+ h1->tcphdr.source == h2->tcphdr.dest &&
+ h1->tcphdr.dest == h2->tcphdr.source);
+}
+
+static void graph_segment_list_free (struct graph *g)
+{
+ struct segment *segment;
+
+ while (g->segments) {
+ segment = g->segments->next;
+ free (g->segments);
+ g->segments = segment;
+ }
+ g->segments = NULL;
+}
+
+static void graph_element_lists_initialize (struct graph *g)
+{
+ g->elists = (struct element_list *)calloc (1, sizeof (struct element_list));
+}
+
+static void graph_element_lists_make (struct graph *g)
+{
+ debug(DBS_FENTRY) puts ("graph_element_lists_make()");
+
+ switch (g->type) {
+ case GRAPH_TSEQ_STEVENS:
+ tseq_stevens_make_elmtlist (g);
+ break;
+ case GRAPH_TSEQ_TCPTRACE:
+ tseq_tcptrace_make_elmtlist (g);
+ break;
+ case GRAPH_THROUGHPUT:
+ tput_make_elmtlist (g);
+ break;
+ case GRAPH_RTT:
+ rtt_make_elmtlist (g);
+ break;
+ default:
+ printf ("graph_element_lists_make: unknown graph type: %d\n", g->type);
+ break;
+ }
+}
+
+static void graph_element_lists_free (struct graph *g)
+{
+ struct element_list *list, *next_list;
+
+#if 0
+ for (list=g->elists; list; list=list->next)
+ free (list->elements);
+ while (g->elists->next) {
+ list = g->elists->next->next;
+ free (g->elists->next);
+ g->elists->next = list;
+ }
+#endif
+
+ for (list=g->elists; list; list=next_list) {
+ free (list->elements);
+ next_list = list->next;
+ free (list);
+ }
+ g->elists = NULL; /* just to make debugging easier */
+}
+
+static void graph_title_pixmap_create (struct graph *g)
+{
+ if (g->title_pixmap)
+ gdk_pixmap_unref (g->title_pixmap);
+
+ g->title_pixmap = gdk_pixmap_new (g->drawing_area->window,
+ g->x_axis->p.width, g->wp.y, -1);
+}
+
+static void graph_title_pixmap_draw (struct graph *g)
+{
+ int i;
+
+ gdk_draw_rectangle (g->title_pixmap, g->bg_gc, TRUE, 0, 0,
+ g->x_axis->p.width, g->wp.y);
+ for (i=0; g->title[i]; i++) {
+ gint w, h;
+ w = gdk_string_width(gdk_font_from_description(g->font),
+ g->title[i]);
+ h = gdk_string_height(gdk_font_from_description(g->font),
+ g->title[i]);
+ gdk_draw_string (g->title_pixmap,
+ gdk_font_from_description(g->font), g->fg_gc,
+ g->wp.width/2 - w/2, 20+h + i*(h+3),
+ g->title[i]);
+ }
+}
+
+static void graph_title_pixmap_display (struct graph *g)
+{
+ gdk_draw_pixmap (g->drawing_area->window, g->fg_gc, g->title_pixmap,
+ 0, 0, g->wp.x, 0, g->x_axis->p.width, g->wp.y);
+}
+
+static void graph_pixmaps_create (struct graph *g)
+{
+ debug(DBS_FENTRY) puts ("graph_pixmaps_create()");
+
+ if (g->pixmap[0])
+ gdk_pixmap_unref (g->pixmap[0]);
+ if (g->pixmap[1])
+ gdk_pixmap_unref (g->pixmap[1]);
+
+ g->pixmap[0] = gdk_pixmap_new (g->drawing_area->window,
+ g->wp.width, g->wp.height, -1);
+ g->pixmap[1] = gdk_pixmap_new (g->drawing_area->window,
+ g->wp.width, g->wp.height, -1);
+
+ g->displayed = 0;
+}
+
+static void graph_display (struct graph *g)
+{
+ graph_pixmap_draw (g);
+ graph_pixmaps_switch (g);
+ graph_pixmap_display (g);
+}
+
+static void graph_pixmap_display (struct graph *g)
+{
+ gdk_draw_pixmap (g->drawing_area->window, g->fg_gc,
+ g->pixmap[g->displayed], 0, 0, g->wp.x, g->wp.y,
+ g->wp.width, g->wp.height);
+}
+
+static void graph_pixmaps_switch (struct graph *g)
+{
+ g->displayed = 1 ^ g->displayed;
+}
+
+static void graph_pixmap_draw (struct graph *g)
+{
+ struct element_list *list;
+ struct element *e;
+ int not_disp;
+
+ debug(DBS_FENTRY) puts ("graph_display()");
+ not_disp = 1 ^ g->displayed;
+
+ gdk_draw_rectangle (g->pixmap[not_disp], g->bg_gc, TRUE,
+ 0, 0, g->wp.width, g->wp.height);
+
+ for (list=g->elists; list; list=list->next)
+ for (e=list->elements; e->type != ELMT_NONE; e++) {
+ switch (e->type) {
+ case ELMT_RECT:
+ break;
+ case ELMT_LINE:
+ draw_element_line (g, e);
+ break;
+ case ELMT_ARC:
+ draw_element_arc (g, e);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void draw_element_line (struct graph *g, struct element *e)
+{
+ int x1, x2, y1, y2;
+
+ debug(DBS_GRAPH_DRAWING) printf ("line element: (%.2f,%.2f)->(%.2f,%.2f), "
+ "seg %d ... ", e->p.line.dim.x1, e->p.line.dim.y1,
+ e->p.line.dim.x2, e->p.line.dim.y2, e->parent->num);
+ x1 = (int )rint (e->p.line.dim.x1 + g->geom.x - g->wp.x);
+ x2 = (int )rint (e->p.line.dim.x2 + g->geom.x - g->wp.x);
+ y1 = (int )rint ((g->geom.height-1-e->p.line.dim.y1) + g->geom.y-g->wp.y);
+ y2 = (int )rint ((g->geom.height-1-e->p.line.dim.y2) + g->geom.y-g->wp.y);
+ if (x1 > x2) {
+ int tmp=x2;
+ x2=x1;
+ x1=tmp;
+ }
+ if (y1 > y2) {
+ int tmp=y2;
+ y2=y1;
+ y1=tmp;
+ }
+ if ((x1<0 && x2<0) || (x1>=g->wp.width && x2>=g->wp.width) ||
+ (y1<0 && y2<0) || (y1>=g->wp.height && y2>=g->wp.height)) {
+ debug(DBS_GRAPH_DRAWING) printf (" refusing: (%d,%d)->(%d,%d)\n",
+ x1, y1, x2, y2);
+ return;
+ }
+ if (x2 > g->wp.width-1)
+ x2 = g->wp.width-1;
+ if (x1 < 0)
+ x1 = 0;
+ if (y2 > g->wp.height-1)
+ y2 = g->wp.height-1;
+ if (y1 < 0)
+ y1 = 0;
+ debug(DBS_GRAPH_DRAWING) printf ("line: (%d,%d)->(%d,%d)\n", x1, y1, x2,y2);
+ gdk_draw_line (g->pixmap[1^g->displayed], e->gc, x1, y1, x2, y2);
+}
+
+static void draw_element_arc (struct graph *g, struct element *e)
+{
+ int x1, x2, y1, y2;
+
+ x1 = (int )rint (e->p.arc.dim.x + g->geom.x - g->wp.x);
+ x2 = e->p.arc.dim.width;
+ y1 = (int )rint (g->geom.height-1 - e->p.arc.dim.y + g->geom.y - g->wp.y);
+ y2 = e->p.arc.dim.height;
+ if (x1<-x2 || x1>=g->wp.width || y1<-y2 || y1>=g->wp.height)
+ return;
+ debug(DBS_GRAPH_DRAWING) printf ("arc: (%d,%d)->(%d,%d)\n", x1, y1, x2, y2);
+ gdk_draw_arc (g->pixmap[1^g->displayed], e->gc, e->p.arc.filled, x1,
+ y1, x2, y2, e->p.arc.angle1, e->p.arc.angle2);
+}
+
+static void axis_pixmaps_create (struct axis *axis)
+{
+ debug(DBS_FENTRY) puts ("axis_pixmaps_create()");
+ if (axis->pixmap[0])
+ gdk_pixmap_unref (axis->pixmap[0]);
+ if (axis->pixmap[1])
+ gdk_pixmap_unref (axis->pixmap[1]);
+
+ axis->pixmap[0] = gdk_pixmap_new (axis->drawing_area->window,
+ axis->p.width, axis->p.height, -1);
+ axis->pixmap[1] = gdk_pixmap_new (axis->drawing_area->window,
+ axis->p.width, axis->p.height, -1);
+
+ axis->displayed = 0;
+}
+
+static void axis_destroy (struct axis *axis)
+{
+ gdk_pixmap_unref (axis->pixmap[0]);
+ gdk_pixmap_unref (axis->pixmap[1]);
+ free (axis->label);
+}
+
+static void axis_display (struct axis *axis)
+{
+ if (axis->flags & AXIS_ORIENTATION)
+ h_axis_pixmap_draw (axis);
+ else
+ v_axis_pixmap_draw (axis);
+ axis_pixmaps_switch (axis);
+ axis_pixmap_display (axis);
+}
+
+static void v_axis_pixmap_draw (struct axis *axis)
+{
+ struct graph *g = axis->g;
+ int i;
+ double major_tick;
+ int not_disp, rdigits, offset, imin, imax;
+ double bottom, top, j, fl, corr;
+
+ debug(DBS_FENTRY) puts ("v_axis_pixmap_draw()");
+ bottom = (g->geom.height - (g->wp.height + g->wp.y + (-g->geom.y))) /
+ (double )g->geom.height * g->bounds.height;
+ bottom += axis->min;
+ top = (g->geom.height - (g->wp.y + (-g->geom.y))) /
+ (double )g->geom.height * g->bounds.height;
+ top += axis->min;
+ axis_compute_ticks (axis, bottom, top, AXIS_VERTICAL);
+
+ j = axis->major - floor (axis->major);
+ for (rdigits=0; rdigits<=6; rdigits++) {
+ j *= 10;
+ if (j<=0.000001)
+ break;
+ j = j - floor (j);
+ }
+
+ not_disp = 1 ^ axis->displayed;
+ gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0,
+ axis->p.width, axis->p.height);
+ /* axis */
+ gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, axis->p.width - 1,
+ (axis->p.height-axis->s.height)/2.0, axis->s.width - 1,
+ axis->p.height);
+
+ offset = g->wp.y + (-g->geom.y);
+ fl = floor (axis->min / axis->major) * axis->major;
+ corr = rint ((axis->min - fl) * g->zoom.y);
+
+ /* major ticks */
+ major_tick = axis->major * g->zoom.y;
+ imin = (g->geom.height - offset + corr - g->wp.height) / major_tick + 1;
+ imax = (g->geom.height - offset + corr) / major_tick;
+ for (i=imin; i <= imax; i++) {
+ gint w, h;
+ char desc[32];
+ int y = g->geom.height-1 - (int )rint (i * major_tick) -
+ offset + corr + axis->s.y;
+
+ debug(DBS_AXES_DRAWING) printf ("%f @ %d\n", i*axis->major + fl, y);
+ if (y < 0 || y > axis->p.height)
+ continue;
+ gdk_draw_line (axis->pixmap[not_disp], g->fg_gc,
+ axis->s.width - 15, y, axis->s.width - 1, y);
+ snprintf (desc, 32, "%.*f", rdigits, i*axis->major + fl);
+ w = gdk_string_width(gdk_font_from_description(g->font), desc);
+ h = gdk_string_height(gdk_font_from_description(g->font), desc);
+ gdk_draw_string(axis->pixmap[not_disp],
+ gdk_font_from_description(g->font), g->fg_gc,
+ axis->s.width-15-4-w, y + h/2, desc);
+ }
+ /* minor ticks */
+ if (axis->minor) {
+ double minor_tick = axis->minor * g->zoom.y;
+ imin = (g->geom.height - offset + corr - g->wp.height)/minor_tick + 1;
+ imax = (g->geom.height - offset + corr) / minor_tick;
+ for (i=imin; i <= imax; i++) {
+ int y = g->geom.height-1 - (int )rint (i*minor_tick) -
+ offset + corr + axis->s.y;
+
+ debug (DBS_AXES_DRAWING) printf ("%f @ %d\n", i*axis->minor+fl, y);
+ if (y > 0 && y < axis->p.height)
+ gdk_draw_line (axis->pixmap[not_disp], g->fg_gc,
+ axis->s.width - 8, y, axis->s.width - 1, y);
+ }
+ }
+ for (i=0; axis->label[i]; i++) {
+ gint w, h;
+ w = gdk_string_width(gdk_font_from_description(g->font),
+ axis->label[i]);
+ h = gdk_string_height(gdk_font_from_description(g->font),
+ axis->label[i]);
+ gdk_draw_string(axis->pixmap[not_disp],
+ gdk_font_from_description(g->font), g->fg_gc,
+ (axis->p.width - w)/2 ,
+ TITLEBAR_HEIGHT-15 - i*(h+3), axis->label[i]);
+ }
+}
+
+static void h_axis_pixmap_draw (struct axis *axis)
+{
+ struct graph *g = axis->g;
+ int i;
+ double major_tick, minor_tick;
+ int not_disp, rdigits, offset, imin, imax;
+ double left, right, j, fl, corr;
+
+ debug(DBS_FENTRY) puts ("h_axis_pixmap_draw()");
+ left = (g->wp.x-g->geom.x) /
+ (double )g->geom.width * g->bounds.width;
+ left += axis->min;
+ right = (g->wp.x-g->geom.x+g->wp.width) /
+ (double )g->geom.width * g->bounds.width;
+ right += axis->min;
+ axis_compute_ticks (axis, left, right, AXIS_HORIZONTAL);
+
+ j = axis->major - floor (axis->major);
+ for (rdigits=0; rdigits<=6; rdigits++) {
+ j *= 10;
+ if (j<=0.000001)
+ break;
+ j = j - floor (j);
+ }
+
+ not_disp = 1 ^ axis->displayed;
+ gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0,
+ axis->p.width, axis->p.height);
+ /* axis */
+ gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, 0, 0,
+ axis->s.width + (axis->p.width-axis->s.width)/2.0, 0);
+ offset = g->wp.x - g->geom.x;
+
+ fl = floor (axis->min / axis->major) * axis->major;
+ corr = rint ((axis->min - fl) * g->zoom.x);
+
+ /* major ticks */
+ major_tick = axis->major*g->zoom.x;
+ imin = (offset + corr) / major_tick + 1;
+ imax = (offset + corr + axis->s.width) / major_tick;
+ for (i=imin; i <= imax; i++) {
+ char desc[32];
+ int w, h;
+ int x = (int )rint (i * major_tick) - offset - corr;
+
+ /* printf ("%f @ %d\n", i*axis->major + fl, x); */
+ if (x < 0 || x > axis->s.width)
+ continue;
+ gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 15);
+ snprintf (desc, 32, "%.*f", rdigits, i*axis->major + fl);
+ w = gdk_string_width(gdk_font_from_description(g->font), desc);
+ h = gdk_string_height(gdk_font_from_description(g->font), desc);
+ gdk_draw_string(axis->pixmap[not_disp],
+ gdk_font_from_description(g->font), g->fg_gc,
+ x - w/2, 15+h+4, desc);
+ }
+ if (axis->minor > 0) {
+ /* minor ticks */
+ minor_tick = axis->minor*g->zoom.x;
+ imin = (offset + corr) / minor_tick + 1;
+ imax = (offset + corr + g->wp.width) / minor_tick;
+ for (i=imin; i <= imax; i++) {
+ int x = (int )rint (i * minor_tick) - offset - corr;
+ if (x > 0 && x < axis->s.width)
+ gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 8);
+ }
+ }
+ for (i=0; axis->label[i]; i++) {
+ gint w, h;
+ w = gdk_string_width(gdk_font_from_description(g->font),
+ axis->label[i]);
+ h = gdk_string_height(gdk_font_from_description(g->font),
+ axis->label[i]);
+ gdk_draw_string(axis->pixmap[not_disp],
+ gdk_font_from_description(g->font), g->fg_gc,
+ axis->s.width - w - 50, 15+2*h+15 + i*(h+3),
+ axis->label[i]);
+ }
+}
+
+static void axis_pixmaps_switch (struct axis *axis)
+{
+ axis->displayed = 1 ^ axis->displayed;
+}
+
+static void axis_pixmap_display (struct axis *axis)
+{
+ gdk_draw_pixmap (axis->drawing_area->window, axis->g->fg_gc,
+ axis->pixmap[axis->displayed], 0, 0, axis->p.x, axis->p.y,
+ axis->p.width, axis->p.height);
+}
+
+static void axis_compute_ticks (struct axis *axis, double x0, double xmax, int dir)
+{
+ int i, j, ii, jj, ms;
+ double zoom, x, steps[3]={ 0.1, 0.5 };
+ int dim, check_needed, diminished;
+ double majthresh[2]={2.0, 3.0};
+
+ debug((DBS_FENTRY | DBS_AXES_TICKS)) puts ("axis_compute_ticks()");
+ debug(DBS_AXES_TICKS)
+ printf ("x0=%f xmax=%f dir=%s\n", x0,xmax, dir?"VERTICAL":"HORIZONTAL");
+
+ zoom = axis_zoom_get (axis, dir);
+ x = xmax-x0;
+ for (i=-9; i<=12; i++) {
+ if (x / pow (10, i) < 1)
+ break;
+ }
+ --i;
+ ms = (int )(x / pow (10, i));
+
+ if (ms > 5) {
+ j = 0;
+ ++i;
+ } else if (ms > 2)
+ j = 1;
+ else
+ j = 0;
+
+ axis->major = steps[j] * pow (10, i);
+
+ debug(DBS_AXES_TICKS) printf ("zoom=%.1f, x=%f -> i=%d -> ms=%d -> j=%d ->"
+ " axis->major=%f\n", zoom, x, i, ms, j, axis->major);
+
+ /* let's compute minor ticks */
+ jj = j;
+ ii = i;
+ axis_ticks_down (&ii, &jj);
+ axis->minor = steps[jj] * pow (10, ii);
+ /* we don't want minors if they would be less than 10 pixels apart */
+ if (axis->minor*zoom < 10) {
+ debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
+ "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
+ axis->minor = 0;
+ }
+
+ check_needed = TRUE;
+ diminished = FALSE;
+ while (check_needed) {
+ check_needed = FALSE;
+ dim = get_label_dim (axis, dir, xmax);
+ debug(DBS_AXES_TICKS) printf ("axis->major==%.1f, axis->minor==%.1f =>"
+ " axis->major*zoom/dim==%f, axis->minor*zoom/dim==%f\n",
+ axis->major, axis->minor, axis->major*zoom/dim,
+ axis->minor*zoom/dim);
+
+ /* corrections: if majors are less than majthresh[dir] times label
+ * dimension apart, we need to use bigger ones */
+ if (axis->major*zoom / dim < majthresh[dir]) {
+ axis_ticks_up (&ii, &jj);
+ axis->minor = axis->major;
+ axis_ticks_up (&i, &j);
+ axis->major = steps[j] * pow (10, i);
+ check_needed = TRUE;
+ debug(DBS_AXES_TICKS) printf ("axis->major enlarged to %.1f\n",
+ axis->major);
+ }
+ /* if minor ticks are bigger than majthresh[dir] times label dimension,
+ * we could promote them to majors as well */
+ if (axis->minor*zoom / dim > majthresh[dir] && !diminished) {
+ axis_ticks_down (&i, &j);
+ axis->major = axis->minor;
+ axis_ticks_down (&ii, &jj);
+ axis->minor = steps[jj] * pow (10, ii);
+ check_needed = TRUE;
+ diminished = TRUE;
+
+ debug(DBS_AXES_TICKS) printf ("axis->minor diminished to %.1f\n",
+ axis->minor);
+
+ if (axis->minor*zoom < 10) {
+ debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
+ "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
+ axis->minor = 0;
+ }
+ }
+ }
+
+ debug(DBS_AXES_TICKS) printf ("corrected: axis->major == %.1f -> "
+ "axis->minor == %.1f\n", axis->major, axis->minor);
+}
+
+static void axis_ticks_up (int *i, int *j)
+{
+ (*j)++;
+ if (*j>1) {
+ (*i)++;
+ *j=0;
+ }
+}
+
+static void axis_ticks_down (int *i, int *j)
+{
+ (*j)--;
+ if (*j<0) {
+ (*i)--;
+ *j=1;
+ }
+}
+
+static int get_label_dim (struct axis *axis, int dir, double label)
+{
+ double y;
+ char str[32];
+ int rdigits, dim;
+
+ /* First, let's compute how many digits to the right of radix
+ * we need to print */
+ y = axis->major - floor (axis->major);
+ for (rdigits=0; rdigits<=6; rdigits++) {
+ y *= 10;
+ if (y<=0.000001)
+ break;
+ y = y - floor (y);
+ }
+ snprintf (str, 32, "%.*f", rdigits, label);
+ switch (dir) {
+ case AXIS_HORIZONTAL:
+ dim = gdk_string_width(gdk_font_from_description(axis->g->font),
+ str);
+ break;
+ case AXIS_VERTICAL:
+ dim = gdk_string_height(gdk_font_from_description(axis->g->font),
+ str);
+ break;
+ default:
+ puts ("initialize axis: an axis must be either horizontal or vertical");
+ return -1;
+ break;
+ }
+ return dim;
+}
+
+static double axis_zoom_get (struct axis *axis, int dir)
+{
+ switch (dir) {
+ case AXIS_HORIZONTAL:
+ return axis->g->zoom.x;
+ break;
+ case AXIS_VERTICAL:
+ return axis->g->zoom.y;
+ break;
+ default:
+ return -1;
+ break;
+ }
+}
+
+static void graph_select_segment (struct graph *g, int x, int y)
+{
+ struct element_list *list;
+ struct element *e;
+
+ debug(DBS_FENTRY) puts ("graph_select_segment()");
+
+ x -= g->geom.x;
+ y = g->geom.height-1 - (y - g->geom.y);
+
+ for (list=g->elists; list; list=list->next)
+ for (e=list->elements; e->type != ELMT_NONE; e++) {
+ switch (e->type) {
+ case ELMT_RECT:
+ break;
+ case ELMT_LINE:
+ if (line_detect_collision (e, x, y)) {
+ int row = e->parent->num - 1;
+ update_packet_list (row);
+ }
+ break;
+ case ELMT_ARC:
+ if (arc_detect_collision (e, x, y)) {
+ int row = e->parent->num - 1;
+ update_packet_list (row);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static int line_detect_collision (struct element *e, int x, int y)
+{
+ int x1, y1, x2, y2;
+
+ if (e->p.line.dim.x1 < e->p.line.dim.x2) {
+ x1 = (int )rint (e->p.line.dim.x1);
+ x2 = (int )rint (e->p.line.dim.x2);
+ } else {
+ x1 = (int )rint (e->p.line.dim.x2);
+ x2 = (int )rint (e->p.line.dim.x1);
+ }
+ if (e->p.line.dim.y1 < e->p.line.dim.y2) {
+ y1 = (int )rint (e->p.line.dim.y1);
+ y2 = (int )rint (e->p.line.dim.y2);
+ } else {
+ y1 = (int )rint (e->p.line.dim.y2);
+ y2 = (int )rint (e->p.line.dim.y1);
+ }
+ /*
+ printf ("line: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", x1, y1, x2, y2, x, y);
+ */
+ if ((x1==x && x2==x && y1<=y && y<=y2)||(y1==y && y2==y && x1<=x && x<=x2))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static int arc_detect_collision (struct element *e, int x, int y)
+{
+ int x1, y1, x2, y2;
+
+ x1 = (int )rint (e->p.arc.dim.x);
+ x2 = (int )rint (e->p.arc.dim.x + e->p.arc.dim.width);
+ y1 = (int )rint (e->p.arc.dim.y - e->p.arc.dim.height);
+ y2 = (int )rint (e->p.arc.dim.y);
+ /*
+ printf ("arc: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", x1, y1, x2, y2, x, y);
+ */
+ if (x1<=x && x<=x2 && y1<=y && y<=y2)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static void update_packet_list (int row)
+{
+ select_packet (&cfile, row);
+ if (gtk_clist_row_is_visible(GTK_CLIST(packet_list), row) !=
+ GTK_VISIBILITY_FULL)
+ gtk_clist_moveto(GTK_CLIST(packet_list), row, -1, 0.5, 0.5);
+ GTK_CLIST(packet_list)->focus_row = row;
+ gtk_clist_select_row(GTK_CLIST(packet_list), row, -1);
+}
+
+static void cross_xor (struct graph *g, int x, int y)
+{
+ if (x > g->wp.x && x < g->wp.x+g->wp.width &&
+ y >= g->wp.y && y < g->wp.y+g->wp.height) {
+ gdk_draw_line (g->drawing_area->window, xor_gc, g->wp.x,
+ y, g->wp.x + g->wp.width, y);
+ gdk_draw_line (g->drawing_area->window, xor_gc, x,
+ g->wp.y, x, g->wp.y + g->wp.height);
+ }
+}
+
+static void cross_draw (struct graph *g, int x, int y)
+{
+ cross_xor (g, x, y);
+ g->cross.x = x;
+ g->cross.y = y;
+ g->cross.erase_needed = 1;
+}
+
+static void cross_erase (struct graph *g)
+{
+ cross_xor (g, g->cross.x, g->cross.y);
+ g->cross.erase_needed = 0;
+}
+
+static void magnify_create (struct graph *g, int x, int y)
+{
+ struct graph *mg;
+ struct element_list *list, *new_list;
+ struct ipoint pos, offsetpos;
+ GdkEvent *e=NULL;
+
+ mg = g->magnify.g = (struct graph * )malloc (sizeof (struct graph));
+ memcpy ((void * )mg, (void * )g, sizeof (struct graph));
+
+ mg->toplevel = gtk_window_new (GTK_WINDOW_POPUP);
+ g_signal_connect(G_OBJECT(mg->toplevel), "realize",
+ G_CALLBACK(window_icon_realize_cb), NULL);
+ mg->drawing_area = mg->toplevel;
+ gtk_widget_set_size_request(mg->toplevel, g->magnify.width,
+ g->magnify.height);
+ gtk_widget_set_events (mg->drawing_area, GDK_EXPOSURE_MASK
+ /* | GDK_ENTER_NOTIFY_MASK */
+ /* | GDK_ALL_EVENTS_MASK */
+ );
+
+ mg->wp.x = 0;
+ mg->wp.y = 0;
+ mg->wp.width = g->magnify.width;
+ mg->wp.height = g->magnify.height;
+ mg->geom.width = (int )rint (g->geom.width * g->magnify.zoom.x);
+ mg->geom.height = (int )rint (g->geom.height * g->magnify.zoom.y);
+ mg->zoom.x = (mg->geom.width - 1) / g->bounds.width;
+ mg->zoom.y = (mg->geom.height- 1) / g->bounds.height;
+
+ /* in order to keep original element lists intact we need our own */
+ graph_element_lists_initialize (mg);
+ list = g->elists->next;
+ new_list = mg->elists;
+ for ( ; list; list=list->next) {
+ new_list->next =
+ (struct element_list * )malloc (sizeof (struct element_list));
+ new_list = new_list->next;
+ new_list->next = NULL;
+ new_list->elements = NULL;
+ }
+ graph_element_lists_make (mg);
+
+ gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &pos.x, &pos.y);
+ g->magnify.x = pos.x + x - g->magnify.width/2;
+ g->magnify.y = pos.y + y - g->magnify.height/2;
+ offsetpos.x = g->magnify.x + g->magnify.offset.x;
+ offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
+ offsetpos.y = g->magnify.y + g->magnify.offset.y;
+ offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
+ gtk_widget_set_uposition (mg->drawing_area, offsetpos.x, offsetpos.y);
+ magnify_get_geom (g, x, y);
+
+ gtk_widget_show (mg->drawing_area);
+
+ /* we need to wait for the first expose event before we start drawing */
+ while (!gdk_events_pending ());
+ do {
+ e = gdk_event_get ();
+ if (e) {
+ if (e->any.type == GDK_EXPOSE) {
+ gdk_event_free (e);
+ break;
+ }
+ gdk_event_free (e);
+ }
+ } while (e);
+
+ mg->pixmap[0] = mg->pixmap[1] = NULL;
+ graph_pixmaps_create (mg);
+ magnify_draw (g);
+ g->magnify.active = 1;
+}
+
+static void magnify_move (struct graph *g, int x, int y)
+{
+ struct ipoint pos, offsetpos;
+
+ gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &pos.x, &pos.y);
+ g->magnify.x = pos.x + x - g->magnify.width/2;
+ g->magnify.y = pos.y + y - g->magnify.height/2;
+ offsetpos.x = g->magnify.x + g->magnify.offset.x;
+ offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
+ offsetpos.y = g->magnify.y + g->magnify.offset.y;
+ offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
+ magnify_get_geom (g, x, y);
+ gtk_widget_set_uposition (g->magnify.g->drawing_area, offsetpos.x,
+ offsetpos.y);
+ magnify_draw (g);
+}
+
+static void magnify_destroy (struct graph *g)
+{
+ struct element_list *list;
+ struct graph *mg = g->magnify.g;
+
+ gtk_widget_destroy (GTK_WIDGET (mg->drawing_area));
+ gdk_pixmap_unref (mg->pixmap[0]);
+ gdk_pixmap_unref (mg->pixmap[1]);
+ for (list=mg->elists; list; list=list->next)
+ free (list->elements);
+ while (mg->elists->next) {
+ list = mg->elists->next->next;
+ free (mg->elists->next);
+ mg->elists->next = list;
+ }
+ free (g->magnify.g);
+ g->magnify.active = 0;
+}
+
+static void magnify_get_geom (struct graph *g, int x, int y)
+{
+ int posx, posy;
+
+ gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &posx, &posy);
+
+ g->magnify.g->geom.x = g->geom.x;
+ g->magnify.g->geom.y = g->geom.y;
+
+ g->magnify.g->geom.x -=
+ (int )rint ((g->magnify.g->geom.width - g->geom.width) *
+ ((x-g->geom.x)/(double )g->geom.width));
+ g->magnify.g->geom.y -=
+ (int )rint ((g->magnify.g->geom.height - g->geom.height) *
+ ((y-g->geom.y)/(double )g->geom.height));
+
+ /* we have coords of origin of graph relative to origin of g->toplevel.
+ * now we need them to relate to origin of magnify window */
+ g->magnify.g->geom.x -= (g->magnify.x - posx);
+ g->magnify.g->geom.y -= (g->magnify.y - posy);
+}
+
+static void magnify_draw (struct graph *g)
+{
+ int not_disp = 1 ^ g->magnify.g->displayed;
+
+ graph_pixmap_draw (g->magnify.g);
+ /* graph pixmap is almost ready, just add border */
+ gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0,
+ g->magnify.width - 1, 0);
+ gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc,
+ g->magnify.width - 1, 0, g->magnify.width - 1, g->magnify.height);
+ gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0,
+ 0, g->magnify.height - 1);
+ gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0,
+ g->magnify.height - 1, g->magnify.width - 1, g->magnify.height - 1);
+
+ graph_pixmaps_switch (g->magnify.g);
+ graph_pixmap_display (g->magnify.g);
+
+}
+
+static gint configure_event (GtkWidget *widget, GdkEventConfigure *event)
+{
+ struct graph *g;
+ struct {
+ double x, y;
+ } zoom;
+ int cur_g_width, cur_g_height;
+ int cur_wp_width, cur_wp_height;
+
+ debug(DBS_FENTRY) puts ("configure_event()");
+
+ for (g=graphs; g; g=g->next)
+ if (g->drawing_area == widget)
+ break;
+
+ cur_wp_width = g->wp.width;
+ cur_wp_height = g->wp.height;
+ g->wp.width = event->width - g->y_axis->p.width - RMARGIN_WIDTH;
+ g->wp.height = event->height - g->x_axis->p.height - g->wp.y;
+ g->x_axis->s.width = g->wp.width;
+ g->x_axis->p.width = g->wp.width + RMARGIN_WIDTH;
+ g->y_axis->p.height = g->wp.height + g->wp.y;
+ g->y_axis->s.height = g->wp.height;
+ g->x_axis->p.y = g->y_axis->p.height;
+ zoom.x = (double )g->wp.width / cur_wp_width;
+ zoom.y = (double )g->wp.height / cur_wp_height;
+ cur_g_width = g->geom.width;
+ cur_g_height = g->geom.height;
+ g->geom.width = (int )rint (g->geom.width * zoom.x);
+ g->geom.height = (int )rint (g->geom.height * zoom.y);
+ g->zoom.x = (double )(g->geom.width - 1) / g->bounds.width;
+ g->zoom.y = (double )(g->geom.height -1) / g->bounds.height;
+ /* g->zoom.initial.x = g->zoom.x; */
+ /* g->zoom.initial.y = g->zoom.y; */
+
+ g->geom.x = g->wp.x - (double )g->geom.width/cur_g_width *
+ (g->wp.x - g->geom.x);
+ g->geom.y = g->wp.y - (double )g->geom.height/cur_g_height *
+ (g->wp.y - g->geom.y);
+#if 0
+ printf ("configure: graph: (%d,%d), (%d,%d); viewport: (%d,%d), (%d,%d); "
+ "zooms: (%f,%f)\n", g->geom.x, g->geom.y, g->geom.width,
+ g->geom.height, g->wp.x, g->wp.y, g->wp.width, g->wp.height,
+ g->zoom.x, g->zoom.y);
+#endif
+
+ update_zoom_spins (g);
+ graph_element_lists_make (g);
+ graph_pixmaps_create (g);
+ graph_title_pixmap_create (g);
+ axis_pixmaps_create (g->y_axis);
+ axis_pixmaps_create (g->x_axis);
+ /* we don't do actual drawing here; we leave it to expose handler */
+ graph_pixmap_draw (g);
+ graph_pixmaps_switch (g);
+ graph_title_pixmap_draw (g);
+ h_axis_pixmap_draw (g->x_axis);
+ axis_pixmaps_switch (g->x_axis);
+ v_axis_pixmap_draw (g->y_axis);
+ axis_pixmaps_switch (g->y_axis);
+ return TRUE;
+}
+
+static gint expose_event (GtkWidget *widget, GdkEventExpose *event)
+{
+ struct graph *g;
+
+ debug(DBS_FENTRY) puts ("expose_event()");
+
+ if (event->count)
+ return TRUE;
+
+ for (g=graphs; g; g=g->next)
+ if (g->drawing_area == widget)
+ break;
+
+ /* lower left corner */
+ gdk_draw_rectangle (g->drawing_area->window, g->bg_gc, TRUE, 0,
+ g->wp.y + g->wp.height, g->y_axis->p.width, g->x_axis->p.height);
+ /* right margin */
+ gdk_draw_rectangle (g->drawing_area->window, g->bg_gc, TRUE,
+ g->wp.x + g->wp.width, g->wp.y, RMARGIN_WIDTH, g->wp.height);
+
+ graph_pixmap_display (g);
+ graph_title_pixmap_display (g);
+ axis_pixmap_display (g->x_axis);
+ axis_pixmap_display (g->y_axis);
+
+ return TRUE;
+}
+
+static gint button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+ struct graph *g;
+
+ debug(DBS_FENTRY) puts ("button_press_event()");
+
+ for (g=graphs; g; g=g->next)
+ if (g->drawing_area == widget)
+ break;
+
+ if (event->button == 3) {
+ if (event->state & GDK_CONTROL_MASK)
+ magnify_create (g, (int )rint (event->x), (int )rint (event->y));
+ else {
+ g->grab.x = (int )rint (event->x) - g->geom.x;
+ g->grab.y = (int )rint (event->y) - g->geom.y;
+ g->grab.grabbed = TRUE;
+ }
+#ifdef WIN32
+ /* Windows mouse control: */
+ /* [<ctrl>-left] - select packet */
+ /* [left] - zoom in */
+ /* [<shift>-left] - zoom out */
+ } else if (event->button == 1) {
+ if (event->state & GDK_CONTROL_MASK) {
+ graph_select_segment (g, (int)event->x, (int)event->y);
+ } else {
+#else /* WIN32 */
+ } else if (event->button == 2) {
+#endif
+ int cur_width = g->geom.width, cur_height = g->geom.height;
+ struct { double x, y; } factor;
+
+ if (g->zoom.flags & ZOOM_OUT) {
+ if (g->zoom.flags & ZOOM_HLOCK)
+ factor.x = 1.0;
+ else
+ factor.x = 1 / g->zoom.step_x;
+ if (g->zoom.flags & ZOOM_VLOCK)
+ factor.y = 1.0;
+ else
+ factor.y = 1 / g->zoom.step_y;
+ } else {
+ if (g->zoom.flags & ZOOM_HLOCK)
+ factor.x = 1.0;
+ else
+ factor.x = g->zoom.step_x;
+ if (g->zoom.flags & ZOOM_VLOCK)
+ factor.y = 1.0;
+ else
+ factor.y = g->zoom.step_y;
+ }
+
+ g->geom.width = (int )rint (g->geom.width * factor.x);
+ g->geom.height = (int )rint (g->geom.height * factor.y);
+ if (g->geom.width < g->wp.width)
+ g->geom.width = g->wp.width;
+ if (g->geom.height < g->wp.height)
+ g->geom.height = g->wp.height;
+ g->zoom.x = (g->geom.width - 1) / g->bounds.width;
+ g->zoom.y = (g->geom.height- 1) / g->bounds.height;
+
+ g->geom.x -= (int )rint ((g->geom.width - cur_width) *
+ ((event->x-g->geom.x)/(double )cur_width));
+ g->geom.y -= (int )rint ((g->geom.height - cur_height) *
+ ((event->y-g->geom.y)/(double )cur_height));
+
+ if (g->geom.x > g->wp.x)
+ g->geom.x = g->wp.x;
+ if (g->geom.y > g->wp.y)
+ g->geom.y = g->wp.y;
+ if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
+ g->geom.x = g->wp.width + g->wp.x - g->geom.width;
+ if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
+ g->geom.y = g->wp.height + g->wp.y - g->geom.height;
+#if 0
+ printf ("button press: graph: (%d,%d), (%d,%d); viewport: (%d,%d), "
+ "(%d,%d); zooms: (%f,%f)\n", g->geom.x, g->geom.y,
+ g->geom.width, g->geom.height, g->wp.x, g->wp.y, g->wp.width,
+ g->wp.height, g->zoom.x, g->zoom.y);
+#endif
+ graph_element_lists_make (g);
+ graph_display (g);
+ axis_display (g->y_axis);
+ axis_display (g->x_axis);
+ update_zoom_spins (g);
+ if (g->cross.draw)
+ cross_draw (g, event->x, event->y);
+#ifndef WIN32
+ } else if (event->button == 1) {
+ graph_select_segment (g, (int )event->x, (int )event->y);
+#else /* WIN32 */
+ }
+#endif
+ }
+ return TRUE;
+}
+
+static gint motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
+{
+ struct graph *g;
+ int x, y;
+ GdkModifierType state;
+
+ /* debug(DBS_FENTRY) puts ("motion_notify_event()"); */
+
+ for (g=graphs; g; g=g->next)
+ if (g->drawing_area == widget)
+ break;
+
+ if (event->is_hint)
+ gdk_window_get_pointer (event->window, &x, &y, &state);
+ else {
+ x = event->x;
+ y = event->y;
+ state = event->state;
+ }
+
+ /* Testing just (state & GDK_BUTTON1_MASK) is not enough since when button1
+ * is pressed while pointer is in motion, we will receive one more motion
+ * notify *before* we get the button press. This last motion notify works
+ * with stale grab coordinates */
+ if (state & GDK_BUTTON3_MASK) {
+ if (g->grab.grabbed) {
+ g->geom.x = x-g->grab.x;
+ g->geom.y = y-g->grab.y;
+
+ if (g->geom.x > g->wp.x)
+ g->geom.x = g->wp.x;
+ if (g->geom.y > g->wp.y)
+ g->geom.y = g->wp.y;
+ if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
+ g->geom.x = g->wp.width + g->wp.x - g->geom.width;
+ if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
+ g->geom.y = g->wp.height + g->wp.y - g->geom.height;
+ graph_display (g);
+ axis_display (g->y_axis);
+ axis_display (g->x_axis);
+ if (g->cross.draw)
+ cross_draw (g, x, y);
+ } else if (g->magnify.active)
+ magnify_move (g, x, y);
+ } else if (state & GDK_BUTTON1_MASK) {
+ graph_select_segment (g, x, y);
+ if (g->cross.erase_needed)
+ cross_erase (g);
+ if (g->cross.draw)
+ cross_draw (g, x, y);
+ } else {
+ if (g->cross.erase_needed)
+ cross_erase (g);
+ if (g->cross.draw)
+ cross_draw (g, x, y);
+ }
+
+ return TRUE;
+}
+
+static gint button_release_event (GtkWidget *widget, GdkEventButton *event)
+{
+ struct graph *g;
+
+ debug(DBS_FENTRY) puts ("button_release_event()");
+
+ for (g=graphs; g; g=g->next)
+ if (g->drawing_area == widget)
+ break;
+
+ if (event->button == 3)
+ g->grab.grabbed = FALSE;
+
+ if (g->magnify.active)
+ magnify_destroy (g);
+ return TRUE;
+}
+
+static gint key_press_event (GtkWidget *widget, GdkEventKey *event)
+{
+ struct graph *g;
+
+ debug(DBS_FENTRY) puts ("key_press_event()");
+
+ for (g=graphs; g; g=g->next)
+ if (g->toplevel == widget)
+ break;
+
+ if (event->keyval == 32 /*space*/) {
+ g->cross.draw ^= 1;
+#if 0
+ if (g->cross.draw) {
+ int x, y;
+ gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
+ cross_draw (g);
+ } else if (g->cross.erase_needed) {
+ cross_erase (g);
+ }
+#endif
+ /* toggle buttons emit their "toggled" signals so don't bother doing
+ * any real work here, it will be done in signal handlers */
+ if (g->cross.draw)
+ gtk_toggle_button_set_active (g->cross.on_toggle, TRUE);
+ else
+ gtk_toggle_button_set_active (g->cross.off_toggle, TRUE);
+ } else if (event->keyval == 't')
+ toggle_time_origin (g);
+ else if (event->keyval == 's')
+ toggle_seq_origin (g);
+ else if (event->keyval == GDK_Shift_L) {
+ /* g->zoom.flags |= ZOOM_OUT; */
+ gtk_toggle_button_set_active (g->zoom.widget.out_toggle, TRUE);
+ }
+ return TRUE;
+}
+
+static gint key_release_event (GtkWidget *widget, GdkEventKey *event)
+{
+ struct graph *g;
+
+ debug(DBS_FENTRY) puts ("key_release_event()");
+
+ for (g=graphs; g; g=g->next)
+ if (g->toplevel == widget)
+ break;
+
+ if (event->keyval == GDK_Shift_L || event->keyval == GDK_ISO_Prev_Group) {
+ /* g->zoom.flags &= ~ZOOM_OUT; */
+ gtk_toggle_button_set_active (g->zoom.widget.in_toggle, TRUE);
+ }
+ return TRUE;
+}
+
+static gint leave_notify_event (GtkWidget *widget, GdkEventCrossing *event _U_)
+{
+ struct graph *g;
+
+ for (g=graphs; g; g=g->next)
+ if (g->drawing_area == widget)
+ break;
+
+ if (g->cross.erase_needed)
+ cross_erase (g);
+
+ return TRUE;
+}
+
+static gint enter_notify_event (GtkWidget *widget, GdkEventCrossing *event _U_)
+{
+ struct graph *g;
+
+ for (g=graphs; g; g=g->next)
+ if (g->drawing_area == widget)
+ break;
+
+ /* graph_pixmap_display (g); */
+ if (g->cross.draw) {
+ int x, y;
+ gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
+ cross_draw (g, x, y);
+ }
+ return TRUE;
+}
+
+static void toggle_time_origin (struct graph *g)
+{
+ switch (g->type) {
+ case GRAPH_TSEQ_STEVENS:
+ tseq_stevens_toggle_time_origin (g);
+ break;
+ case GRAPH_TSEQ_TCPTRACE:
+ tseq_tcptrace_toggle_time_origin (g);
+ break;
+ case GRAPH_THROUGHPUT:
+ tput_toggle_time_origin (g);
+ break;
+ default:
+ break;
+ }
+ axis_display (g->x_axis);
+}
+
+static void toggle_seq_origin (struct graph *g)
+{
+ switch (g->type) {
+ case GRAPH_TSEQ_STEVENS:
+ tseq_stevens_toggle_seq_origin (g);
+ axis_display (g->y_axis);
+ break;
+ case GRAPH_TSEQ_TCPTRACE:
+ tseq_tcptrace_toggle_seq_origin (g);
+ axis_display (g->y_axis);
+ break;
+ case GRAPH_RTT:
+ rtt_toggle_seq_origin (g);
+ axis_display (g->x_axis);
+ break;
+ default:
+ break;
+ }
+}
+
+static int get_num_dsegs (struct graph *g)
+{
+ int count;
+ struct segment *tmp;
+
+ for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
+ if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
+ count++;
+ }
+ }
+ return count;
+}
+
+static int get_num_acks (struct graph *g)
+{
+ int count;
+ struct segment *tmp;
+
+ for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
+ if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
+ count++;
+ }
+ }
+ return count;
+}
+
+/*
+ * Stevens-style time-sequence grapH
+ */
+
+static void tseq_stevens_read_config (struct graph *g)
+{
+ debug(DBS_FENTRY) puts ("tseq_stevens_read_config()");
+
+ g->s.tseq_stevens.seq_width = 4;
+ g->s.tseq_stevens.seq_height = 4;
+ g->s.tseq_stevens.flags = 0;
+
+ g->title = (char ** )malloc (2 * sizeof (char *));
+ g->title[0] = "Time/Sequence Graph";
+ g->title[1] = NULL;
+ g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
+ g->y_axis->label[0] = "number[B]";
+ g->y_axis->label[1] = "Sequence";
+ g->y_axis->label[2] = NULL;
+ g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
+ g->x_axis->label[0] = "Time[s]";
+ g->x_axis->label[1] = NULL;
+}
+
+static void tseq_stevens_initialize (struct graph *g)
+{
+ debug(DBS_FENTRY) puts ("tseq_stevens_initialize()");
+ tseq_stevens_get_bounds (g);
+
+ g->x_axis->min = 0;
+ g->y_axis->min = 0;
+
+ switch (g->type) {
+ case GRAPH_TSEQ_STEVENS:
+ tseq_stevens_read_config(g);
+ break;
+ case GRAPH_TSEQ_TCPTRACE:
+ tseq_tcptrace_read_config(g);
+ break;
+ }
+}
+
+static void tseq_stevens_get_bounds (struct graph *g)
+{
+ struct segment *tmp, *last, *first;
+ double t0, tmax, y0, ymax;
+
+ for (first=g->segments; first->next; first=first->next) {
+ if (compare_headers (g->current, first, COMPARE_CURR_DIR))
+ break;
+ }
+ last = NULL;
+ ymax = 0;
+ for (tmp=g->segments; tmp; tmp=tmp->next) {
+ unsigned int highest_byte_num;
+ last = tmp;
+ if (compare_headers (g->current, tmp, COMPARE_CURR_DIR))
+ highest_byte_num = g_ntohl (tmp->tcphdr.seq) + tmp->data;
+ else
+ highest_byte_num = g_ntohl (tmp->tcphdr.ack_seq);
+ if (highest_byte_num > ymax)
+ ymax = highest_byte_num;
+ }
+ if (!last) {
+ puts ("tseq_stevens_get_bounds: segment list corrupted!");
+ return;
+ }
+
+ t0 = g->segments->rel_secs + g->segments->rel_usecs / 1000000.0;
+ tmax = last->rel_secs + last->rel_usecs / 1000000.0;
+ y0 = g_ntohl (first->tcphdr.seq);
+
+ g->bounds.x0 = t0;
+ g->bounds.y0 = y0;
+ g->bounds.width = tmax - t0;
+ g->bounds.height = ymax - y0;
+ g->zoom.x = (g->geom.width - 1) / g->bounds.width;
+ g->zoom.y = (g->geom.height -1) / g->bounds.height;
+}
+
+static void tseq_stevens_make_elmtlist (struct graph *g)
+{
+ struct segment *tmp;
+ struct element *elements, *e;
+ double x0 = g->bounds.x0, y0 = g->bounds.y0;
+
+ debug(DBS_FENTRY) puts ("tseq_stevens_make_elmtlist()");
+ if (g->elists->elements == NULL) {
+ int n = 1 + get_num_dsegs (g);
+ e = elements = (struct element * )malloc (n*sizeof (struct element));
+ } else
+ e = elements = g->elists->elements;
+
+ for (tmp=g->segments; tmp; tmp=tmp->next) {
+ double secs, seqno;
+
+ if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR))
+ continue;
+
+ secs = g->zoom.x * (tmp->rel_secs + tmp->rel_usecs / 1000000.0 - x0);
+ seqno = g->zoom.y * (g_ntohl (tmp->tcphdr.seq) - y0);
+
+ e->type = ELMT_ARC;
+ e->parent = tmp;
+ e->gc = g->fg_gc;
+ e->p.arc.dim.width = g->s.tseq_stevens.seq_width;
+ e->p.arc.dim.height = g->s.tseq_stevens.seq_height;
+ e->p.arc.dim.x = secs - g->s.tseq_stevens.seq_width/2.0;
+ e->p.arc.dim.y = seqno + g->s.tseq_stevens.seq_height/2.0;
+ e->p.arc.filled = TRUE;
+ e->p.arc.angle1 = 0;
+ e->p.arc.angle2 = 23040;
+ e++;
+ }
+ e->type = ELMT_NONE;
+ g->elists->elements = elements;
+}
+
+static void tseq_stevens_toggle_seq_origin (struct graph *g)
+{
+ g->s.tseq_stevens.flags ^= SEQ_ORIGIN;
+
+ if ((g->s.tseq_stevens.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
+ g->y_axis->min = g->bounds.y0;
+ else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
+ g->y_axis->min = 0;
+}
+
+static void tseq_stevens_toggle_time_origin (struct graph *g)
+{
+ g->s.tseq_stevens.flags ^= TIME_ORIGIN;
+
+ if ((g->s.tseq_stevens.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
+ g->x_axis->min = g->bounds.x0;
+ else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
+ g->x_axis->min = 0;
+}
+
+/*
+ * tcptrace-style time-sequence graph
+ */
+
+static void tseq_tcptrace_read_config (struct graph *g)
+{
+ GdkColormap *colormap;
+ GdkColor color;
+
+ g->s.tseq_tcptrace.flags = 0;
+ g->s.tseq_tcptrace.gc_seq = gdk_gc_new (g->drawing_area->window);
+ g->s.tseq_tcptrace.gc_ack[0] = gdk_gc_new (g->drawing_area->window);
+ g->s.tseq_tcptrace.gc_ack[1] = gdk_gc_new (g->drawing_area->window);
+ colormap = gdk_window_get_colormap (g->drawing_area->window);
+ gdk_color_parse ("black", &color);
+ gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
+ gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_seq, &color);
+ gdk_color_parse ("LightSlateGray", &color);
+ gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
+ gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[0], &color);
+ gdk_color_parse ("LightGray", &color);
+ gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
+ gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[1], &color);
+
+ g->elists->next = (struct element_list * )
+ malloc (sizeof (struct element_list));
+ g->elists->next->next = NULL;
+ g->elists->next->elements = NULL;
+
+ g->title = (char ** )malloc (2 * sizeof (char *));
+ g->title[0] = "Time/Sequence Graph";
+ g->title[1] = NULL;
+ g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
+ g->y_axis->label[0] = "number[B]";
+ g->y_axis->label[1] = "Sequence";
+ g->y_axis->label[2] = NULL;
+ g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
+ g->x_axis->label[0] = "Time[s]";
+ g->x_axis->label[1] = NULL;
+}
+
+static void tseq_tcptrace_make_elmtlist (struct graph *g)
+{
+ struct segment *tmp;
+ struct element *elements0, *e0; /* list of elmts with prio 0 */
+ struct element *elements1, *e1; /* list of elmts with prio 1 */
+ double x0, y0;
+ double p_t; /* ackno, window and time of previous segment */
+ double p_ackno, p_win;
+ int toggle=0;
+
+ debug(DBS_FENTRY) puts ("tseq_tcptrace_make_elmtlist()");
+
+ if (g->elists->elements == NULL) {
+ int n = 1 + 4*get_num_acks(g);
+ e0 = elements0 = (struct element * )malloc (n*sizeof (struct element));
+ } else
+ e0 = elements0 = g->elists->elements;
+
+ if (g->elists->next->elements == NULL ) {
+ int n = 1 + 3*get_num_dsegs(g);
+ e1 = elements1 = (struct element * )malloc (n*sizeof (struct element));
+ } else
+ e1 = elements1 = g->elists->next->elements;
+
+ x0 = g->bounds.x0;
+ y0 = g->bounds.y0;
+ /* initialize "previous" values */
+ for (tmp=g->segments; tmp; tmp=tmp->next)
+ if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR))
+ break;
+ /*
+ p_ackno = (unsigned int )(g->zoom.y * (g_ntohl (tmp->tcphdr.ack_seq) - y0));
+ */
+ p_ackno = 0;
+ p_win = g->zoom.y * g_ntohs (tmp->tcphdr.window);
+ p_t = g->segments->rel_secs + g->segments->rel_usecs/1000000.0 - x0;
+ for (tmp=g->segments; tmp; tmp=tmp->next) {
+ double secs, seqno, data;
+ double x;
+
+ secs = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+ x = secs - x0;
+ x *= g->zoom.x;
+ if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
+ /* forward direction -> we need seqno and amount of data */
+ double y1, y2;
+
+ seqno = g_ntohl (tmp->tcphdr.seq);
+ if (TCP_SYN (tmp->tcphdr))
+ data = 1;
+ else
+ data = tmp->data;
+
+ y1 = g->zoom.y * (seqno - y0);
+ y2 = g->zoom.y * (seqno - y0 + data);
+ e1->type = ELMT_LINE;
+ e1->parent = tmp;
+ e1->gc = g->s.tseq_tcptrace.gc_seq;
+ e1->p.line.dim.x1 = e1->p.line.dim.x2 = x;
+ e1->p.line.dim.y1 = y1;
+ e1->p.line.dim.y2 = y2;
+ e1++;
+ e1->type = ELMT_LINE;
+ e1->parent = tmp;
+ e1->gc = g->s.tseq_tcptrace.gc_seq;
+ e1->p.line.dim.x1 = x - 1;
+ e1->p.line.dim.x2 = x + 1;
+ e1->p.line.dim.y1 = e1->p.line.dim.y2 = y1;
+ e1++;
+ e1->type = ELMT_LINE;
+ e1->parent = tmp;
+ e1->gc = g->s.tseq_tcptrace.gc_seq;
+ e1->p.line.dim.x1 = x + 1;
+ e1->p.line.dim.x2 = x - 1;
+ e1->p.line.dim.y1 = e1->p.line.dim.y2 = y2;
+ e1++;
+ } else {
+ double ackno, win;
+ if (TCP_SYN (tmp->tcphdr) && ! TCP_ACK (tmp->tcphdr))
+ /* SYN's have ACK==0 and are useless here */
+ continue;
+ /* backward direction -> we need ackno and window */
+ ackno = (g_ntohl (tmp->tcphdr.ack_seq) - y0) * g->zoom.y;
+ win = g_ntohs (tmp->tcphdr.window) * g->zoom.y;
+
+ /* ack line */
+ e0->type = ELMT_LINE;
+ e0->parent = tmp;
+ e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
+ e0->p.line.dim.x1 = p_t;
+ e0->p.line.dim.y1 = p_ackno;
+ e0->p.line.dim.x2 = x;
+ e0->p.line.dim.y2 = p_ackno;
+ e0++;
+ e0->type = ELMT_LINE;
+ e0->parent = tmp;
+ e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
+ e0->p.line.dim.x1 = x;
+ e0->p.line.dim.y1 = p_ackno;
+ e0->p.line.dim.x2 = x;
+ e0->p.line.dim.y2 = ackno!=p_ackno || ackno<4 ? ackno : ackno-4;
+ e0++;
+ /* window line */
+ e0->type = ELMT_LINE;
+ e0->parent = tmp;
+ e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
+ e0->p.line.dim.x1 = p_t;
+ e0->p.line.dim.y1 = p_win + p_ackno;
+ e0->p.line.dim.x2 = x;
+ e0->p.line.dim.y2 = p_win + p_ackno;
+ e0++;
+ e0->type = ELMT_LINE;
+ e0->parent = tmp;
+ e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
+ e0->p.line.dim.x1 = x;
+ e0->p.line.dim.y1 = p_win + p_ackno;
+ e0->p.line.dim.x2 = x;
+ e0->p.line.dim.y2 = win + ackno;
+ e0++;
+ p_ackno = ackno;
+ p_win = win;
+ p_t = x;
+ toggle = 1^toggle;
+ }
+ }
+ e0->type = ELMT_NONE;
+ e1->type = ELMT_NONE;
+ g->elists->elements = elements0;
+ g->elists->next->elements = elements1;
+}
+
+static void tseq_tcptrace_toggle_seq_origin (struct graph *g)
+{
+ g->s.tseq_tcptrace.flags ^= SEQ_ORIGIN;
+
+ if ((g->s.tseq_tcptrace.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
+ g->y_axis->min = g->bounds.y0;
+ else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
+ g->y_axis->min = 0;
+}
+
+static void tseq_tcptrace_toggle_time_origin (struct graph *g)
+{
+ g->s.tseq_tcptrace.flags ^= TIME_ORIGIN;
+
+ if ((g->s.tseq_tcptrace.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
+ g->x_axis->min = g->bounds.x0;
+ else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
+ g->x_axis->min = 0;
+}
+
+/*
+ * throughput graph
+ */
+
+static void tput_make_elmtlist (struct graph *g)
+{
+ struct segment *tmp, *oldest;
+ struct element *elements, *e;
+ int i, sum=0;
+ double dtime, tput;
+
+ if (g->elists->elements == NULL) {
+ int n = 1 + get_num_dsegs (g);
+ e = elements = (struct element * )malloc (n*sizeof (struct element));
+ } else
+ e = elements = g->elists->elements;
+
+ for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
+ double time = tmp->rel_secs + tmp->rel_usecs/1000000.0;
+ dtime = time - (oldest->rel_secs + oldest->rel_usecs/1000000.0);
+ if (i>g->s.tput.nsegs) {
+ sum -= oldest->data;
+ oldest=oldest->next;
+ }
+ sum += tmp->data;
+ tput = sum / dtime;
+ /* debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput); */
+
+ e->type = ELMT_ARC;
+ e->parent = tmp;
+ e->gc = g->fg_gc;
+ e->p.arc.dim.width = g->s.tput.width;
+ e->p.arc.dim.height = g->s.tput.height;
+ e->p.arc.dim.x = g->zoom.x*(time - g->bounds.x0) - g->s.tput.width/2.0;
+ e->p.arc.dim.y = g->zoom.y*tput + g->s.tput.height/2.0;
+ e->p.arc.filled = TRUE;
+ e->p.arc.angle1 = 0;
+ e->p.arc.angle2 = 23040;
+ e++;
+ }
+ e->type = ELMT_NONE;
+ g->elists->elements = elements;
+}
+
+/* Purpose of <graph_type>_initialize functions:
+ * - find maximum and minimum for both axes
+ * - call setup routine for style struct */
+static void tput_initialize (struct graph *g)
+{
+ struct segment *tmp, *oldest, *last;
+ int i, sum=0;
+ double dtime, tput, tputmax=0;
+ double t0, tmax, y0, ymax;
+
+ debug(DBS_FENTRY) puts ("tput_initialize()");
+
+ tput_read_config(g);
+
+ for (last=g->segments; last->next; last=last->next);
+ for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
+ dtime = tmp->rel_secs + tmp->rel_usecs/1000000.0 -
+ (oldest->rel_secs + oldest->rel_usecs/1000000.0);
+ if (i>g->s.tput.nsegs) {
+ sum -= oldest->data;
+ oldest=oldest->next;
+ }
+ sum += tmp->data;
+ tput = sum / dtime;
+ debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput);
+ if (tput > tputmax)
+ tputmax = tput;
+ }
+
+ t0 = g->segments->rel_secs + g->segments->rel_usecs / 1000000.0;
+ tmax = last->rel_secs + last->rel_usecs / 1000000.0;
+ y0 = 0;
+ ymax = tputmax;
+
+ g->bounds.x0 = t0;
+ g->bounds.y0 = y0;
+ g->bounds.width = tmax - t0;
+ g->bounds.height = ymax - y0;
+ g->zoom.x = (g->geom.width - 1) / g->bounds.width;
+ g->zoom.y = (g->geom.height -1) / g->bounds.height;
+}
+
+static void tput_read_config (struct graph *g)
+{
+ debug(DBS_FENTRY) puts ("tput_read_config()");
+
+ g->s.tput.width = 4;
+ g->s.tput.height = 4;
+ g->s.tput.nsegs = 20;
+
+ g->title = (char ** )malloc (2 * sizeof (char *));
+ g->title[0] = "Throughput Graph";
+ g->title[1] = NULL;
+ g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
+ g->y_axis->label[0] = "[B/s]";
+ g->y_axis->label[1] = "Throughput";
+ g->y_axis->label[2] = NULL;
+ g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
+ g->x_axis->label[0] = "Time[s]";
+ g->x_axis->label[1] = NULL;
+ g->s.tput.flags = 0;
+}
+
+static void tput_toggle_time_origin (struct graph *g)
+{
+ g->s.tput.flags ^= TIME_ORIGIN;
+
+ if ((g->s.tput.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
+ g->x_axis->min = g->bounds.x0;
+ else /* g->s.tput.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
+ g->x_axis->min = 0;
+}
+
+/* RTT graph */
+
+static void rtt_read_config (struct graph *g)
+{
+ debug(DBS_FENTRY) puts ("rtt_read_config()");
+
+ g->s.rtt.width = 4;
+ g->s.rtt.height = 4;
+ g->s.rtt.flags = 0;
+
+ g->title = (char ** )malloc (2 * sizeof (char *));
+ g->title[0] = "Round Trip Time Graph";
+ g->title[1] = NULL;
+ g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
+ g->y_axis->label[0] = "RTT [s]";
+ g->y_axis->label[1] = NULL;
+ g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
+ g->x_axis->label[0] = "Sequence Number[B]";
+ g->x_axis->label[1] = NULL;
+}
+
+static void rtt_initialize (struct graph *g)
+{
+ struct segment *tmp, *first=NULL;
+ struct unack *unack = NULL, *u;
+ double rttmax=0;
+ double x0, xmax=0, y0, ymax;
+
+ debug(DBS_FENTRY) puts ("rtt_initialize()");
+
+ rtt_read_config (g);
+
+ for (tmp=g->segments; tmp; tmp=tmp->next) {
+ if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
+ unsigned int seqno = g_ntohl (tmp->tcphdr.seq);
+
+ if (!first)
+ first= tmp;
+
+ if (tmp->data && !rtt_is_retrans (unack, seqno)) {
+ double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+ u = rtt_get_new_unack (time, seqno);
+ if (!u) return;
+ rtt_put_unack_on_list (&unack, u);
+ }
+
+ if (seqno + tmp->data > xmax)
+ xmax = seqno + tmp->data;
+ } else {
+ unsigned int ackno = g_ntohl (tmp->tcphdr.ack_seq);
+ double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+ struct unack *v;
+
+ for (u=unack; u; u=v)
+ if (ackno > u->seqno) {
+ double rtt = time - u->time;
+ if (rtt > rttmax)
+ rttmax = rtt;
+ v=u->next;
+ rtt_delete_unack_from_list (&unack, u);
+ } else
+ v=u->next;
+ }
+ }
+
+ x0 = g_ntohl (first->tcphdr.seq);
+ y0 = 0;
+ ymax = rttmax;
+
+ g->bounds.x0 = x0;
+ g->bounds.y0 = y0;
+ g->bounds.width = xmax - x0;
+ g->bounds.height = ymax - y0;
+ g->zoom.x = g->geom.width / g->bounds.width;
+ g->zoom.y = g->geom.height / g->bounds.height;
+}
+
+static int rtt_is_retrans (struct unack *list, unsigned int seqno)
+{
+ struct unack *u;
+
+ for (u=list; u; u=u->next)
+ if (u->seqno == seqno)
+ return TRUE;
+
+ return FALSE;
+}
+
+static struct unack *rtt_get_new_unack (double time, unsigned int seqno)
+{
+ struct unack *u;
+
+ u = (struct unack * )malloc (sizeof (struct unack));
+ if (!u)
+ return NULL;
+ u->next = NULL;
+ u->time = time;
+ u->seqno = seqno;
+ return u;
+}
+
+static void rtt_put_unack_on_list (struct unack **l, struct unack *new)
+{
+ struct unack *u, *list = *l;
+
+ for (u=list; u; u=u->next)
+ if (!u->next)
+ break;
+
+ if (u)
+ u->next = new;
+ else
+ *l = new;
+}
+
+static void rtt_delete_unack_from_list (struct unack **l, struct unack *dead)
+{
+ struct unack *u, *list = *l;
+
+ if (!dead || !list)
+ return;
+
+ if (dead==list) {
+ *l = list->next;
+ free (list);
+ } else
+ for (u=list; u; u=u->next)
+ if (u->next == dead) {
+ u->next = u->next->next;
+ free (dead);
+ break;
+ }
+}
+
+static void rtt_make_elmtlist (struct graph *g)
+{
+ struct segment *tmp;
+ struct unack *unack = NULL, *u;
+ struct element *elements, *e;
+
+ debug(DBS_FENTRY) puts ("rtt_make_elmtlist()");
+
+ if (g->elists->elements == NULL) {
+ int n = 1 + get_num_dsegs (g);
+ e = elements = (struct element * )malloc (n*sizeof (struct element));
+ } else
+ e = elements = g->elists->elements;
+
+ for (tmp=g->segments; tmp; tmp=tmp->next) {
+ if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
+ unsigned int seqno = g_ntohl (tmp->tcphdr.seq);
+
+ if (tmp->data && !rtt_is_retrans (unack, seqno)) {
+ double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+ u = rtt_get_new_unack (time, seqno);
+ if (!u) return;
+ rtt_put_unack_on_list (&unack, u);
+ }
+ } else {
+ unsigned int ackno = g_ntohl (tmp->tcphdr.ack_seq);
+ double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+ struct unack *v;
+
+ for (u=unack; u; u=v)
+ if (ackno > u->seqno) {
+ double rtt = time - u->time;
+
+ e->type = ELMT_ARC;
+ e->parent = tmp;
+ e->gc = g->fg_gc;
+ e->p.arc.dim.width = g->s.rtt.width;
+ e->p.arc.dim.height = g->s.rtt.height;
+ e->p.arc.dim.x = g->zoom.x * (u->seqno - g->bounds.x0)
+ - g->s.rtt.width/2.0;
+ e->p.arc.dim.y = g->zoom.y * rtt + g->s.rtt.height/2.0;
+ e->p.arc.filled = TRUE;
+ e->p.arc.angle1 = 0;
+ e->p.arc.angle2 = 23040;
+ e++;
+
+ v=u->next;
+ rtt_delete_unack_from_list (&unack, u);
+ } else
+ v=u->next;
+ }
+ }
+ e->type = ELMT_NONE;
+ g->elists->elements = elements;
+}
+
+static void rtt_toggle_seq_origin (struct graph *g)
+{
+ g->s.rtt.flags ^= SEQ_ORIGIN;
+
+ if ((g->s.rtt.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
+ g->x_axis->min = g->bounds.x0;
+ else
+ g->x_axis->min = 0;
+}
+
+#ifdef WIN32
+/* replacement of Unix rint() for Windows */
+static int rint (double x)
+{
+ char *buf;
+ int i,dec,sig;
+
+ buf = _fcvt(x, 0, &dec, &sig);
+ i = atoi(buf);
+ if(sig == 1) {
+ i = i * -1;
+ }
+ return(i);
+}
+#endif
diff --git a/gtk2/tcp_graph.h b/gtk2/tcp_graph.h
new file mode 100644
index 0000000000..3cc23956d8
--- /dev/null
+++ b/gtk2/tcp_graph.h
@@ -0,0 +1,27 @@
+/* tcp_graph.h
+ * Declarations for TCP graph drawing code
+ * By Pavel Mores <pvl@uh.cz>
+ * Win32 port: rwh@unifiedtech.com
+ *
+ * $Id: tcp_graph.h,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+extern void tcp_graph_cb (GtkWidget *, gpointer, guint);
diff --git a/gtk2/ui_util.c b/gtk2/ui_util.c
new file mode 100644
index 0000000000..690b3e7795
--- /dev/null
+++ b/gtk2/ui_util.c
@@ -0,0 +1,205 @@
+/* ui_util.c
+ * UI utility routines
+ *
+ * $Id: ui_util.c,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib.h>
+
+#include <gtk/gtk.h>
+
+#include "gtkglobals.h"
+#include "ui_util.h"
+#include "prefs.h"
+#include "../ui_util.h"
+#include "image/eicon3d16.xpm"
+
+
+/* Set the name of the top-level window and its icon to the specified
+ string. */
+void
+set_main_window_name(gchar *window_name)
+{
+ gtk_window_set_title(GTK_WINDOW(top_level), window_name);
+ gdk_window_set_icon_name(top_level->window, window_name);
+}
+
+/* Given a pointer to a GtkWidget for a top-level window, raise it and
+ de-iconify it. This routine is used if the user has done something to
+ ask that a window of a certain type be popped up when there can be only
+ one such window and such a window has already been popped up - we
+ pop up the existing one rather than creating a new one.
+
+ XXX - we should request that it be given the input focus, too. Alas,
+ GDK has nothing to do that, e.g. by calling "XSetInputFocus()" in a
+ window in X. Besides, using "XSetInputFocus()" doesn't work anyway,
+ apparently due to the way GTK+/GDK manages the input focus.
+
+ The X Desktop Group's Window Manager Standard specifies, in the section
+ on Root Window Properties, an _NET_ACTIVE_WINDOW client message that
+ can be sent to the root window, containing the window ID of the
+ window to activate; I infer that this might be the way to give the
+ window the input focus - I assume that means it's also de-iconified,
+ but I wouldn't assume it'd raise it.
+
+ XXX - will this do the right thing on window systems other than X? */
+void
+reactivate_window(GtkWidget *win)
+{
+ gdk_window_show(win->window);
+ gdk_window_raise(win->window);
+}
+
+/* Set our window icon. The GDK documentation doesn't provide any
+ actual documentation for gdk_window_set_icon(), so we'll steal
+ libgimp/gimpdialog.c:gimp_dialog_realize_callback() from the Gimp
+ sources and assume it's safe.
+
+ XXX - The current icon size is fixed at 16x16 pixels, which looks fine
+ with kwm (KDE 1.x's window manager), Sawfish (the "default" window
+ manager for GNOME?), and under Windows with Exceed putting X windows
+ on the Windows desktop, using Exceed as the window manager, as those
+ window managers put a 16x16 icon on the title bar.
+
+ The window managers in some windowing environments (e.g. dtwm in CDE)
+ and some stand-alone window managers have larger icon sizes (many window
+ managers put the window icon on the desktop, in the Windows 3.x style,
+ rather than in the titlebar, in the Windows 4.x style), so we need to
+ find a way to size our icon appropriately.
+
+ The X11 Inter-Client Communications Conventions Manual, Version 1.1,
+ in X11R5, specifies that "a window manager that wishes to place
+ constraints on the sizes of icon pixmaps and/or windows should
+ place a property called WM_ICON_SIZE on the root"; that property
+ contains minimum width and height, maximum width and height, and
+ width and height increment values. "XGetIconSizes()" retrieves
+ that property; unfortunately, I've yet to find a window manager
+ that sets it on the root window (kwm, AfterStep, and Exceed don't
+ appear to set it).
+
+ The X Desktop Group's Window Manager Standard specifies, in the section
+ on Application Window Properties, an _NET_WM_ICON property, presumably
+ set by the window manager, which is an array of possible icon sizes
+ for the client. There's no API in GTK+ 1.2[.x] for this; there may
+ eventually be one either in GTK+ 2.0 or GNOME 2.0.
+
+ Some window managers can be configured to take the window name
+ specified by the WM_NAME property of a window or the resource
+ or class name specified by the WM_CLASS property and base the
+ choice of icon for the window on one of those; WM_CLASS for
+ Ethereal's windows has a resource name of "ethereal" and a class
+ name of "Ethereal". However, the way that's done is window-manager-
+ specific, and there's no way to determine what size a particular
+ window manager would want, so there's no way to automate this as
+ part of the installation of Ethereal.
+ */
+void
+window_icon_realize_cb (GtkWidget *win, gpointer data _U_)
+{
+#ifndef WIN32
+ static GdkPixmap *icon_pmap = NULL;
+ static GdkBitmap *icon_mask = NULL;
+ GtkStyle *style;
+
+ style = gtk_widget_get_style (win);
+
+ if (icon_pmap == NULL) {
+ icon_pmap = gdk_pixmap_create_from_xpm_d (win->window,
+ &icon_mask, &style->bg[GTK_STATE_NORMAL], eicon3d16_xpm);
+ }
+
+ gdk_window_set_icon (win->window, NULL, icon_pmap, icon_mask);
+#endif
+}
+
+/* List of all GtkScrolledWindows, so we can globally set the scrollbar
+ placement of all of them. */
+static GList *scrolled_windows;
+
+static void setup_scrolled_window(GtkWidget *scrollw);
+static void forget_scrolled_window(GtkWidget *scrollw, gpointer data);
+static void set_scrollbar_placement_scrollw(GtkWidget *scrollw);
+
+/* Create a GtkScrolledWindow, set its scrollbar placement appropriately,
+ and remember it. */
+GtkWidget *
+scrolled_window_new(GtkAdjustment *hadjustment, GtkAdjustment *vadjustment)
+{
+ GtkWidget *scrollw;
+
+ scrollw = gtk_scrolled_window_new(hadjustment, vadjustment);
+ setup_scrolled_window(scrollw);
+ return scrollw;
+}
+
+/* Set a GtkScrolledWindow's scrollbar placement and add it to the list
+ of GtkScrolledWindows. */
+static void
+setup_scrolled_window(GtkWidget *scrollw)
+{
+ set_scrollbar_placement_scrollw(scrollw);
+
+ scrolled_windows = g_list_append(scrolled_windows, scrollw);
+
+ /* Catch the "destroy" event on the widget, so that we remove it from
+ the list when it's destroyed. */
+ g_signal_connect(G_OBJECT(scrollw), "destroy",
+ G_CALLBACK(forget_scrolled_window), NULL);
+}
+
+/* Remove a GtkScrolledWindow from the list of GtkScrolledWindows. */
+static void
+forget_scrolled_window(GtkWidget *scrollw, gpointer data _U_)
+{
+ scrolled_windows = g_list_remove(scrolled_windows, scrollw);
+}
+
+/* Set the scrollbar placement of a GtkScrolledWindow based upon user
+ preference. */
+static void
+set_scrollbar_placement_scrollw(GtkWidget *scrollw)
+{
+ if (prefs.gui_scrollbar_on_right) {
+ gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
+ GTK_CORNER_TOP_LEFT);
+ } else {
+ gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
+ GTK_CORNER_TOP_RIGHT);
+ }
+}
+
+static void
+set_scrollbar_placement_cb(gpointer data, gpointer user_data _U_)
+{
+ set_scrollbar_placement_scrollw((GtkWidget *)data);
+}
+
+/* Set the scrollbar placement of all GtkScrolledWindows based on
+ user preference. */
+void
+set_scrollbar_placement_all(void)
+{
+ g_list_foreach(scrolled_windows, set_scrollbar_placement_cb, NULL);
+}
diff --git a/gtk2/ui_util.h b/gtk2/ui_util.h
new file mode 100644
index 0000000000..79ae71b120
--- /dev/null
+++ b/gtk2/ui_util.h
@@ -0,0 +1,55 @@
+/* ui_util.h
+ * Definitions for UI utility routines
+ *
+ * $Id: ui_util.h,v 1.1 2002/08/31 09:55:22 oabad Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * 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.
+ */
+
+#ifndef __GTKGUIUI_UTIL_H__
+#define __GTKGUIUI_UTIL_H__
+
+/* Given a pointer to a GtkWidget for a top-level window, raise it and
+ de-iconify it. This routine is used if the user has done something to
+ ask that a window of a certain type be popped up when there can be only
+ one such window and such a window has already been popped up - we
+ pop up the existing one rather than creating a new one. */
+void reactivate_window(GtkWidget *);
+
+/* Set the window icon to the 16x16 3D icon. */
+void window_icon_realize_cb (GtkWidget *, gpointer);
+
+/* Create a GtkScrolledWindow, set its scrollbar placement appropriately,
+ and remember it. */
+GtkWidget *scrolled_window_new(GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment);
+
+/* Set the scrollbar placement of all scrolled windows based on user
+ preference. */
+void set_scrollbar_placement_all(void);
+
+/* Create a GtkCTree, give it the right styles, and remember it. */
+GtkWidget *ctree_new(gint columns, gint tree_column);
+GtkWidget *ctree_new_with_titles(gint columns, gint tree_column,
+ gchar *titles[]);
+
+/* Set the styles of all GtkCTrees based upon user preferences. */
+void set_ctree_styles_all(void);
+
+#endif /* __GTKGUIUI_UTIL_H__ */