And finally (I hope) the last part from the patch
[obnox/wireshark/wip.git] / gtk / file_dlg.c
index 112694d0c3fb3ed521355c0cfcc53a272dd0b2a4..54864ec3668076a57e1dd1d9e30f34efdf49f10b 100644 (file)
@@ -1,7 +1,7 @@
 /* file_dlg.c
  * Dialog boxes for handling files
  *
- * $Id: file_dlg.c,v 1.111 2004/06/17 22:42:40 guy Exp $
+ * $Id$
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
 
 #include <gtk/gtk.h>
 
-#include "range.h"
+#include "packet-range.h"
 #include <epan/filesystem.h>
 
 #include "globals.h"
 #include "gtkglobals.h"
-#include <epan/resolv.h>
+#include <epan/addr_resolv.h>
 #include "keys.h"
-#include "filter_prefs.h"
+#include "filter_dlg.h"
 #include "ui_util.h"
 #include "alert_box.h"
 #include "simple_dialog.h"
 #include "file_dlg.h"
 #include "main.h"
 #include "compat_macros.h"
-#include "prefs.h"
+#include <epan/prefs.h>
 #include "recent.h"
 #include "color.h"
 #include "../ui_util.h"
-#include "gtk/color_filters.h"
+#include "color_filters.h"
 #include "gtk/color_dlg.h"
 #ifdef HAVE_LIBPCAP
 #include "capture_dlg.h"
+#endif
 #include "range_utils.h"
+#include "merge.h"
+#include "util.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
 #endif
 
+#ifdef HAVE_IO_H
+#include <io.h> /* open/close on win32 */
+#endif
 
 static void file_open_ok_cb(GtkWidget *w, gpointer fs);
 static void file_open_destroy_cb(GtkWidget *win, gpointer user_data);
@@ -69,6 +78,7 @@ static void file_color_import_ok_cb(GtkWidget *w, gpointer fs);
 static void file_color_import_destroy_cb(GtkWidget *win, gpointer user_data);
 static void file_color_export_ok_cb(GtkWidget *w, gpointer fs);
 static void file_color_export_destroy_cb(GtkWidget *win, gpointer user_data);
+static void set_file_type_list(GtkWidget *option_menu);
 
 #define E_FILE_M_RESOLVE_KEY     "file_dlg_mac_resolve_key"
 #define E_FILE_N_RESOLVE_KEY     "file_dlg_network_resolve_key"
@@ -80,6 +90,16 @@ static void file_color_export_destroy_cb(GtkWidget *win, gpointer user_data);
 
 #define ARGUMENT_CL "argument_cl"
 
+
+#define PREVIEW_TABLE_KEY       "preview_table_key"
+#define PREVIEW_FILENAME_KEY    "preview_filename_key"
+#define PREVIEW_FORMAT_KEY      "preview_format_key"
+#define PREVIEW_SIZE_KEY        "preview_size_key"
+#define PREVIEW_ELAPSED_KEY     "preview_elapsed_key"
+#define PREVIEW_PACKETS_KEY     "preview_packets_key"
+#define PREVIEW_FIRST_KEY       "preview_first_key"
+
+
 /*
  * 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"
@@ -88,8 +108,319 @@ static void file_color_export_destroy_cb(GtkWidget *win, gpointer user_data);
  */
 static GtkWidget *file_save_as_w;
 
+/* XXX - can we make these not be static? */
+static packet_range_t range;
+static gboolean color_marked;
+static int filetype;
+static GtkWidget *cfmark_cb;
+static GtkWidget *ft_om;
+static GtkWidget *range_tb;
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#define PREVIEW_STR_MAX         200
+
+static double
+secs_usecs( guint32 s, guint32 us)
+{
+  return (us / 1000000.0) + (double)s;
+}
+
+
+/* set a new filename for the preview widget */
+static wtap *
+preview_set_filename(GtkWidget *prev, const gchar *cf_name)
+{
+    GtkWidget  *label;
+    wtap       *wth;
+    int         err = 0;
+    gchar      *err_info;
+    struct stat cf_stat;
+    gchar       string_buff[PREVIEW_STR_MAX];
+    guint64     filesize;
+
+
+    /* init preview labels */
+    label = OBJECT_GET_DATA(prev, PREVIEW_FILENAME_KEY);
+    gtk_label_set_text(GTK_LABEL(label), "-");
+    label = OBJECT_GET_DATA(prev, PREVIEW_FORMAT_KEY);
+    gtk_label_set_text(GTK_LABEL(label), "-");
+    label = OBJECT_GET_DATA(prev, PREVIEW_SIZE_KEY);
+    gtk_label_set_text(GTK_LABEL(label), "-");
+    label = OBJECT_GET_DATA(prev, PREVIEW_ELAPSED_KEY);
+    gtk_label_set_text(GTK_LABEL(label), "-");
+    label = OBJECT_GET_DATA(prev, PREVIEW_PACKETS_KEY);
+    gtk_label_set_text(GTK_LABEL(label), "-");
+    label = OBJECT_GET_DATA(prev, PREVIEW_FIRST_KEY);
+    gtk_label_set_text(GTK_LABEL(label), "-");
+
+    if(!cf_name) {
+        return NULL;
+    }
+
+    label = OBJECT_GET_DATA(prev, PREVIEW_FILENAME_KEY);
+    gtk_label_set_text(GTK_LABEL(label), get_basename(cf_name));
+
+    if (test_for_directory(cf_name) == EISDIR) {
+        label = OBJECT_GET_DATA(prev, PREVIEW_FORMAT_KEY);
+        gtk_label_set_text(GTK_LABEL(label), "directory");
+        return NULL;
+    }
+
+    wth = wtap_open_offline(cf_name, &err, &err_info, TRUE);
+    if (wth == NULL) {
+        label = OBJECT_GET_DATA(prev, PREVIEW_FORMAT_KEY);
+        if(err == WTAP_ERR_FILE_UNKNOWN_FORMAT) {
+            gtk_label_set_text(GTK_LABEL(label), "unknown file format");
+        } else {
+            gtk_label_set_text(GTK_LABEL(label), "error opening file");
+        }
+        return NULL;
+    }
+
+    /* Find the size of the file. */
+    if (fstat(wtap_fd(wth), &cf_stat) < 0) {
+        wtap_close(wth);
+        return NULL;
+    }
+
+    /* size */
+    filesize = cf_stat.st_size;
+    g_snprintf(string_buff, PREVIEW_STR_MAX, "%" PRIu64 " bytes", filesize);
+    label = OBJECT_GET_DATA(prev, PREVIEW_SIZE_KEY);
+    gtk_label_set_text(GTK_LABEL(label), string_buff);
+
+    /* type */
+    g_snprintf(string_buff, PREVIEW_STR_MAX, "%s", wtap_file_type_string(wtap_file_type(wth)));
+    label = OBJECT_GET_DATA(prev, PREVIEW_FORMAT_KEY);
+    gtk_label_set_text(GTK_LABEL(label), string_buff);
+
+    return wth;
+}
+
+
+/* do a preview run on the currently selected capture file */
+static void
+preview_do(GtkWidget *prev, wtap *wth)
+{
+    GtkWidget  *label;
+    unsigned int elapsed_time;
+    time_t      time_preview;
+    time_t      time_current;
+    int         err = 0;
+    gchar      *err_info;
+    long        data_offset;
+    const struct wtap_pkthdr *phdr;
+    double      start_time = 0;        /* seconds, with msec resolution */
+    double      stop_time = 0; /* seconds, with msec resolution */
+    double      cur_time;
+    unsigned int packets = 0;
+    gboolean    is_breaked = FALSE;
+    gchar       string_buff[PREVIEW_STR_MAX];
+    time_t      ti_time;
+    struct tm  *ti_tm;
+
+
+    time(&time_preview);
+    while ( (wtap_read(wth, &err, &err_info, &data_offset)) ) {
+        phdr = wtap_phdr(wth);        
+        cur_time = secs_usecs(phdr->ts.tv_sec, phdr->ts.tv_usec);
+        if(packets == 0) {
+            start_time         = cur_time;
+            stop_time = cur_time;
+        }
+        if (cur_time < start_time) {
+            start_time = cur_time;
+        }
+        if (cur_time > stop_time){
+            stop_time = cur_time;
+        }
+
+        packets++;
+        if(packets%1000) {
+            /* do we have a timeout? */
+            time(&time_current);
+            if(time_current-time_preview >= (time_t) prefs.gui_fileopen_preview) {
+                is_breaked = TRUE;
+                break;
+            }
+        }
+    }
+
+    if(err != 0) {
+        g_snprintf(string_buff, PREVIEW_STR_MAX, "error after reading %u packets", packets);
+        label = OBJECT_GET_DATA(prev, PREVIEW_PACKETS_KEY);
+        gtk_label_set_text(GTK_LABEL(label), string_buff);
+        wtap_close(wth);
+        return;
+    }
+
+    /* packet count */
+    if(is_breaked) {
+        g_snprintf(string_buff, PREVIEW_STR_MAX, "more than %u packets (preview timeout)", packets);
+    } else {
+        g_snprintf(string_buff, PREVIEW_STR_MAX, "%u", packets);
+    }
+    label = OBJECT_GET_DATA(prev, PREVIEW_PACKETS_KEY);
+    gtk_label_set_text(GTK_LABEL(label), string_buff);
+
+    /* first packet */
+    ti_time = (long)start_time;
+    ti_tm = localtime( &ti_time );
+    g_snprintf(string_buff, PREVIEW_STR_MAX,
+             "%04d-%02d-%02d %02d:%02d:%02d",
+             ti_tm->tm_year + 1900,
+             ti_tm->tm_mon + 1,
+             ti_tm->tm_mday,
+             ti_tm->tm_hour,
+             ti_tm->tm_min,
+             ti_tm->tm_sec);
+    label = OBJECT_GET_DATA(prev, PREVIEW_FIRST_KEY);
+    gtk_label_set_text(GTK_LABEL(label), string_buff);
+
+    /* elapsed time */
+    elapsed_time = (unsigned int)(stop_time-start_time);
+    if(elapsed_time/86400) {
+      g_snprintf(string_buff, PREVIEW_STR_MAX, "%02u days %02u:%02u:%02u", 
+        elapsed_time/86400, elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60);
+    } else {
+      g_snprintf(string_buff, PREVIEW_STR_MAX, "%02u:%02u:%02u", 
+        elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60);
+    }
+    if(is_breaked) {
+      g_snprintf(string_buff, PREVIEW_STR_MAX, "unknown");
+    }
+    label = OBJECT_GET_DATA(prev, PREVIEW_ELAPSED_KEY);
+    gtk_label_set_text(GTK_LABEL(label), string_buff);
+
+    wtap_close(wth);
+}
+
+#if 0
+/* as the dialog layout will look very ugly when using the file chooser preview mechanism,
+   simply use the same layout as in GTK1 */
+/* (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2 */
+static void
+update_preview_cb (GtkFileChooser *file_chooser, gpointer data)
+{
+    GtkWidget *prev = GTK_WIDGET (data);
+    char *cf_name;
+    gboolean have_preview;
+
+    cf_name = gtk_file_chooser_get_preview_filename (file_chooser);
+
+    have_preview = preview_set_filename(prev, cf_name);
+
+    g_free (cf_name);
+
+    have_preview = TRUE;
+    gtk_file_chooser_set_preview_widget_active (file_chooser, have_preview);
+}
+#endif
+
+
+/* the filename text entry changed */
+static void
+file_open_entry_changed(GtkWidget *w _U_, gpointer file_sel)
+{
+    GtkWidget *prev = OBJECT_GET_DATA(file_sel, PREVIEW_TABLE_KEY);
+    const gchar* cf_name;
+    gboolean have_preview;
+    wtap       *wth;
+
+    /* get the filename */
+#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
+    cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_sel));
+#else
+    cf_name = gtk_file_selection_get_filename(GTK_FILE_SELECTION(file_sel));
+#endif
+
+    /* set the filename to the preview */
+    wth = preview_set_filename(prev, cf_name);
+    have_preview = (wth != NULL);
+
+    /* make the preview widget sensitive */
+    gtk_widget_set_sensitive(prev, have_preview);
+
+    /* make the open/save/... dialog button sensitive */
+#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
+    gtk_dialog_set_response_sensitive(file_sel, GTK_RESPONSE_ACCEPT, have_preview);
+#else
+    gtk_widget_set_sensitive(GTK_FILE_SELECTION(file_sel)->ok_button, have_preview);
+#endif
+
+    /* do the actual preview */
+    if(have_preview)
+        preview_do(prev, wth);
+}
+
+
+/* copied from summary_dlg.c */
+static GtkWidget *
+add_string_to_table_sensitive(GtkWidget *list, guint *row, gchar *title, gchar *value, gboolean sensitive)
+{
+    GtkWidget *label;
+    gchar     *indent;
+
+    if(strlen(value) != 0) {
+        indent = g_strdup_printf("   %s", title);
+    } else {
+        indent = g_strdup(title);
+    }
+    label = gtk_label_new(indent);
+    g_free(indent);
+    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+    gtk_widget_set_sensitive(label, sensitive);
+    gtk_table_attach_defaults(GTK_TABLE(list), label, 0, 1, *row, *row+1);
+
+    label = gtk_label_new(value);
+    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+    gtk_widget_set_sensitive(label, sensitive);
+    gtk_table_attach_defaults(GTK_TABLE(list), label, 1, 2, *row, *row+1);
+
+    *row = *row + 1;
+
+    return label;
+}
+
+static GtkWidget *
+add_string_to_table(GtkWidget *list, guint *row, gchar *title, gchar *value)
+{
+    return add_string_to_table_sensitive(list, row, title, value, TRUE);
+}
+
 
 
+static GtkWidget *
+preview_new(void)
+{
+    GtkWidget *table, *label;
+    guint         row;
+
+    table = gtk_table_new(1, 2, FALSE);
+    gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+    gtk_table_set_row_spacings(GTK_TABLE(table), 3);
+    row = 0;
+
+    label = add_string_to_table(table, &row, "Filename:", "-");
+    WIDGET_SET_SIZE(label, DEF_WIDTH/3, -1);
+    OBJECT_SET_DATA(table, PREVIEW_FILENAME_KEY, label);
+    label = add_string_to_table(table, &row, "Format:", "-");
+    OBJECT_SET_DATA(table, PREVIEW_FORMAT_KEY, label);
+    label = add_string_to_table(table, &row, "Size:", "-");
+    OBJECT_SET_DATA(table, PREVIEW_SIZE_KEY, label);
+    label = add_string_to_table(table, &row, "Packets:", "-");
+    OBJECT_SET_DATA(table, PREVIEW_PACKETS_KEY, label);
+    label = add_string_to_table(table, &row, "First Packet:", "-");
+    OBJECT_SET_DATA(table, PREVIEW_FIRST_KEY, label);
+    label = add_string_to_table(table, &row, "Elapsed time:", "-");
+    OBJECT_SET_DATA(table, PREVIEW_ELAPSED_KEY, label);
+
+    return table;
+}
+
 /*
  * 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
@@ -102,8 +433,8 @@ static GtkWidget *file_open_w;
 void
 file_open_cmd(GtkWidget *w)
 {
-  GtkWidget    *main_vb, *filter_hbox, *filter_bt, *filter_te,
-               *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
+  GtkWidget    *main_hb, *main_vb, *filter_hbox, *filter_bt, *filter_te,
+               *m_resolv_cb, *n_resolv_cb, *t_resolv_cb, *prev;
 #if GTK_MAJOR_VERSION < 2
   GtkAccelGroup *accel_group;
 #endif
@@ -123,8 +454,13 @@ file_open_cmd(GtkWidget *w)
 
   file_open_w = file_selection_new("Ethereal: Open Capture File",
                                    FILE_SELECTION_OPEN);
-  /* window is already shown here, gtk_window_set_default_size() will not work */
-  WIDGET_SET_SIZE(file_open_w, DEF_WIDTH, DEF_HEIGHT);
+#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
+  /* it's annoying, that the file chooser dialog is already shown here, 
+     so we cannot use the correct gtk_window_set_default_size() to resize it */
+  WIDGET_SET_SIZE(GTK_WINDOW(file_open_w), DEF_WIDTH, DEF_HEIGHT);
+#else
+  gtk_window_set_default_size(GTK_WINDOW(file_open_w), DEF_WIDTH, DEF_HEIGHT);
+#endif
 
 #if GTK_MAJOR_VERSION < 2
   /* Accelerator group for the accelerators (or, as they're called in
@@ -153,13 +489,19 @@ file_open_cmd(GtkWidget *w)
       file_selection_set_current_folder(file_open_w, prefs.gui_fileopen_dir);
     break;
   }
-    
+
+  
+  main_hb = gtk_hbox_new(FALSE, 3);
+  file_selection_set_extra_widget(file_open_w, main_hb);
+  gtk_widget_show(main_hb);
+
   /* Container for each row of widgets */
   main_vb = gtk_vbox_new(FALSE, 3);
   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
-  file_selection_set_extra_widget(file_open_w, main_vb);
+  gtk_box_pack_start(GTK_BOX(main_hb), main_vb, FALSE, FALSE, 0);
   gtk_widget_show(main_vb);
 
+  /* filter row */
   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);
@@ -184,6 +526,7 @@ file_open_cmd(GtkWidget *w)
                   E_RFILTER_TE_KEY, filter_te);
 #endif
 
+  /* resolve buttons */
   m_resolv_cb = CHECK_BUTTON_NEW_WITH_MNEMONIC("Enable _MAC name resolution", accel_group);
   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_resolv_cb),
        g_resolv_flags & RESOLV_MAC);
@@ -224,7 +567,17 @@ file_open_cmd(GtkWidget *w)
 
   SIGNAL_CONNECT(file_open_w, "destroy", file_open_destroy_cb, NULL);
 
+  /* preview widget */
+  prev = preview_new();
+  OBJECT_SET_DATA(file_open_w, PREVIEW_TABLE_KEY, prev);
+  gtk_widget_show_all(prev);
+  gtk_box_pack_start(GTK_BOX(main_hb), prev, TRUE, TRUE, 0);
+
 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
+  SIGNAL_CONNECT(GTK_FILE_CHOOSER(file_open_w), "selection-changed", 
+      file_open_entry_changed, file_open_w);
+  file_open_entry_changed(file_open_w, file_open_w);
+
   OBJECT_SET_DATA(file_open_w, E_DFILTER_TE_KEY,
                   OBJECT_GET_DATA(w, E_DFILTER_TE_KEY));
   if (gtk_dialog_run(GTK_DIALOG(file_open_w)) == GTK_RESPONSE_ACCEPT)
@@ -233,6 +586,9 @@ file_open_cmd(GtkWidget *w)
   }
   else window_destroy(file_open_w);
 #else
+  SIGNAL_CONNECT(GTK_FILE_SELECTION(file_open_w)->selection_entry, "changed", 
+      file_open_entry_changed, file_open_w);
+
   /* Connect the ok_button to file_open_ok_cb function and pass along a
      pointer to the file selection box widget */
   SIGNAL_CONNECT(GTK_FILE_SELECTION(file_open_w)->ok_button, "clicked",
@@ -255,11 +611,11 @@ file_open_cmd(GtkWidget *w)
 static void file_open_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
 {
     switch(btn) {
-    case(ESD_BTN_YES):
+    case(ESD_BTN_SAVE):
         /* save file first */
         file_save_as_cmd(after_save_open_dialog, data);
         break;
-    case(ESD_BTN_NO):
+    case(ESD_BTN_DONT_SAVE):
         cf_close(&cfile);
         file_open_cmd(data);
         break;
@@ -274,9 +630,9 @@ void
 file_open_cmd_cb(GtkWidget *widget, gpointer data _U_) {
   gpointer  dialog;
 
-  if((cfile.state != FILE_CLOSED) && !cfile.user_saved) {
+  if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
     /* user didn't saved his current file, ask him */
-    dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_YES_NO_CANCEL,
+    dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
                 PRIMARY_TEXT_START "Save capture file before opening a new one?" PRIMARY_TEXT_END "\n\n"
                 "If you open a new capture file without saving, your capture data will be discarded.");
     simple_dialog_set_cb(dialog, file_open_answered_cb, widget);
@@ -286,12 +642,14 @@ file_open_cmd_cb(GtkWidget *widget, gpointer data _U_) {
   }
 }
 
+/* user pressed "open" button */
 static void
 file_open_ok_cb(GtkWidget *w, gpointer fs) {
-  gchar     *cf_name, *rfilter, *s;
-  GtkWidget *filter_te, *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
-  dfilter_t *rfcode = NULL;
-  int        err;
+  gchar       *cf_name, *s;
+  const gchar *rfilter;
+  GtkWidget   *filter_te, *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
+  dfilter_t   *rfcode = NULL;
+  int          err;
 
 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
   cf_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
@@ -299,7 +657,7 @@ file_open_ok_cb(GtkWidget *w, gpointer fs) {
   cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
 #endif
   filter_te = OBJECT_GET_DATA(w, E_RFILTER_TE_KEY);
-  rfilter = (gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
+  rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
   if (!dfilter_compile(rfilter, &rfcode)) {
     bad_dfilter_alert_box(rfilter);
     g_free(cf_name);
@@ -318,7 +676,7 @@ file_open_ok_cb(GtkWidget *w, gpointer fs) {
   }
 
   /* Try to open the capture file. */
-  if ((err = cf_open(cf_name, FALSE, &cfile)) != 0) {
+  if (cf_open(&cfile, cf_name, FALSE, &err) != CF_OK) {
     /* 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,
@@ -335,27 +693,36 @@ file_open_ok_cb(GtkWidget *w, gpointer fs) {
   cfile.rfcode = rfcode;
 
   /* Set the global resolving variable */
-  g_resolv_flags = prefs.name_resolve & RESOLV_CONCURRENT;
+  g_resolv_flags = prefs.name_resolve;
   m_resolv_cb = OBJECT_GET_DATA(w, E_FILE_M_RESOLVE_KEY);
-  g_resolv_flags |= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (m_resolv_cb)) ? RESOLV_MAC : RESOLV_NONE;
+  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (m_resolv_cb)))
+    g_resolv_flags |= RESOLV_MAC;
+  else
+    g_resolv_flags &= ~RESOLV_MAC;
   n_resolv_cb = OBJECT_GET_DATA(w, E_FILE_N_RESOLVE_KEY);
-  g_resolv_flags |= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (n_resolv_cb)) ? RESOLV_NETWORK : RESOLV_NONE;
+  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (n_resolv_cb)))
+    g_resolv_flags |= RESOLV_NETWORK;
+  else
+    g_resolv_flags &= ~RESOLV_NETWORK;
   t_resolv_cb = OBJECT_GET_DATA(w, E_FILE_T_RESOLVE_KEY);
-  g_resolv_flags |= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (t_resolv_cb)) ? RESOLV_TRANSPORT : RESOLV_NONE;
+  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (t_resolv_cb)))
+    g_resolv_flags |= RESOLV_TRANSPORT;
+  else
+    g_resolv_flags &= ~RESOLV_TRANSPORT;
 
   /* We've crossed the Rubicon; get rid of the file selection box. */
   window_destroy(GTK_WIDGET (fs));
 
   switch (cf_read(&cfile)) {
 
-  case READ_SUCCESS:
-  case READ_ERROR:
+  case CF_READ_OK:
+  case CF_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:
+  case CF_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
@@ -382,7 +749,7 @@ file_open_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
 }
 
 /*
- * Keep a static pointer to the current "Marge Capture File" window, if
+ * Keep a static pointer to the current "Merge Capture File" window, if
  * any, so that if somebody tries to do "File:Merge" while there's already
  * an "Merge Capture File" window up, we just pop up the existing one,
  * rather than creating a new one.
@@ -393,11 +760,13 @@ static GtkWidget *file_merge_w;
 void
 file_merge_cmd(GtkWidget *w)
 {
-  GtkWidget    *main_vb, *filter_hbox, *filter_bt, *filter_te,
-               *prepend_rb, *chrono_rb, *append_rb;
+  GtkWidget    *main_hb, *main_vb, *ft_hb, *ft_lb, *filter_hbox,
+               *filter_bt, *filter_te, *prepend_rb, *chrono_rb,
+               *append_rb, *prev;
 #if GTK_MAJOR_VERSION < 2
   GtkAccelGroup *accel_group;
 #endif
+  GtkTooltips *tooltips = gtk_tooltips_new();
   /* 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 = {
@@ -412,10 +781,18 @@ file_merge_cmd(GtkWidget *w)
     return;
   }
 
+  /* Default to saving all packets, in the file's current format. */
+  filetype = cfile.cd_t;
+
   file_merge_w = file_selection_new("Ethereal: Merge with Capture File",
                                    FILE_SELECTION_OPEN);
-  /* window is already shown here, gtk_window_set_default_size() will not work */
-  WIDGET_SET_SIZE(file_merge_w, DEF_WIDTH, DEF_HEIGHT);
+#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
+  /* it's annoying, that the file chooser dialog is already shown here, 
+     so we cannot use the correct gtk_window_set_default_size() to resize it */
+  WIDGET_SET_SIZE(GTK_WINDOW(file_merge_w), DEF_WIDTH, DEF_HEIGHT);
+#else
+  gtk_window_set_default_size(GTK_WINDOW(file_merge_w), DEF_WIDTH, DEF_HEIGHT);
+#endif
 
 #if GTK_MAJOR_VERSION < 2
   /* Accelerator group for the accelerators (or, as they're called in
@@ -445,12 +822,33 @@ file_merge_cmd(GtkWidget *w)
     break;
   }
     
+  main_hb = gtk_hbox_new(FALSE, 3);
+  file_selection_set_extra_widget(file_merge_w, main_hb);
+  gtk_widget_show(main_hb);
+
   /* Container for each row of widgets */
   main_vb = gtk_vbox_new(FALSE, 3);
   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
-  file_selection_set_extra_widget(file_merge_w, main_vb);
+  gtk_box_pack_start(GTK_BOX(main_hb), main_vb, FALSE, FALSE, 0);
   gtk_widget_show(main_vb);
 
+  /* File type row */
+  range_tb = NULL;
+  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("Merged output 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);
+
   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);
@@ -476,6 +874,9 @@ file_merge_cmd(GtkWidget *w)
 #endif
 
   prepend_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "Prepend packets to existing file", accel_group);
+  gtk_tooltips_set_tip(tooltips, prepend_rb, 
+      "The resulting file contains the packets from the selected, followed by the packets from the currently loaded file,"
+      " the packet timestamps will be ignored.", NULL);
   gtk_box_pack_start(GTK_BOX(main_vb), prepend_rb, FALSE, FALSE, 0);
 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
   OBJECT_SET_DATA(file_merge_w,
@@ -486,7 +887,10 @@ file_merge_cmd(GtkWidget *w)
 #endif
   gtk_widget_show(prepend_rb);
 
-  chrono_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(prepend_rb, "Sort packets chronologically", accel_group);
+  chrono_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(prepend_rb, "Merge packets chronologically", accel_group);
+  gtk_tooltips_set_tip(tooltips, chrono_rb, 
+      "The resulting file contains all the packets from the currently loaded and the selected file,"
+      " sorted by the packet timestamps.", NULL);
   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chrono_rb), TRUE);
   gtk_box_pack_start(GTK_BOX(main_vb), chrono_rb, FALSE, FALSE, 0);
   gtk_widget_show(chrono_rb);
@@ -498,6 +902,9 @@ file_merge_cmd(GtkWidget *w)
 #endif
 
   append_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(prepend_rb, "Append packets to existing file", accel_group);
+  gtk_tooltips_set_tip(tooltips, append_rb, 
+      "The resulting file contains the packets from the currently loaded, followed by the packets from the selected file,"
+      " the packet timestamps will be ignored.", NULL);
   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(append_rb),
        g_resolv_flags & RESOLV_TRANSPORT);
   gtk_box_pack_start(GTK_BOX(main_vb), append_rb, FALSE, FALSE, 0);
@@ -512,7 +919,17 @@ file_merge_cmd(GtkWidget *w)
 
   SIGNAL_CONNECT(file_merge_w, "destroy", file_merge_destroy_cb, NULL);
 
+  /* preview widget */
+  prev = preview_new();
+  OBJECT_SET_DATA(file_merge_w, PREVIEW_TABLE_KEY, prev);
+  gtk_widget_show_all(prev);
+  gtk_box_pack_start(GTK_BOX(main_hb), prev, TRUE, TRUE, 0);
+
 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
+  SIGNAL_CONNECT(GTK_FILE_CHOOSER(file_merge_w), "selection-changed", 
+      file_open_entry_changed, file_merge_w);
+  file_open_entry_changed(file_merge_w, file_merge_w);
+
   OBJECT_SET_DATA(file_merge_w, E_DFILTER_TE_KEY,
                   OBJECT_GET_DATA(w, E_DFILTER_TE_KEY));
   if (gtk_dialog_run(GTK_DIALOG(file_merge_w)) == GTK_RESPONSE_ACCEPT)
@@ -521,6 +938,9 @@ file_merge_cmd(GtkWidget *w)
   }
   else window_destroy(file_merge_w);
 #else
+  SIGNAL_CONNECT(GTK_FILE_SELECTION(file_merge_w)->selection_entry, "changed", 
+      file_open_entry_changed, file_merge_w);
+
   /* Connect the ok_button to file_merge_ok_cb function and pass along a
      pointer to the file selection box widget */
   SIGNAL_CONNECT(GTK_FILE_SELECTION(file_merge_w)->ok_button, "clicked",
@@ -543,11 +963,11 @@ file_merge_cmd(GtkWidget *w)
 static void file_merge_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
 {
     switch(btn) {
-    case(ESD_BTN_YES):
+    case(ESD_BTN_OK):
         /* save file first */
         file_save_as_cmd(after_save_merge_dialog, data);
         break;
-    case(ESD_BTN_NO):
+    case(ESD_BTN_CANCEL):
         break;
     default:
         g_assert_not_reached();
@@ -558,11 +978,11 @@ void
 file_merge_cmd_cb(GtkWidget *widget, gpointer data _U_) {
   gpointer  dialog;
 
-  if((cfile.state != FILE_CLOSED) && !cfile.user_saved) {
+  if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
     /* user didn't saved his current file, ask him */
-    dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_YES_NO,
+    dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
                 PRIMARY_TEXT_START "Save the capture file before merging to another one?" PRIMARY_TEXT_END "\n\n"
-                "A temporary capture file cannot be merged.");
+                "A temporary capture file can't be merged.");
     simple_dialog_set_cb(dialog, file_merge_answered_cb, widget);
   } else {
     /* unchanged file, just start to merge */
@@ -571,17 +991,17 @@ file_merge_cmd_cb(GtkWidget *widget, gpointer data _U_) {
 }
 
 
-extern gboolean
-merge_two_files(char *out_filename, char *in_file0, char *in_file1, gboolean append);
-
 static void
 file_merge_ok_cb(GtkWidget *w, gpointer fs) {
-  gchar     *cf_name, *rfilter, *s;
-  gchar     *cf_current_name, *cf_merged_name;
-  GtkWidget *filter_te, *rb;
-  dfilter_t *rfcode = NULL;
-  int        err;
-  gboolean   merge_ok;
+  gchar       *cf_name, *s;
+  const gchar *rfilter;
+  GtkWidget   *filter_te, *rb;
+  dfilter_t   *rfcode = NULL;
+  int          err;
+  cf_status_t  merge_status;
+  char        *in_filenames[2];
+  int          out_fd;
+  char         tmpname[128+1];
 
 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
   cf_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
@@ -589,7 +1009,7 @@ file_merge_ok_cb(GtkWidget *w, gpointer fs) {
   cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
 #endif
   filter_te = OBJECT_GET_DATA(w, E_RFILTER_TE_KEY);
-  rfilter = (gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
+  rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
   if (!dfilter_compile(rfilter, &rfcode)) {
     bad_dfilter_alert_box(rfilter);
     g_free(cf_name);
@@ -607,46 +1027,55 @@ file_merge_ok_cb(GtkWidget *w, gpointer fs) {
        return;
   }
 
-  cf_current_name = strdup(cfile.filename);
-  /*XXX should use temp file stuff in util routines */
-  cf_merged_name = tmpnam(NULL);
+  out_fd = create_tempfile(tmpname, sizeof tmpname, "ether");
 
-  /* merge or append the files */
+  /* merge or append the two files */
   rb = OBJECT_GET_DATA(w, E_MERGE_CHRONO_KEY);
   if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rb))) {
-      /* chonological order */
-      merge_ok = merge_two_files(cf_merged_name, cf_current_name, cf_name, FALSE);
+      /* chronological order */
+      in_filenames[0] = cfile.filename;
+      in_filenames[1] = cf_name;
+      merge_status = cf_merge_files(tmpname, out_fd, 2, in_filenames,
+                                filetype, FALSE);
   } else {
       rb = OBJECT_GET_DATA(w, E_MERGE_PREPEND_KEY);
       if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rb))) {
           /* prepend file */
-          merge_ok = merge_two_files(cf_merged_name, cf_current_name, cf_name, TRUE);
+          in_filenames[0] = cfile.filename;
+          in_filenames[1] = cf_name;
+          merge_status = cf_merge_files(tmpname, out_fd, 2, in_filenames,
+                                    filetype, TRUE);
       } else {
           /* append file */
-          merge_ok = merge_two_files(cf_merged_name, cf_name, cf_current_name, TRUE);
+          in_filenames[0] = cf_name;
+          in_filenames[1] = cfile.filename;
+          merge_status = cf_merge_files(tmpname, out_fd, 2, in_filenames,
+                                    filetype, TRUE);
       }
   }
 
-  cf_close(&cfile);
-  g_free(cf_current_name);
-  cf_name = cf_merged_name;
+  g_free(cf_name);
   
-  if(!merge_ok) {
+  if (merge_status != CF_OK) {
+    close(out_fd);     /* XXX - it's already closed, right? */
     if (rfcode != NULL)
       dfilter_free(rfcode);
-    g_free(cf_name);
     return;
   }
 
-  /* Try to open the capture file. */
-  if ((err = cf_open(cf_name, FALSE, &cfile)) != 0) {
+  cf_close(&cfile);
+
+  /* We've crossed the Rubicon; get rid of the file selection box. */
+  window_destroy(GTK_WIDGET (fs));
+
+  /* Try to open the merged capture file. */
+  if (cf_open(&cfile, tmpname, TRUE /* temporary file */, &err) != CF_OK) {
     /* 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;
   }
 
@@ -654,37 +1083,30 @@ file_merge_ok_cb(GtkWidget *w, gpointer fs) {
      it closed the previous capture file, and thus destroyed any
      previous read filter attached to "cf"). */
   cfile.rfcode = rfcode;
-  cfile.is_tempfile = TRUE;
-
-  /* We've crossed the Rubicon; get rid of the file selection box. */
-  window_destroy(GTK_WIDGET (fs));
 
   switch (cf_read(&cfile)) {
 
-  case READ_SUCCESS:
-  case READ_ERROR:
+  case CF_READ_OK:
+  case CF_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:
+  case CF_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
+     if any; we can write over cf_merged_name, which is a good thing, given that
      "get_dirname()" does write over its argument. */
-  s = get_dirname(cf_name);
+  s = get_dirname(tmpname);
   set_last_open_dir(s);
   gtk_widget_grab_focus(packet_list);
-
-  g_free(cf_name);
 }
 
 static void
@@ -698,11 +1120,11 @@ file_merge_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
 void file_close_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
 {
     switch(btn) {
-    case(ESD_BTN_YES):
+    case(ESD_BTN_SAVE):
         /* save file first */
         file_save_as_cmd(after_save_close_file, NULL);
         break;
-    case(ESD_BTN_NO):
+    case(ESD_BTN_DONT_SAVE):
         cf_close(&cfile);
         break;
     case(ESD_BTN_CANCEL):
@@ -717,9 +1139,9 @@ void
 file_close_cmd_cb(GtkWidget *widget _U_, gpointer data _U_) {
   gpointer  dialog;
 
-  if((cfile.state != FILE_CLOSED) && !cfile.user_saved) {
+  if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
     /* user didn't saved his current file, ask him */
-    dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_YES_NO_CANCEL,
+    dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
                 PRIMARY_TEXT_START "Save capture file before closing it?" PRIMARY_TEXT_END "\n\n"
                 "If you close without saving, your capture data will be discarded.");
 
@@ -740,14 +1162,6 @@ file_save_cmd_cb(GtkWidget *w, gpointer data) {
   file_save_as_cmd_cb(w, data);
 }
 
-/* XXX - can we make these not be static? */
-static packet_range_t range;
-static gboolean color_marked;
-static int filetype;
-static GtkWidget *cfmark_cb;
-static GtkWidget *ft_om;
-static GtkWidget *range_tb;
-
 static gboolean
 can_save_with_wiretap(int ft)
 {
@@ -820,7 +1234,8 @@ select_file_type_cb(GtkWidget *w _U_, gpointer 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. */
-    range_set_displayed_sensitive(range_tb, can_save_with_wiretap(new_filetype));
+    if (range_tb != NULL)
+       range_set_displayed_sensitive(range_tb, can_save_with_wiretap(new_filetype));
     filetype = new_filetype;
     file_set_save_marked_sensitive();
   }
@@ -844,7 +1259,7 @@ file_set_save_marked_sensitive(void)
        
   /* 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) {
+  if (can_save_with_wiretap(filetype) && cfile.marked_count > 0) {
     range_set_marked_sensitive(range_tb, TRUE);
   }
   else {
@@ -1002,22 +1417,48 @@ file_save_as_ok_cb(GtkWidget *w _U_, gpointer fs) {
         return;
   }
 
+  /* Check whether the range is valid. */
+  if (!range_check_validity(&range)) {
+    /* The range isn't valid; 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 error, try again. */
+    g_free(cf_name);
+#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
+    /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
+     * as this will prevent the user from closing the now existing error
+     * message, simply close the dialog (this is the best we can do here). */
+    if (file_save_as_w)
+      window_destroy(GTK_WIDGET (fs));
+#else
+    gtk_widget_show(GTK_WIDGET (fs));
+#endif
+    return;
+  }
+
   /* don't show the dialog while saving */
   gtk_widget_hide(GTK_WIDGET (fs));
 
   /* Write out the packets (all, or only the ones from the current
      range) to the file with the specified name. */
-  if (! cf_save(cf_name, &cfile, &range, filetype)) {
+  if (cf_save(&cfile, cf_name, &range, filetype) != CF_OK) {
     /* The write failed; 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 error, try again. */
     g_free(cf_name);
+#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
+    /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
+     * as this will prevent the user from closing the now existing error
+     * message, simply close the dialog (this is the best we can do here). */
+    if (file_save_as_w)
+      window_destroy(GTK_WIDGET (fs));
+#else
     gtk_widget_show(GTK_WIDGET (fs));
+#endif
     return;
   }
 
   /* The write succeeded; get rid of the file selection box. */
-  /* cf_save might already closed our dialog! */
+  /* cf_save() might already closed our dialog! */
   if (file_save_as_w)
     window_destroy(GTK_WIDGET (fs));
 
@@ -1077,52 +1518,7 @@ file_save_as_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
 /* Reload a file using the current read and display filters */
 void
 file_reload_cmd_cb(GtkWidget *w _U_, gpointer data _U_) {
-  gchar *filename;
-  gboolean is_tempfile;
-
-  /* If the file could be opened, "cf_open()" calls "cf_close()"
-     to get rid of state for the old capture file before filling in state
-     for the new capture file.  "cf_close()" 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, "cf_close()" 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 (cf_open(filename, is_tempfile, &cfile) == 0) {
-    switch (cf_read(&cfile)) {
-
-    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 "cf_open()" will do that;
-       make sure it does! */
-    cfile.is_tempfile = is_tempfile;
-  }
-  /* "cf_open()" made a copy of the file name we handed it, so
-     we should free up our copy. */
-  g_free(filename);
+  cf_reload(&cfile);
 }
 
 /******************** Color Filters *********************************/