diff options
Diffstat (limited to 'gtk2')
71 files changed, 24425 insertions, 0 deletions
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, ¤t)) { + /* 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, ¤t); + 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 (¤t, 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__ */ |