Update based upon latest names "packet-type-codes" list from the IANA:
[obnox/wireshark/wip.git] / gtk / capture_file_dlg.c
1 /* capture_file_dlg.c
2  * Dialog boxes for handling capture files
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32
33 #include <string.h>
34
35 #include <gtk/gtk.h>
36
37 #include "packet-range.h"
38 #include <epan/filesystem.h>
39 #include <epan/addr_resolv.h>
40 #include <epan/prefs.h>
41
42 #include "../globals.h"
43 #include "../alert_box.h"
44 #include "../simple_dialog.h"
45 #include "../color.h"
46 #include "../ui_util.h"
47 #include "../color_filters.h"
48 #include "../merge.h"
49 #include "../util.h"
50 #include <wsutil/file_util.h>
51
52 #include "gtk/gtkglobals.h"
53 #include "gtk/keys.h"
54 #include "gtk/filter_dlg.h"
55 #include "gtk/gui_utils.h"
56 #include "gtk/dlg_utils.h"
57 #include "gtk/file_dlg.h"
58 #include "gtk/capture_file_dlg.h"
59 #include "gtk/drag_and_drop.h"
60 #include "gtk/main.h"
61 #include "gtk/menus.h"
62 #include "gtk/recent.h"
63 #include "gtk/color_dlg.h"
64 #include "gtk/new_packet_list.h"
65 #ifdef HAVE_LIBPCAP
66 #include "gtk/capture_dlg.h"
67 #endif
68 #include "gtk/stock_icons.h"
69 #include "gtk/range_utils.h"
70 #include "gtk/filter_autocomplete.h"
71
72 #if _WIN32
73 #include <gdk/gdkwin32.h>
74 #include <windows.h>
75 #include "win32/file_dlg_win32.h"
76 #endif
77
78
79 static void file_open_ok_cb(GtkWidget *w, gpointer fs);
80 static void file_open_destroy_cb(GtkWidget *win, gpointer user_data);
81 static void file_merge_ok_cb(GtkWidget *w, gpointer fs);
82 static void file_merge_destroy_cb(GtkWidget *win, gpointer user_data);
83 static void file_save_as_select_file_type_cb(GtkWidget *w, gpointer data);
84 static void file_save_as_ok_cb(GtkWidget *w, gpointer fs);
85 static void file_save_as_destroy_cb(GtkWidget *win, gpointer user_data);
86 static void file_color_import_ok_cb(GtkWidget *w, gpointer filter_list);
87 static void file_color_import_destroy_cb(GtkWidget *win, gpointer user_data);
88 static void file_color_export_ok_cb(GtkWidget *w, gpointer filter_list);
89 static void file_color_export_destroy_cb(GtkWidget *win, gpointer user_data);
90 static gint set_file_type_list(GtkWidget *combo_box, int default_file_type);
91
92 #define E_FILE_TYPE_COMBO_BOX_KEY "file_type_combo_box"
93 #define E_COMPRESSED_CB_KEY       "compressed_cb"
94
95 #define E_FILE_M_RESOLVE_KEY      "file_dlg_mac_resolve_key"
96 #define E_FILE_N_RESOLVE_KEY      "file_dlg_network_resolve_key"
97 #define E_FILE_T_RESOLVE_KEY      "file_dlg_transport_resolve_key"
98
99 #define E_MERGE_PREPEND_KEY       "merge_dlg_prepend_key"
100 #define E_MERGE_CHRONO_KEY            "merge_dlg_chrono_key"
101 #define E_MERGE_APPEND_KEY            "merge_dlg_append_key"
102
103
104 #define PREVIEW_TABLE_KEY       "preview_table_key"
105 #define PREVIEW_FILENAME_KEY    "preview_filename_key"
106 #define PREVIEW_FORMAT_KEY      "preview_format_key"
107 #define PREVIEW_SIZE_KEY        "preview_size_key"
108 #define PREVIEW_ELAPSED_KEY     "preview_elapsed_key"
109 #define PREVIEW_PACKETS_KEY     "preview_packets_key"
110 #define PREVIEW_FIRST_KEY       "preview_first_key"
111
112
113 /*
114  * Keep a static pointer to the current "Save Capture File As" window, if
115  * any, so that if somebody tries to do "File:Save" or "File:Save As"
116  * while there's already a "Save Capture File As" window up, we just pop
117  * up the existing one, rather than creating a new one.
118  */
119 static GtkWidget *file_save_as_w;
120
121 /* XXX - can we make these not be static? */
122 static packet_range_t  range;
123 static gboolean        color_selected;
124 static GtkWidget      *range_tb;
125
126 #define PREVIEW_STR_MAX         200
127
128
129 /* set a new filename for the preview widget */
130 static wtap *
131 preview_set_filename(GtkWidget *prev, const gchar *cf_name)
132 {
133     GtkWidget  *label;
134     wtap       *wth;
135     int         err = 0;
136     gchar      *err_info;
137     gchar       string_buff[PREVIEW_STR_MAX];
138     gint64      filesize;
139
140
141     /* init preview labels */
142     label = g_object_get_data(G_OBJECT(prev), PREVIEW_FILENAME_KEY);
143     gtk_label_set_text(GTK_LABEL(label), "-");
144     label = g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
145     gtk_label_set_text(GTK_LABEL(label), "-");
146     label = g_object_get_data(G_OBJECT(prev), PREVIEW_SIZE_KEY);
147     gtk_label_set_text(GTK_LABEL(label), "-");
148     label = g_object_get_data(G_OBJECT(prev), PREVIEW_ELAPSED_KEY);
149     gtk_label_set_text(GTK_LABEL(label), "-");
150     label = g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
151     gtk_label_set_text(GTK_LABEL(label), "-");
152     label = g_object_get_data(G_OBJECT(prev), PREVIEW_FIRST_KEY);
153     gtk_label_set_text(GTK_LABEL(label), "-");
154
155     if(!cf_name) {
156         return NULL;
157     }
158
159     label = g_object_get_data(G_OBJECT(prev), PREVIEW_FILENAME_KEY);
160     gtk_label_set_text(GTK_LABEL(label), get_basename(cf_name));
161
162     if (test_for_directory(cf_name) == EISDIR) {
163         label = g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
164         gtk_label_set_text(GTK_LABEL(label), "directory");
165         return NULL;
166     }
167
168     wth = wtap_open_offline(cf_name, &err, &err_info, TRUE);
169     if (wth == NULL) {
170         label = g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
171         if(err == WTAP_ERR_FILE_UNKNOWN_FORMAT) {
172             gtk_label_set_text(GTK_LABEL(label), "unknown file format");
173         } else {
174             gtk_label_set_text(GTK_LABEL(label), "error opening file");
175         }
176         return NULL;
177     }
178
179     /* Find the size of the file. */
180     filesize = wtap_file_size(wth, &err);
181     if (filesize == -1) {
182         gtk_label_set_text(GTK_LABEL(label), "error getting file size");
183         wtap_close(wth);
184         return NULL;
185     }
186     g_snprintf(string_buff, PREVIEW_STR_MAX, "%" G_GINT64_MODIFIER "d bytes", filesize);
187     label = g_object_get_data(G_OBJECT(prev), PREVIEW_SIZE_KEY);
188     gtk_label_set_text(GTK_LABEL(label), string_buff);
189
190     /* type */
191     g_strlcpy(string_buff, wtap_file_type_string(wtap_file_type(wth)), PREVIEW_STR_MAX);
192     label = g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
193     gtk_label_set_text(GTK_LABEL(label), string_buff);
194
195     return wth;
196 }
197
198
199 /* do a preview run on the currently selected capture file */
200 static void
201 preview_do(GtkWidget *prev, wtap *wth)
202 {
203     GtkWidget  *label;
204     unsigned int elapsed_time;
205     time_t      time_preview;
206     time_t      time_current;
207     int         err = 0;
208     gchar      *err_info;
209     gint64      data_offset;
210     const struct wtap_pkthdr *phdr;
211     double      start_time = 0; /* seconds, with nsec resolution */
212     double      stop_time = 0;  /* seconds, with nsec resolution */
213     double      cur_time;
214     unsigned int packets = 0;
215     gboolean    is_breaked = FALSE;
216     gchar       string_buff[PREVIEW_STR_MAX];
217     time_t      ti_time;
218     struct tm  *ti_tm;
219
220
221     time(&time_preview);
222     while ( (wtap_read(wth, &err, &err_info, &data_offset)) ) {
223         phdr = wtap_phdr(wth);
224         cur_time = wtap_nstime_to_sec(&phdr->ts);
225         if(packets == 0) {
226             start_time  = cur_time;
227             stop_time = cur_time;
228         }
229         if (cur_time < start_time) {
230             start_time = cur_time;
231         }
232         if (cur_time > stop_time){
233             stop_time = cur_time;
234         }
235
236         packets++;
237         if(packets%1000 == 0) {
238             /* do we have a timeout? */
239             time(&time_current);
240             if(time_current-time_preview >= (time_t) prefs.gui_fileopen_preview) {
241                 is_breaked = TRUE;
242                 break;
243             }
244         }
245     }
246
247     if(err != 0) {
248         g_snprintf(string_buff, PREVIEW_STR_MAX, "error after reading %u packets", packets);
249         label = g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
250         gtk_label_set_text(GTK_LABEL(label), string_buff);
251         wtap_close(wth);
252         return;
253     }
254
255     /* packet count */
256     if(is_breaked) {
257         g_snprintf(string_buff, PREVIEW_STR_MAX, "more than %u packets (preview timeout)", packets);
258     } else {
259         g_snprintf(string_buff, PREVIEW_STR_MAX, "%u", packets);
260     }
261     label = g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
262     gtk_label_set_text(GTK_LABEL(label), string_buff);
263
264     /* first packet */
265     ti_time = (long)start_time;
266     ti_tm = localtime( &ti_time );
267         if(ti_tm) {
268                 g_snprintf(string_buff, PREVIEW_STR_MAX,
269                                  "%04d-%02d-%02d %02d:%02d:%02d",
270                                  ti_tm->tm_year + 1900,
271                                  ti_tm->tm_mon + 1,
272                                  ti_tm->tm_mday,
273                                  ti_tm->tm_hour,
274                                  ti_tm->tm_min,
275                                  ti_tm->tm_sec);
276         } else {
277                 g_snprintf(string_buff, PREVIEW_STR_MAX, "?");
278         }
279         label = g_object_get_data(G_OBJECT(prev), PREVIEW_FIRST_KEY);
280     gtk_label_set_text(GTK_LABEL(label), string_buff);
281
282     /* elapsed time */
283     elapsed_time = (unsigned int)(stop_time-start_time);
284     if(elapsed_time/86400) {
285       g_snprintf(string_buff, PREVIEW_STR_MAX, "%02u days %02u:%02u:%02u",
286         elapsed_time/86400, elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60);
287     } else {
288       g_snprintf(string_buff, PREVIEW_STR_MAX, "%02u:%02u:%02u",
289         elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60);
290     }
291     if(is_breaked) {
292       g_snprintf(string_buff, PREVIEW_STR_MAX, "unknown");
293     }
294     label = g_object_get_data(G_OBJECT(prev), PREVIEW_ELAPSED_KEY);
295     gtk_label_set_text(GTK_LABEL(label), string_buff);
296
297     wtap_close(wth);
298 }
299
300 #if 0
301 /* as the dialog layout will look very ugly when using the file chooser preview mechanism,
302    simply use the same layout as in GTK2.0 */
303 static void
304 update_preview_cb (GtkFileChooser *file_chooser, gpointer data)
305 {
306     GtkWidget *prev = GTK_WIDGET (data);
307     char *cf_name;
308     gboolean have_preview;
309
310     cf_name = gtk_file_chooser_get_preview_filename (file_chooser);
311
312     have_preview = preview_set_filename(prev, cf_name);
313
314     g_free (cf_name);
315
316     have_preview = TRUE;
317     gtk_file_chooser_set_preview_widget_active (file_chooser, have_preview);
318 }
319 #endif
320
321
322 /* the filename text entry changed */
323 static void
324 file_open_entry_changed(GtkWidget *w _U_, gpointer file_sel)
325 {
326     GtkWidget *prev = g_object_get_data(G_OBJECT(file_sel), PREVIEW_TABLE_KEY);
327     gchar *cf_name;
328     gboolean have_preview;
329     wtap       *wth;
330
331     /* get the filename */
332     cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_sel));
333
334     /* set the filename to the preview */
335     wth = preview_set_filename(prev, cf_name);
336     have_preview = (wth != NULL);
337
338     g_free(cf_name);
339
340     /* make the preview widget sensitive */
341     gtk_widget_set_sensitive(prev, have_preview);
342
343     /*
344      * XXX - if the Open button isn't sensitive, you can't type into
345      * the location bar and select the file or directory you've typed.
346      * See
347      *
348      *  https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=1791
349      *
350      * It's not as if allowing users to click Open when they've
351      * selected a file that's not a valid capture file will cause
352      * anything worse than an error dialog, so we'll leave the Open
353      * button sensitive for now.  Perhaps making it sensitive if
354      * cf_name is NULL would also work, although I don't know whether
355      * there are any cases where it would be non-null when you've
356      * typed in the location bar.
357      *
358      * XXX - Bug 1791 also notes that, with the line removed, Bill
359      * Meier "somehow managed to get the file chooser window somewhat
360      * wedged in that neither the cancel or open buttons were responsive".
361      * That seems a bit odd, given that, without this line, we're not
362      * monkeying with the Open button's sensitivity, but...
363      */
364 #if 0
365     /* make the open/save/... dialog button sensitive */
366
367     gtk_dialog_set_response_sensitive(file_sel, GTK_RESPONSE_ACCEPT, have_preview);
368 #endif
369
370     /* do the actual preview */
371     if(have_preview)
372         preview_do(prev, wth);
373 }
374
375
376 /* copied from summary_dlg.c */
377 static GtkWidget *
378 add_string_to_table_sensitive(GtkWidget *list, guint *row, const gchar *title, const gchar *value, gboolean sensitive)
379 {
380     GtkWidget *label;
381     gchar     *indent;
382
383     if(strlen(value) != 0) {
384         indent = g_strdup_printf("   %s", title);
385     } else {
386         indent = g_strdup(title);
387     }
388     label = gtk_label_new(indent);
389     g_free(indent);
390     gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
391     gtk_widget_set_sensitive(label, sensitive);
392     gtk_table_attach_defaults(GTK_TABLE(list), label, 0, 1, *row, *row+1);
393
394     label = gtk_label_new(value);
395     gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
396     gtk_widget_set_sensitive(label, sensitive);
397     gtk_table_attach_defaults(GTK_TABLE(list), label, 1, 2, *row, *row+1);
398
399     *row = *row + 1;
400
401     return label;
402 }
403
404 static GtkWidget *
405 add_string_to_table(GtkWidget *list, guint *row, const gchar *title, const gchar *value)
406 {
407     return add_string_to_table_sensitive(list, row, title, value, TRUE);
408 }
409
410
411
412 static GtkWidget *
413 preview_new(void)
414 {
415     GtkWidget *table, *label;
416     guint         row;
417
418     table = gtk_table_new(1, 2, FALSE);
419     gtk_table_set_col_spacings(GTK_TABLE(table), 6);
420     gtk_table_set_row_spacings(GTK_TABLE(table), 3);
421     row = 0;
422
423     label = add_string_to_table(table, &row, "Filename:", "-");
424     gtk_widget_set_size_request(label, DEF_WIDTH/3, -1);
425     g_object_set_data(G_OBJECT(table), PREVIEW_FILENAME_KEY, label);
426     label = add_string_to_table(table, &row, "Format:", "-");
427     g_object_set_data(G_OBJECT(table), PREVIEW_FORMAT_KEY, label);
428     label = add_string_to_table(table, &row, "Size:", "-");
429     g_object_set_data(G_OBJECT(table), PREVIEW_SIZE_KEY, label);
430     label = add_string_to_table(table, &row, "Packets:", "-");
431     g_object_set_data(G_OBJECT(table), PREVIEW_PACKETS_KEY, label);
432     label = add_string_to_table(table, &row, "First Packet:", "-");
433     g_object_set_data(G_OBJECT(table), PREVIEW_FIRST_KEY, label);
434     label = add_string_to_table(table, &row, "Elapsed time:", "-");
435     g_object_set_data(G_OBJECT(table), PREVIEW_ELAPSED_KEY, label);
436
437     return table;
438 }
439
440 /*
441  * Keep a static pointer to the current "Open Capture File" window, if
442  * any, so that if somebody tries to do "File:Open" while there's already
443  * an "Open Capture File" window up, we just pop up the existing one,
444  * rather than creating a new one.
445  */
446 static GtkWidget *file_open_w;
447
448 /* Open a file */
449 static void
450 file_open_cmd(GtkWidget *w)
451 {
452 #if _WIN32
453   win32_open_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)));
454 #else /* _WIN32 */
455   GtkWidget     *main_hb, *main_vb, *filter_hbox, *filter_bt, *filter_te,
456                 *m_resolv_cb, *n_resolv_cb, *t_resolv_cb, *prev;
457   /* No Apply button, and "OK" just sets our text widget, it doesn't
458      activate it (i.e., it doesn't cause us to try to open the file). */
459   static construct_args_t args = {
460         "Wireshark: Read Filter",
461         FALSE,
462         FALSE,
463     TRUE
464   };
465
466   if (file_open_w != NULL) {
467     /* There's already an "Open Capture File" dialog box; reactivate it. */
468     reactivate_window(file_open_w);
469     return;
470   }
471
472   file_open_w = file_selection_new("Wireshark: Open Capture File",
473                                    FILE_SELECTION_OPEN);
474   /* it's annoying, that the file chooser dialog is already shown here,
475      so we cannot use the correct gtk_window_set_default_size() to resize it */
476   gtk_widget_set_size_request(file_open_w, DEF_WIDTH, DEF_HEIGHT);
477
478   switch (prefs.gui_fileopen_style) {
479
480   case FO_STYLE_LAST_OPENED:
481     /* The user has specified that we should start out in the last directory
482        we looked in.  If we've already opened a file, use its containing
483        directory, if we could determine it, as the directory, otherwise
484        use the "last opened" directory saved in the preferences file if
485        there was one. */
486     /* This is now the default behaviour in file_selection_new() */
487     break;
488
489   case FO_STYLE_SPECIFIED:
490     /* The user has specified that we should always start out in a
491        specified directory; if they've specified that directory,
492        start out by showing the files in that dir. */
493     if (prefs.gui_fileopen_dir[0] != '\0')
494       file_selection_set_current_folder(file_open_w, prefs.gui_fileopen_dir);
495     break;
496   }
497
498
499   main_hb = gtk_hbox_new(FALSE, 3);
500   file_selection_set_extra_widget(file_open_w, main_hb);
501   gtk_widget_show(main_hb);
502
503   /* Container for each row of widgets */
504   main_vb = gtk_vbox_new(FALSE, 3);
505   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
506   gtk_box_pack_start(GTK_BOX(main_hb), main_vb, FALSE, FALSE, 0);
507   gtk_widget_show(main_vb);
508
509   /* filter row */
510   filter_hbox = gtk_hbox_new(FALSE, 1);
511   gtk_container_set_border_width(GTK_CONTAINER(filter_hbox), 0);
512   gtk_box_pack_start(GTK_BOX(main_vb), filter_hbox, FALSE, FALSE, 0);
513   gtk_widget_show(filter_hbox);
514
515   filter_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
516   g_signal_connect(filter_bt, "clicked",
517                    G_CALLBACK(display_filter_construct_cb), &args);
518   g_signal_connect(filter_bt, "destroy",
519                    G_CALLBACK(filter_button_destroy_cb), NULL);
520   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
521   gtk_widget_show(filter_bt);
522   gtk_widget_set_tooltip_text(filter_bt, "Open the \"Display Filter\" dialog, to edit/apply filters");
523
524   filter_te = gtk_entry_new();
525   g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
526   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
527   g_signal_connect(filter_te, "changed",
528                    G_CALLBACK(filter_te_syntax_check_cb), NULL);
529   g_object_set_data(G_OBJECT(filter_hbox), E_FILT_AUTOCOMP_PTR_KEY, NULL);
530   g_signal_connect(filter_te, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
531   g_signal_connect(file_open_w, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
532   colorize_filter_te_as_empty(filter_te);
533   gtk_widget_show(filter_te);
534   gtk_widget_set_tooltip_text(filter_te, "Enter a display filter.");
535
536   g_object_set_data(G_OBJECT(file_open_w), E_RFILTER_TE_KEY, filter_te);
537
538   /* resolve buttons */
539   m_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _MAC name resolution");
540   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_resolv_cb),
541         gbl_resolv_flags & RESOLV_MAC);
542   gtk_box_pack_start(GTK_BOX(main_vb), m_resolv_cb, FALSE, FALSE, 0);
543   g_object_set_data(G_OBJECT(file_open_w),
544                   E_FILE_M_RESOLVE_KEY, m_resolv_cb);
545   gtk_widget_show(m_resolv_cb);
546
547   n_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _network name resolution");
548   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(n_resolv_cb),
549         gbl_resolv_flags & RESOLV_NETWORK);
550   gtk_box_pack_start(GTK_BOX(main_vb), n_resolv_cb, FALSE, FALSE, 0);
551   gtk_widget_show(n_resolv_cb);
552   g_object_set_data(G_OBJECT(file_open_w), E_FILE_N_RESOLVE_KEY, n_resolv_cb);
553   t_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _transport name resolution");
554   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(t_resolv_cb),
555         gbl_resolv_flags & RESOLV_TRANSPORT);
556   gtk_box_pack_start(GTK_BOX(main_vb), t_resolv_cb, FALSE, FALSE, 0);
557   gtk_widget_show(t_resolv_cb);
558   g_object_set_data(G_OBJECT(file_open_w), E_FILE_T_RESOLVE_KEY, t_resolv_cb);
559
560   g_signal_connect(file_open_w, "destroy",
561                    G_CALLBACK(file_open_destroy_cb), NULL);
562
563   /* preview widget */
564   prev = preview_new();
565   g_object_set_data(G_OBJECT(file_open_w), PREVIEW_TABLE_KEY, prev);
566   gtk_widget_show_all(prev);
567   gtk_box_pack_start(GTK_BOX(main_hb), prev, TRUE, TRUE, 0);
568
569   g_signal_connect(GTK_FILE_CHOOSER(file_open_w), "selection-changed",
570                    G_CALLBACK(file_open_entry_changed), file_open_w);
571   file_open_entry_changed(file_open_w, file_open_w);
572
573   g_object_set_data(G_OBJECT(file_open_w), E_DFILTER_TE_KEY,
574                     g_object_get_data(G_OBJECT(w), E_DFILTER_TE_KEY));
575   if (gtk_dialog_run(GTK_DIALOG(file_open_w)) == GTK_RESPONSE_ACCEPT)
576   {
577     file_open_ok_cb(file_open_w, file_open_w);
578   }
579   else window_destroy(file_open_w);
580 #endif /* _WIN32 */
581 }
582
583 static void file_open_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
584 {
585     switch(btn) {
586     case(ESD_BTN_SAVE):
587         /* save file first */
588         file_save_as_cmd(after_save_open_dialog, data);
589         break;
590     case(ESD_BTN_DONT_SAVE):
591         cf_close(&cfile);
592         file_open_cmd(data);
593         break;
594     case(ESD_BTN_CANCEL):
595         break;
596     default:
597         g_assert_not_reached();
598     }
599 }
600
601 void
602 file_open_cmd_cb(GtkWidget *widget, gpointer data _U_) {
603   gpointer  dialog;
604
605   if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
606     /* user didn't save his current file, ask him */
607     dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
608                 "%sSave capture file before opening a new one?%s\n\n"
609                 "If you open a new capture file without saving, your capture data will be discarded.",
610                 simple_dialog_primary_start(), simple_dialog_primary_end());
611     simple_dialog_set_cb(dialog, file_open_answered_cb, widget);
612   } else {
613     /* unchanged file, just open a new one */
614     file_open_cmd(widget);
615   }
616 }
617
618 /* user pressed "open" button */
619 static void
620 file_open_ok_cb(GtkWidget *w, gpointer fs) {
621   gchar       *cf_name, *s;
622   const gchar *rfilter;
623   GtkWidget   *filter_te, *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
624   dfilter_t   *rfcode = NULL;
625   int          err;
626
627   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
628   filter_te = g_object_get_data(G_OBJECT(w), E_RFILTER_TE_KEY);
629   rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
630   if (!dfilter_compile(rfilter, &rfcode)) {
631     bad_dfilter_alert_box(rfilter);
632     g_free(cf_name);
633     return;
634   }
635
636   /* Perhaps the user specified a directory instead of a file.
637      Check whether they did. */
638   if (test_for_directory(cf_name) == EISDIR) {
639         /* It's a directory - set the file selection box to display that
640            directory, don't try to open the directory as a capture file. */
641         set_last_open_dir(cf_name);
642         g_free(cf_name);
643         file_selection_set_current_folder(fs, get_last_open_dir());
644         return;
645   }
646
647   /* Try to open the capture file. */
648   if (cf_open(&cfile, cf_name, FALSE, &err) != CF_OK) {
649     /* We couldn't open it; don't dismiss the open dialog box,
650        just leave it around so that the user can, after they
651        dismiss the alert box popped up for the open error,
652        try again. */
653     if (rfcode != NULL)
654       dfilter_free(rfcode);
655     g_free(cf_name);
656
657     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
658      * as this will prevent the user from closing the now existing error
659      * message, simply close the dialog (this is the best we can do here). */
660     if (file_open_w)
661       window_destroy(file_open_w);
662
663     return;
664   }
665
666   /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
667      it closed the previous capture file, and thus destroyed any
668      previous read filter attached to "cf"). */
669   cfile.rfcode = rfcode;
670
671   /* Set the global resolving variable */
672   gbl_resolv_flags = prefs.name_resolve;
673   m_resolv_cb = g_object_get_data(G_OBJECT(w), E_FILE_M_RESOLVE_KEY);
674   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (m_resolv_cb)))
675     gbl_resolv_flags |= RESOLV_MAC;
676   else
677     gbl_resolv_flags &= ~RESOLV_MAC;
678   n_resolv_cb = g_object_get_data(G_OBJECT(w), E_FILE_N_RESOLVE_KEY);
679   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (n_resolv_cb)))
680     gbl_resolv_flags |= RESOLV_NETWORK;
681   else
682     gbl_resolv_flags &= ~RESOLV_NETWORK;
683   t_resolv_cb = g_object_get_data(G_OBJECT(w), E_FILE_T_RESOLVE_KEY);
684   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (t_resolv_cb)))
685     gbl_resolv_flags |= RESOLV_TRANSPORT;
686   else
687     gbl_resolv_flags &= ~RESOLV_TRANSPORT;
688
689   /* We've crossed the Rubicon; get rid of the file selection box. */
690   window_destroy(GTK_WIDGET (fs));
691
692   switch (cf_read(&cfile, FALSE)) {
693
694   case CF_READ_OK:
695   case CF_READ_ERROR:
696     /* Just because we got an error, that doesn't mean we were unable
697        to read any of the file; we handle what we could get from the
698        file. */
699     break;
700
701   case CF_READ_ABORTED:
702     /* The user bailed out of re-reading the capture file; the
703        capture file has been closed - just free the capture file name
704        string and return (without changing the last containing
705        directory). */
706     g_free(cf_name);
707     return;
708   }
709
710   /* Save the name of the containing directory specified in the path name,
711      if any; we can write over cf_name, which is a good thing, given that
712      "get_dirname()" does write over its argument. */
713   s = get_dirname(cf_name);
714   set_last_open_dir(s);
715
716   g_free(cf_name);
717 }
718
719 static void
720 file_open_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
721 {
722   /* Note that we no longer have a "Open Capture File" dialog box. */
723   file_open_w = NULL;
724 }
725
726 /*
727  * Keep a static pointer to the current "Merge Capture File" window, if
728  * any, so that if somebody tries to do "File:Merge" while there's already
729  * an "Merge Capture File" window up, we just pop up the existing one,
730  * rather than creating a new one.
731  */
732 static GtkWidget *file_merge_w;
733
734 /* Merge existing with another file */
735 static void
736 file_merge_cmd(GtkWidget *w)
737 {
738 #if _WIN32
739   win32_merge_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)));
740   new_packet_list_freeze();
741   new_packet_list_thaw();
742 #else /* _WIN32 */
743   GtkWidget     *main_hb, *main_vb, *ft_hb, *ft_lb, *ft_combo_box, *filter_hbox,
744                 *filter_bt, *filter_te, *prepend_rb, *chrono_rb,
745                 *append_rb, *prev;
746   int combo_box_item_to_select;
747   /* No Apply button, and "OK" just sets our text widget, it doesn't
748      activate it (i.e., it doesn't cause us to try to open the file). */
749   static construct_args_t args = {
750         "Wireshark: Read Filter",
751         FALSE,
752         FALSE,
753     TRUE
754   };
755
756   if (file_merge_w != NULL) {
757     /* There's already an "Merge Capture File" dialog box; reactivate it. */
758     reactivate_window(file_merge_w);
759     return;
760   }
761
762   /* Default to saving all packets, in the file's current format. */
763
764   file_merge_w = file_selection_new("Wireshark: Merge with Capture File",
765                                    FILE_SELECTION_OPEN);
766   /* it's annoying, that the file chooser dialog is already shown here,
767      so we cannot use the correct gtk_window_set_default_size() to resize it */
768   gtk_widget_set_size_request(file_merge_w, DEF_WIDTH, DEF_HEIGHT);
769
770   switch (prefs.gui_fileopen_style) {
771
772   case FO_STYLE_LAST_OPENED:
773     /* The user has specified that we should start out in the last directory
774        we looked in.  If we've already opened a file, use its containing
775        directory, if we could determine it, as the directory, otherwise
776        use the "last opened" directory saved in the preferences file if
777        there was one. */
778     /* This is now the default behaviour in file_selection_new() */
779     break;
780
781   case FO_STYLE_SPECIFIED:
782     /* The user has specified that we should always start out in a
783        specified directory; if they've specified that directory,
784        start out by showing the files in that dir. */
785     if (prefs.gui_fileopen_dir[0] != '\0')
786       file_selection_set_current_folder(file_merge_w, prefs.gui_fileopen_dir);
787     break;
788   }
789
790   main_hb = gtk_hbox_new(FALSE, 3);
791   file_selection_set_extra_widget(file_merge_w, main_hb);
792   gtk_widget_show(main_hb);
793
794   /* Container for each row of widgets */
795   main_vb = gtk_vbox_new(FALSE, 3);
796   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
797   gtk_box_pack_start(GTK_BOX(main_hb), main_vb, FALSE, FALSE, 0);
798   gtk_widget_show(main_vb);
799
800   /* File type row */
801   range_tb = NULL;
802   ft_hb = gtk_hbox_new(FALSE, 3);
803   gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
804   gtk_widget_show(ft_hb);
805
806   ft_lb = gtk_label_new("Merged output file type:");
807   gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
808   gtk_widget_show(ft_lb);
809
810   ft_combo_box = ws_combo_box_new_text_and_pointer();
811
812   /* Generate the list of file types we can save. */
813   combo_box_item_to_select = set_file_type_list(ft_combo_box, cfile.cd_t);
814   gtk_box_pack_start(GTK_BOX(ft_hb), ft_combo_box, FALSE, FALSE, 0);
815   gtk_widget_show(ft_combo_box);
816   g_object_set_data(G_OBJECT(file_merge_w), E_FILE_TYPE_COMBO_BOX_KEY, ft_combo_box);
817   ws_combo_box_set_active(GTK_COMBO_BOX(ft_combo_box), combo_box_item_to_select); /* No callback */
818
819   filter_hbox = gtk_hbox_new(FALSE, 1);
820   gtk_container_set_border_width(GTK_CONTAINER(filter_hbox), 0);
821   gtk_box_pack_start(GTK_BOX(main_vb), filter_hbox, FALSE, FALSE, 0);
822   gtk_widget_show(filter_hbox);
823
824   filter_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
825   g_signal_connect(filter_bt, "clicked",
826                    G_CALLBACK(display_filter_construct_cb), &args);
827   g_signal_connect(filter_bt, "destroy",
828                    G_CALLBACK(filter_button_destroy_cb), NULL);
829   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
830   gtk_widget_show(filter_bt);
831   gtk_widget_set_tooltip_text(filter_bt, "Open the \"Display Filter\" dialog, to edit/apply filters");
832
833   filter_te = gtk_entry_new();
834   g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
835   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
836   g_signal_connect(filter_te, "changed",
837                    G_CALLBACK(filter_te_syntax_check_cb), NULL);
838   g_object_set_data(G_OBJECT(filter_hbox), E_FILT_AUTOCOMP_PTR_KEY, NULL);
839   g_signal_connect(filter_te, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
840   g_signal_connect(file_merge_w, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
841   colorize_filter_te_as_empty(filter_te);
842   gtk_widget_show(filter_te);
843   gtk_widget_set_tooltip_text(filter_te, "Enter a display filter.");
844
845   g_object_set_data(G_OBJECT(file_merge_w), E_RFILTER_TE_KEY, filter_te);
846
847   prepend_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL,
848       "Prepend packets to existing file");
849   gtk_widget_set_tooltip_text(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.");
850   gtk_box_pack_start(GTK_BOX(main_vb), prepend_rb, FALSE, FALSE, 0);
851   g_object_set_data(G_OBJECT(file_merge_w),
852                   E_MERGE_PREPEND_KEY, prepend_rb);
853   gtk_widget_show(prepend_rb);
854
855   chrono_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(prepend_rb), "Merge packets chronologically");
856   gtk_widget_set_tooltip_text(chrono_rb, "The resulting file contains all the packets from the currently loaded and the selected file, sorted by the packet timestamps.");
857   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chrono_rb), TRUE);
858   gtk_box_pack_start(GTK_BOX(main_vb), chrono_rb, FALSE, FALSE, 0);
859   gtk_widget_show(chrono_rb);
860   g_object_set_data(G_OBJECT(file_merge_w), E_MERGE_CHRONO_KEY, chrono_rb);
861
862   append_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(prepend_rb), "Append packets to existing file");
863   gtk_widget_set_tooltip_text(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.");
864   gtk_box_pack_start(GTK_BOX(main_vb), append_rb, FALSE, FALSE, 0);
865   gtk_widget_show(append_rb);
866   g_object_set_data(G_OBJECT(file_merge_w), E_MERGE_APPEND_KEY, append_rb);
867
868   g_signal_connect(file_merge_w, "destroy",
869                    G_CALLBACK(file_merge_destroy_cb), NULL);
870
871   /* preview widget */
872   prev = preview_new();
873   g_object_set_data(G_OBJECT(file_merge_w), PREVIEW_TABLE_KEY, prev);
874   gtk_widget_show_all(prev);
875   gtk_box_pack_start(GTK_BOX(main_hb), prev, TRUE, TRUE, 0);
876
877   g_signal_connect(GTK_FILE_CHOOSER(file_merge_w), "selection-changed",
878                    G_CALLBACK(file_open_entry_changed), file_merge_w);
879   file_open_entry_changed(file_merge_w, file_merge_w);
880
881   g_object_set_data(G_OBJECT(file_merge_w), E_DFILTER_TE_KEY,
882                     g_object_get_data(G_OBJECT(w), E_DFILTER_TE_KEY));
883   if (gtk_dialog_run(GTK_DIALOG(file_merge_w)) == GTK_RESPONSE_ACCEPT)
884   {
885     file_merge_ok_cb(file_merge_w, file_merge_w);
886   }
887   else window_destroy(file_merge_w);
888 #endif /* _WIN32 */
889 }
890
891 static void file_merge_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
892 {
893     switch(btn) {
894     case(ESD_BTN_OK):
895         /* save file first */
896         file_save_as_cmd(after_save_merge_dialog, data);
897         break;
898     case(ESD_BTN_CANCEL):
899         break;
900     default:
901         g_assert_not_reached();
902     }
903 }
904
905 void
906 file_merge_cmd_cb(GtkWidget *widget, gpointer data _U_) {
907   gpointer  dialog;
908
909   if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
910     /* user didn't saved his current file, ask him */
911     dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
912                 "%sSave the capture file before merging to another one?%s\n\n"
913                 "A temporary capture file can't be merged.",
914                 simple_dialog_primary_start(), simple_dialog_primary_end());
915     simple_dialog_set_cb(dialog, file_merge_answered_cb, widget);
916   } else {
917     /* unchanged file, just start to merge */
918     file_merge_cmd(widget);
919   }
920 }
921
922
923 static void
924 file_merge_ok_cb(GtkWidget *w, gpointer fs) {
925   gchar       *cf_name, *s;
926   const gchar *rfilter;
927   GtkWidget   *ft_combo_box, *filter_te, *rb;
928   dfilter_t   *rfcode = NULL;
929   int          err;
930   cf_status_t  merge_status;
931   char        *in_filenames[2];
932   char        *tmpname;
933   gpointer     ptr;
934   int          file_type;
935
936
937   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
938   filter_te = g_object_get_data(G_OBJECT(w), E_RFILTER_TE_KEY);
939   rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
940   if (!dfilter_compile(rfilter, &rfcode)) {
941     bad_dfilter_alert_box(rfilter);
942     g_free(cf_name);
943     return;
944   }
945
946   ft_combo_box  = g_object_get_data(G_OBJECT(w), E_FILE_TYPE_COMBO_BOX_KEY);
947   if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(ft_combo_box), &ptr)) {
948       g_assert_not_reached();  /* Programming error: somehow nothing is active */
949   }
950   file_type = GPOINTER_TO_INT(ptr);
951
952   /* Perhaps the user specified a directory instead of a file.
953      Check whether they did. */
954   if (test_for_directory(cf_name) == EISDIR) {
955         /* It's a directory - set the file selection box to display that
956            directory, don't try to open the directory as a capture file. */
957         set_last_open_dir(cf_name);
958         g_free(cf_name);
959         file_selection_set_current_folder(fs, get_last_open_dir());
960         return;
961   }
962
963   /* merge or append the two files */
964   rb = g_object_get_data(G_OBJECT(w), E_MERGE_CHRONO_KEY);
965   tmpname = NULL;
966   if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rb))) {
967       /* chronological order */
968       in_filenames[0] = cfile.filename;
969       in_filenames[1] = cf_name;
970       merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
971   } else {
972       rb = g_object_get_data(G_OBJECT(w), E_MERGE_PREPEND_KEY);
973       if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rb))) {
974           /* prepend file */
975           in_filenames[0] = cf_name;
976           in_filenames[1] = cfile.filename;
977           merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type,
978                                         TRUE);
979       } else {
980           /* append file */
981           in_filenames[0] = cfile.filename;
982           in_filenames[1] = cf_name;
983           merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type,
984                                         TRUE);
985       }
986   }
987
988   g_free(cf_name);
989
990   if (merge_status != CF_OK) {
991     if (rfcode != NULL)
992       dfilter_free(rfcode);
993     g_free(tmpname);
994     return;
995   }
996
997   cf_close(&cfile);
998
999   /* We've crossed the Rubicon; get rid of the file selection box. */
1000   window_destroy(GTK_WIDGET (fs));
1001
1002   /* Try to open the merged capture file. */
1003   if (cf_open(&cfile, tmpname, TRUE /* temporary file */, &err) != CF_OK) {
1004     /* We couldn't open it; don't dismiss the open dialog box,
1005        just leave it around so that the user can, after they
1006        dismiss the alert box popped up for the open error,
1007        try again. */
1008     if (rfcode != NULL)
1009       dfilter_free(rfcode);
1010     g_free(tmpname);
1011     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1012      * as this will prevent the user from closing the now existing error
1013      * message, simply close the dialog (this is the best we can do here). */
1014     if (file_open_w)
1015       window_destroy(file_open_w);
1016     return;
1017   }
1018   g_free(tmpname);
1019
1020   /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
1021      it closed the previous capture file, and thus destroyed any
1022      previous read filter attached to "cf"). */
1023   cfile.rfcode = rfcode;
1024
1025   switch (cf_read(&cfile, FALSE)) {
1026
1027   case CF_READ_OK:
1028   case CF_READ_ERROR:
1029     /* Just because we got an error, that doesn't mean we were unable
1030        to read any of the file; we handle what we could get from the
1031        file. */
1032     break;
1033
1034   case CF_READ_ABORTED:
1035     /* The user bailed out of re-reading the capture file; the
1036        capture file has been closed - just free the capture file name
1037        string and return (without changing the last containing
1038        directory). */
1039     return;
1040   }
1041
1042   /* Save the name of the containing directory specified in the path name,
1043      if any; we can write over cf_merged_name, which is a good thing, given that
1044      "get_dirname()" does write over its argument. */
1045   s = get_dirname(tmpname);
1046   set_last_open_dir(s);
1047 }
1048
1049 static void
1050 file_merge_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1051 {
1052   /* Note that we no longer have a "Merge Capture File" dialog box. */
1053   file_merge_w = NULL;
1054 }
1055
1056
1057 static void file_close_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
1058 {
1059     switch(btn) {
1060     case(ESD_BTN_SAVE):
1061         /* save file first */
1062         file_save_as_cmd(after_save_close_file, NULL);
1063         break;
1064     case(ESD_BTN_DONT_SAVE):
1065         cf_close(&cfile);
1066         break;
1067     case(ESD_BTN_CANCEL):
1068         break;
1069     default:
1070         g_assert_not_reached();
1071     }
1072 }
1073
1074 /* Close a file */
1075 void
1076 file_close_cmd_cb(GtkWidget *widget _U_, gpointer data _U_) {
1077   gpointer  dialog;
1078
1079   if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
1080     /* user didn't saved his current file, ask him */
1081     dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
1082                 "%sSave capture file before closing it?%s\n\n"
1083                 "If you close without saving, your capture data will be discarded.",
1084                 simple_dialog_primary_start(), simple_dialog_primary_end());
1085
1086     simple_dialog_set_cb(dialog, file_close_answered_cb, NULL);
1087   } else {
1088     /* unchanged file, just close it */
1089     cf_close(&cfile);
1090   }
1091 }
1092
1093 void
1094 file_save_cmd_cb(GtkWidget *w, gpointer data) {
1095   /* If the file's already been saved, do nothing.  */
1096   if (cfile.user_saved)
1097     return;
1098
1099   /* Do a "Save As". */
1100   file_save_as_cmd_cb(w, data);
1101 }
1102
1103 static gboolean
1104 can_save_with_wiretap(int ft)
1105 {
1106   /* To save a file with Wiretap, Wiretap has to handle that format,
1107      and its code to handle that format must be able to write a file
1108      with this file's encapsulation type. */
1109   return wtap_dump_can_open(ft) && wtap_dump_can_write_encap(ft, cfile.lnk_t);
1110 }
1111
1112
1113 /* Attach a list of the valid 'save as' file types to a combo_box by
1114    checking what Wiretap supports.
1115    returns: index of default entry
1116  */
1117 static gint
1118 set_file_type_list(GtkWidget *combo_box, int default_file_type)
1119 {
1120   int   ft;
1121   guint idx;
1122   gint  item_to_select;
1123
1124   /* Default to the first supported file type (which should be libpcap)
1125      if this file's current type isn't supported  */
1126   item_to_select = 0;
1127
1128   /* Check all file types. */
1129   idx = 0;
1130   for (ft = 0; ft < WTAP_NUM_FILE_TYPES; ft++) {
1131     if (can_save_with_wiretap(ft)) {
1132       /* OK, we can write it out in this type. */
1133       ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(combo_box), wtap_file_type_string(ft), GINT_TO_POINTER(ft));
1134       if (ft == default_file_type) {
1135         /* Default to the same format as the file, if it's supported. */
1136         item_to_select = idx;
1137       }
1138       idx++;
1139     }
1140   }
1141
1142   return item_to_select;
1143 }
1144
1145 static void
1146 file_save_as_select_file_type_cb(GtkWidget *w, gpointer data _U_)
1147 {
1148   int new_file_type;
1149   gpointer ptr;
1150   GtkWidget *compressed_cb;
1151
1152   if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(w), &ptr)) {
1153       g_assert_not_reached();  /* Programming error: somehow nothing is active */
1154   }
1155   new_file_type = GPOINTER_TO_INT(ptr);
1156
1157   compressed_cb = g_object_get_data(G_OBJECT(file_save_as_w), E_COMPRESSED_CB_KEY);
1158   gtk_widget_set_sensitive(compressed_cb, wtap_dump_can_compress(new_file_type));
1159 }
1160
1161 /*
1162  * Update various dynamic parts of the range controls; called from outside
1163  * the file dialog code whenever the packet counts change.
1164  */
1165 void
1166 file_save_update_dynamics(void)
1167 {
1168   if (file_save_as_w == NULL) {
1169     /* We don't currently have a "Save As..." dialog box up. */
1170     return;
1171   }
1172
1173   range_update_dynamics(range_tb);
1174 }
1175
1176
1177 action_after_save_e action_after_save_g;
1178 gpointer            action_after_save_data_g;
1179
1180
1181 void
1182 file_save_as_cmd(action_after_save_e action_after_save, gpointer action_after_save_data)
1183 {
1184 #if _WIN32
1185   win32_save_as_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), action_after_save, action_after_save_data);
1186 #else /* _WIN32 */
1187   GtkWidget     *main_vb, *ft_hb, *ft_lb, *ft_combo_box, *range_fr, *compressed_cb;
1188   gint combo_box_item_to_select;
1189
1190   if (file_save_as_w != NULL) {
1191     /* There's already an "Save Capture File As" dialog box; reactivate it. */
1192     reactivate_window(file_save_as_w);
1193     return;
1194   }
1195
1196   /* Default to saving all packets, in the file's current format. */
1197
1198   /* init the packet range */
1199   packet_range_init(&range);
1200
1201   /* build the file selection */
1202   file_save_as_w = file_selection_new ("Wireshark: Save Capture File As",
1203                                        FILE_SELECTION_SAVE);
1204
1205   /* as the dialog might already be gone, when using this values, we cannot
1206    * set data to the dialog object, but keep global values */
1207   action_after_save_g       = action_after_save;
1208   action_after_save_data_g  = action_after_save_data;
1209
1210   /* Container for each row of widgets */
1211
1212   main_vb = gtk_vbox_new(FALSE, 5);
1213   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
1214   file_selection_set_extra_widget(file_save_as_w, main_vb);
1215   gtk_widget_show(main_vb);
1216
1217   /*** Packet Range frame ***/
1218   range_fr = gtk_frame_new("Packet Range");
1219   gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 0);
1220   gtk_widget_show(range_fr);
1221
1222   /* range table */
1223   range_tb = range_new(&range);
1224   gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
1225   gtk_widget_show(range_tb);
1226
1227   /* File type row */
1228   ft_hb = gtk_hbox_new(FALSE, 3);
1229   gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
1230   gtk_widget_show(ft_hb);
1231
1232   ft_lb = gtk_label_new("File type:");
1233   gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
1234   gtk_widget_show(ft_lb);
1235
1236   ft_combo_box = ws_combo_box_new_text_and_pointer();
1237
1238   /* Generate the list of file types we can save. */
1239   combo_box_item_to_select = set_file_type_list(ft_combo_box, cfile.cd_t);
1240   gtk_box_pack_start(GTK_BOX(ft_hb), ft_combo_box, FALSE, FALSE, 0);
1241   gtk_widget_show(ft_combo_box);
1242   g_object_set_data(G_OBJECT(file_save_as_w), E_FILE_TYPE_COMBO_BOX_KEY, ft_combo_box);
1243
1244   /* dynamic values in the range frame */
1245   range_update_dynamics(range_tb);
1246
1247   /* compressed */
1248   compressed_cb = gtk_check_button_new_with_label("Compress with gzip");
1249   gtk_container_add(GTK_CONTAINER(ft_hb), compressed_cb);
1250   /* XXX - disable output compression for now, as this doesn't work with the
1251    * current optimization to simply copy a capture file if it's using the same
1252    * encapsulation ... */
1253   /* the rest of the implementation is just working fine :-( */
1254 #if 0
1255   gtk_widget_show(compressed_cb);
1256 #endif
1257   g_object_set_data(G_OBJECT(file_save_as_w), E_COMPRESSED_CB_KEY, compressed_cb);
1258   /* Ok: now "select" the default filetype which invokes file_save_as_select_file_type_cb */
1259   g_signal_connect(ft_combo_box, "changed", G_CALLBACK(file_save_as_select_file_type_cb), NULL);
1260   ws_combo_box_set_active(GTK_COMBO_BOX(ft_combo_box), combo_box_item_to_select);
1261
1262   g_signal_connect(file_save_as_w, "destroy",
1263                    G_CALLBACK(file_save_as_destroy_cb), NULL);
1264
1265   if (gtk_dialog_run(GTK_DIALOG(file_save_as_w)) == GTK_RESPONSE_ACCEPT) {
1266     file_save_as_ok_cb(file_save_as_w, file_save_as_w);
1267   } else {
1268     window_destroy(file_save_as_w);
1269   }
1270 #endif /* _WIN32 */
1271 }
1272
1273 void
1274 file_save_as_cmd_cb(GtkWidget *w _U_, gpointer data _U_)
1275 {
1276   file_save_as_cmd(after_save_no_action, NULL);
1277 }
1278
1279
1280 /* all tests ok, we only have to save the file */
1281 /* (and probably continue with a pending operation) */
1282 static void
1283 file_save_as_cb(GtkWidget *w _U_, gpointer fs) {
1284   GtkWidget *ft_combo_box;
1285   GtkWidget *compressed_cb;
1286   gchar     *cf_name;
1287   gchar     *dirname;
1288   gpointer   ptr;
1289   int        file_type;
1290
1291   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
1292
1293   compressed_cb = g_object_get_data(G_OBJECT(fs), E_COMPRESSED_CB_KEY);
1294   ft_combo_box  = g_object_get_data(G_OBJECT(fs), E_FILE_TYPE_COMBO_BOX_KEY);
1295
1296   if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(ft_combo_box), &ptr)) {
1297       g_assert_not_reached();  /* Programming error: somehow nothing is active */
1298   }
1299   file_type = GPOINTER_TO_INT(ptr);
1300
1301   /* XXX - if the user requests to save to an already existing filename, */
1302   /* ask in a dialog if that's intended */
1303   /* currently, cf_save() will simply deny it */
1304
1305   /* Write out the packets (all, or only the ones from the current
1306      range) to the file with the specified name. */
1307   if (cf_save(&cfile, cf_name, &range, file_type,
1308           gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compressed_cb))) != CF_OK) {
1309     /* The write failed; don't dismiss the open dialog box,
1310        just leave it around so that the user can, after they
1311        dismiss the alert box popped up for the error, try again. */
1312     g_free(cf_name);
1313     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1314      * as this will prevent the user from closing the now existing error
1315      * message, simply close the dialog (this is the best we can do here). */
1316     if (file_save_as_w)
1317       window_destroy(GTK_WIDGET (fs));
1318     return;
1319   }
1320
1321   /* The write succeeded; get rid of the file selection box. */
1322   /* cf_save() might already closed our dialog! */
1323   if (file_save_as_w)
1324     window_destroy(GTK_WIDGET (fs));
1325
1326   /* Save the directory name for future file dialogs. */
1327   dirname = get_dirname(cf_name);  /* Overwrites cf_name */
1328   set_last_open_dir(dirname);
1329   g_free(cf_name);
1330
1331   /* we have finished saving, do we have pending things to do? */
1332   switch(action_after_save_g) {
1333   case(after_save_no_action):
1334       break;
1335   case(after_save_open_dialog):
1336       file_open_cmd(action_after_save_data_g);
1337       break;
1338   case(after_save_open_recent_file):
1339       menu_open_recent_file_cmd(action_after_save_data_g);
1340       break;
1341   case(after_save_open_dnd_file):
1342       dnd_open_file_cmd(action_after_save_data_g);
1343       break;
1344   case(after_save_merge_dialog):
1345       file_merge_cmd(action_after_save_data_g);
1346       break;
1347 #ifdef HAVE_LIBPCAP
1348   case(after_save_capture_dialog):
1349       capture_start_confirmed();
1350       break;
1351 #endif
1352   case(after_save_close_file):
1353       cf_close(&cfile);
1354       break;
1355   case(after_save_exit):
1356       main_do_quit();
1357       break;
1358   default:
1359       g_assert_not_reached();
1360   }
1361
1362   action_after_save_g = after_save_no_action;
1363 }
1364
1365
1366 static void file_save_as_exists_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
1367 {
1368     gchar       *cf_name;
1369
1370     cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(data));
1371
1372     switch(btn) {
1373     case(ESD_BTN_OK):
1374         /* save file */
1375         ws_unlink(cf_name);
1376         file_save_as_cb(NULL, data);
1377         break;
1378     case(ESD_BTN_CANCEL):
1379         /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1380          * as this will prevent the user from closing the now existing error
1381          * message, simply close the dialog (this is the best we can do here). */
1382         if (file_save_as_w)
1383             window_destroy(file_save_as_w);
1384         break;
1385     default:
1386         g_assert_not_reached();
1387     }
1388     g_free(cf_name);
1389 }
1390
1391
1392 /* user pressed "Save" dialog "Ok" button */
1393 static void
1394 file_save_as_ok_cb(GtkWidget *w _U_, gpointer fs) {
1395   gchar *cf_name;
1396   gpointer  dialog;
1397
1398   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
1399
1400   /* Perhaps the user specified a directory instead of a file.
1401      Check whether they did. */
1402   if (test_for_directory(cf_name) == EISDIR) {
1403         /* It's a directory - set the file selection box to display that
1404            directory, and leave the selection box displayed. */
1405         set_last_open_dir(cf_name);
1406         g_free(cf_name);
1407         file_selection_set_current_folder(fs, get_last_open_dir());
1408         return;
1409   }
1410
1411   /* Check whether the range is valid. */
1412   if (!range_check_validity(&range)) {
1413     /* The range isn't valid; don't dismiss the open dialog box,
1414        just leave it around so that the user can, after they
1415        dismiss the alert box popped up for the error, try again. */
1416     g_free(cf_name);
1417     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1418      * as this will prevent the user from closing the now existing error
1419      * message, simply close the dialog (this is the best we can do here). */
1420     if (file_save_as_w)
1421       window_destroy(GTK_WIDGET (fs));
1422
1423     return;
1424   }
1425
1426   /*
1427    * Check that the from file is not the same as to file
1428    * We do it here so we catch all cases ...
1429    * Unfortunately, the file requester gives us an absolute file
1430    * name and the read file name may be relative (if supplied on
1431    * the command line). From Joerg Mayer.
1432    */
1433   if (files_identical(cfile.filename, cf_name)) {
1434     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1435       "%sCapture file: \"%s\" identical to loaded file!%s\n\n"
1436       "Please choose a different filename.",
1437       simple_dialog_primary_start(), cf_name, simple_dialog_primary_end());
1438     g_free(cf_name);
1439     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1440      * as this will prevent the user from closing the now existing error
1441      * message, simply close the dialog (this is the best we can do here). */
1442     if (file_save_as_w)
1443       window_destroy(GTK_WIDGET (fs));
1444
1445     return;
1446   }
1447
1448   /* don't show the dialog while saving (or asking) */
1449   gtk_widget_hide(GTK_WIDGET (fs));
1450
1451   /* it the file doesn't exist, simply try to save it */
1452   if (!file_exists(cf_name)) {
1453     file_save_as_cb(NULL, fs);
1454     g_free(cf_name);
1455     return;
1456   }
1457
1458   /* the file exists, ask the user to remove it first */
1459   dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
1460       "%sA file named \"%s\" already exists.%s\n\n"
1461       "Do you want to replace it with the capture you are saving?",
1462       simple_dialog_primary_start(), cf_name, simple_dialog_primary_end());
1463   simple_dialog_set_cb(dialog, file_save_as_exists_answered_cb, fs);
1464
1465   g_free(cf_name);
1466 }
1467
1468 void
1469 file_save_as_destroy(void)
1470 {
1471   if (file_save_as_w)
1472     window_destroy(file_save_as_w);
1473 }
1474
1475 static void
1476 file_save_as_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1477 {
1478   /* Note that we no longer have a "Save Capture File As" dialog box. */
1479   file_save_as_w = NULL;
1480 }
1481
1482 /* Reload a file using the current read and display filters */
1483 void
1484 file_reload_cmd_cb(GtkWidget *w _U_, gpointer data _U_) {
1485   cf_reload(&cfile);
1486 }
1487
1488 /******************** Color Filters *********************************/
1489 /*
1490  * Keep a static pointer to the current "Color Export" window, if
1491  * any, so that if somebody tries to do "Export"
1492  * while there's already a "Color Export" window up, we just pop
1493  * up the existing one, rather than creating a new one.
1494  */
1495 static GtkWidget *file_color_import_w;
1496
1497 /* sets the file path to the global color filter file.
1498    WARNING: called by both the import and the export dialog.
1499 */
1500 static void
1501 color_global_cb(GtkWidget *widget _U_, gpointer data)
1502 {
1503   GtkWidget *fs_widget = data;
1504   gchar *path;
1505
1506   /* decide what file to open (from dfilter code) */
1507   path = get_datafile_path("colorfilters");
1508
1509   gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(fs_widget), path);
1510
1511   g_free(path);
1512 }
1513
1514 /* Import color filters */
1515 void
1516 file_color_import_cmd_cb(GtkWidget *color_filters, gpointer filter_list _U_)
1517 {
1518 #if _WIN32
1519   win32_import_color_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), color_filters);
1520 #else /* _WIN32 */
1521   GtkWidget     *main_vb, *cfglobal_but;
1522
1523   /* No Apply button, and "OK" just sets our text widget, it doesn't
1524      activate it (i.e., it doesn't cause us to try to open the file). */
1525
1526   if (file_color_import_w != NULL) {
1527     /* There's already an "Import Color Filters" dialog box; reactivate it. */
1528     reactivate_window(file_color_import_w);
1529     return;
1530   }
1531
1532   file_color_import_w = file_selection_new("Wireshark: Import Color Filters",
1533                                            FILE_SELECTION_OPEN);
1534
1535   /* Container for each row of widgets */
1536   main_vb = gtk_vbox_new(FALSE, 3);
1537   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
1538   file_selection_set_extra_widget(file_color_import_w, main_vb);
1539   gtk_widget_show(main_vb);
1540
1541
1542   cfglobal_but = gtk_button_new_with_label("Global Color Filter File");
1543   gtk_container_add(GTK_CONTAINER(main_vb), cfglobal_but);
1544   g_signal_connect(cfglobal_but, "clicked",
1545                    G_CALLBACK(color_global_cb), file_color_import_w);
1546   gtk_widget_show(cfglobal_but);
1547
1548   g_signal_connect(file_color_import_w, "destroy",
1549                    G_CALLBACK(file_color_import_destroy_cb), NULL);
1550
1551
1552   if (gtk_dialog_run(GTK_DIALOG(file_color_import_w)) == GTK_RESPONSE_ACCEPT)
1553   {
1554       file_color_import_ok_cb(file_color_import_w, color_filters);
1555   }
1556   else window_destroy(file_color_import_w);
1557 #endif /* _WIN32 */
1558 }
1559
1560 static void
1561 file_color_import_ok_cb(GtkWidget *w, gpointer color_filters) {
1562   gchar     *cf_name, *s;
1563   GtkWidget *fs = gtk_widget_get_toplevel(w);
1564
1565   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
1566
1567   /* Perhaps the user specified a directory instead of a file.
1568      Check whether they did. */
1569   if (test_for_directory(cf_name) == EISDIR) {
1570         /* It's a directory - set the file selection box to display that
1571            directory, don't try to open the directory as a color filter file. */
1572         set_last_open_dir(cf_name);
1573         g_free(cf_name);
1574         file_selection_set_current_folder(fs, get_last_open_dir());
1575         return;
1576   }
1577
1578   /* Try to open the color filter file. */
1579
1580   if (!color_filters_import(cf_name, color_filters)) {
1581     /* We couldn't open it; don't dismiss the open dialog box,
1582        just leave it around so that the user can, after they
1583        dismiss the alert box popped up for the open error,
1584        try again. */
1585     g_free(cf_name);
1586     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1587      * as this will prevent the user from closing the now existing error
1588      * message, simply close the dialog (this is the best we can do here). */
1589     window_destroy(GTK_WIDGET (fs));
1590
1591     return;
1592   }
1593
1594   /* We've crossed the Rubicon; get rid of the file selection box. */
1595   window_destroy(GTK_WIDGET (fs));
1596
1597   /* Save the name of the containing directory specified in the path name,
1598      if any; we can write over cf_name, which is a good thing, given that
1599      "get_dirname()" does write over its argument. */
1600   s = get_dirname(cf_name);
1601   set_last_open_dir(s);
1602
1603   g_free(cf_name);
1604 }
1605
1606 static void
1607 file_color_import_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1608 {
1609   /* Note that we no longer have a "Open Capture File" dialog box. */
1610   file_color_import_w = NULL;
1611 }
1612
1613 static GtkWidget *file_color_export_w;
1614 /*
1615  * Set the "Export only selected filters" toggle button as appropriate for
1616  * the current output file type and count of selected filters.
1617  *
1618  * Called when the "Export" dialog box is created and when the selected
1619  * count changes.
1620  */
1621 static void
1622 color_set_export_selected_sensitive(GtkWidget * cfselect_cb)
1623 {
1624   if (file_color_export_w == NULL) {
1625     /* We don't currently have an "Export" dialog box up. */
1626     return;
1627   }
1628
1629   /* We can request that only the selected filters be saved only if
1630         there *are* selected filters. */
1631   if (color_selected_count() != 0)
1632     gtk_widget_set_sensitive(cfselect_cb, TRUE);
1633   else {
1634     /* Force the "Export only selected filters" toggle to "false", turn
1635        off the flag it controls. */
1636     color_selected = FALSE;
1637     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfselect_cb), FALSE);
1638     gtk_widget_set_sensitive(cfselect_cb, FALSE);
1639   }
1640 }
1641
1642 static void
1643 color_toggle_selected_cb(GtkWidget *widget, gpointer data _U_)
1644 {
1645   color_selected = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
1646 }
1647
1648 void
1649 file_color_export_cmd_cb(GtkWidget *w _U_, gpointer filter_list)
1650 {
1651 #if _WIN32
1652   win32_export_color_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), filter_list);
1653 #else /* _WIN32 */
1654   GtkWidget *main_vb, *cfglobal_but;
1655   GtkWidget *cfselect_cb;
1656
1657   if (file_color_export_w != NULL) {
1658     /* There's already an "Color Filter Export" dialog box; reactivate it. */
1659     reactivate_window(file_color_export_w);
1660     return;
1661   }
1662
1663   color_selected   = FALSE;
1664
1665   file_color_export_w = file_selection_new("Wireshark: Export Color Filters",
1666                                            FILE_SELECTION_SAVE);
1667
1668   /* Container for each row of widgets */
1669   main_vb = gtk_vbox_new(FALSE, 3);
1670   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
1671   file_selection_set_extra_widget(file_color_export_w, main_vb);
1672   gtk_widget_show(main_vb);
1673
1674   cfselect_cb = gtk_check_button_new_with_label("Export only selected filters");
1675   gtk_container_add(GTK_CONTAINER(main_vb), cfselect_cb);
1676   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfselect_cb), FALSE);
1677   g_signal_connect(cfselect_cb, "toggled",
1678                    G_CALLBACK(color_toggle_selected_cb), NULL);
1679   gtk_widget_show(cfselect_cb);
1680   color_set_export_selected_sensitive(cfselect_cb);
1681
1682   cfglobal_but = gtk_button_new_with_label("Global Color Filter File");
1683   gtk_container_add(GTK_CONTAINER(main_vb), cfglobal_but);
1684   g_signal_connect(cfglobal_but, "clicked",
1685                    G_CALLBACK(color_global_cb), file_color_export_w);
1686   gtk_widget_show(cfglobal_but);
1687
1688   g_signal_connect(file_color_export_w, "destroy",
1689                    G_CALLBACK(file_color_export_destroy_cb), NULL);
1690
1691   if (gtk_dialog_run(GTK_DIALOG(file_color_export_w)) == GTK_RESPONSE_ACCEPT)
1692   {
1693       file_color_export_ok_cb(file_color_export_w, filter_list);
1694   }
1695   else window_destroy(file_color_export_w);
1696 #endif /* _WIN32 */
1697 }
1698
1699 static void
1700 file_color_export_ok_cb(GtkWidget *w, gpointer filter_list) {
1701   gchar *cf_name;
1702   gchar *dirname;
1703   GtkWidget *fs = gtk_widget_get_toplevel(w);
1704
1705   cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
1706
1707   /* Perhaps the user specified a directory instead of a file.
1708      Check whether they did. */
1709   if (test_for_directory(cf_name) == EISDIR) {
1710         /* It's a directory - set the file selection box to display that
1711            directory, and leave the selection box displayed. */
1712         set_last_open_dir(cf_name);
1713         g_free(cf_name);
1714         file_selection_set_current_folder(fs, get_last_open_dir());
1715         return;
1716   }
1717
1718   /* Write out the filters (all, or only the ones that are currently
1719      displayed or selected) to the file with the specified name. */
1720
1721    if (!color_filters_export(cf_name, filter_list, color_selected))
1722    {
1723     /* The write failed; don't dismiss the open dialog box,
1724        just leave it around so that the user can, after they
1725        dismiss the alert box popped up for the error, try again. */
1726        g_free(cf_name);
1727
1728       /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1729        * as this will prevent the user from closing the now existing error
1730        * message, simply close the dialog (this is the best we can do here). */
1731        window_destroy(GTK_WIDGET (fs));
1732
1733        return;
1734    }
1735
1736   /* The write succeeded; get rid of the file selection box. */
1737   window_destroy(GTK_WIDGET (fs));
1738
1739   /* Save the directory name for future file dialogs. */
1740   dirname = get_dirname(cf_name);  /* Overwrites cf_name */
1741   set_last_open_dir(dirname);
1742   g_free(cf_name);
1743 }
1744
1745 static void
1746 file_color_export_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1747 {
1748   file_color_export_w = NULL;
1749 }