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