Add another wrapper routine, "file_selection_set_extra_widget()", to set
[obnox/wireshark/wip.git] / gtk / file_dlg.c
1 /* file_dlg.c
2  * Dialog boxes for handling files
3  *
4  * $Id: file_dlg.c,v 1.102 2004/03/29 22:55:13 guy Exp $
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 <gtk/gtk.h>
30
31 #include "range.h"
32 #include <epan/filesystem.h>
33
34 #include "globals.h"
35 #include "gtkglobals.h"
36 #include <epan/resolv.h>
37 #include "keys.h"
38 #include "filter_prefs.h"
39 #include "ui_util.h"
40 #include "alert_box.h"
41 #include "simple_dialog.h"
42 #include "menu.h"
43 #include "file_dlg.h"
44 #include "dlg_utils.h"
45 #include "main.h"
46 #include "compat_macros.h"
47 #include "prefs.h"
48 #include "recent.h"
49 #include "color.h"
50 #include "../ui_util.h"
51 #include "gtk/color_filters.h"
52 #include "gtk/color_dlg.h"
53 #ifdef HAVE_LIBPCAP
54 #include "capture_dlg.h"
55 #endif
56
57
58 static void file_open_ok_cb(GtkWidget *w, gpointer fs);
59 static void file_open_destroy_cb(GtkWidget *win, gpointer user_data);
60 static void select_file_type_cb(GtkWidget *w, gpointer data);
61 static void file_save_as_ok_cb(GtkWidget *w, gpointer fs);
62 static void file_save_as_destroy_cb(GtkWidget *win, gpointer user_data);
63 static void file_color_import_ok_cb(GtkWidget *w, gpointer fs);
64 static void file_color_import_destroy_cb(GtkWidget *win, gpointer user_data);
65 static void file_color_export_ok_cb(GtkWidget *w, gpointer fs);
66 static void file_color_export_destroy_cb(GtkWidget *win, gpointer user_data);
67 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 4) || GTK_MAJOR_VERSION < 2
68 static void file_select_ok_cb(GtkWidget *w, gpointer data);
69 static void file_select_cancel_cb(GtkWidget *w, gpointer data);
70 #endif
71 static void file_select_destroy_cb(GtkWidget *win, GtkWidget* file_te);
72 static void toggle_captured_cb(GtkWidget *widget, gpointer data _U_);
73
74 #define E_FILE_M_RESOLVE_KEY      "file_dlg_mac_resolve_key"
75 #define E_FILE_N_RESOLVE_KEY      "file_dlg_network_resolve_key"
76 #define E_FILE_T_RESOLVE_KEY      "file_dlg_transport_resolve_key"
77
78 #define ARGUMENT_CL "argument_cl"
79
80 /*
81  * Keep a static pointer to the current "Save Capture File As" window, if
82  * any, so that if somebody tries to do "File:Save" or "File:Save As"
83  * while there's already a "Save Capture File As" window up, we just pop
84  * up the existing one, rather than creating a new one.
85  */
86 static GtkWidget *file_save_as_w;
87
88 /*
89  * A generic select_file_cb routine that is intended to be connected to
90  * a Browse button on other dialog boxes. This allows the user to browse
91  * for a file and select it. We fill in the text_entry that is asssociated
92  * with the button that invoked us. 
93  *
94  * We display the window label specified in our args.
95  */
96 void
97 select_file_cb(GtkWidget *file_bt, const char *label)
98 {
99   GtkWidget *caller = gtk_widget_get_toplevel(file_bt);
100   GtkWidget *fs, *file_te;
101 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
102   gchar     *f_name;
103 #endif
104
105   /* Has a file selection dialog box already been opened for that top-level
106      widget? */
107   fs = OBJECT_GET_DATA(caller, E_FILE_SEL_DIALOG_PTR_KEY);
108   file_te = OBJECT_GET_DATA(file_bt, E_FILE_TE_PTR_KEY);
109   if (fs != NULL) {
110     /* Yes.  Just re-activate that dialog box. */
111     reactivate_window(fs);
112     return;
113   }
114
115   fs = file_selection_new(label, FILE_SELECTION_SAVE);
116
117   /* If we've opened a file, start out by showing the files in the directory
118      in which that file resided. */
119   if (last_open_dir)
120     file_selection_set_current_folder(fs, last_open_dir);
121
122   OBJECT_SET_DATA(fs, PRINT_FILE_TE_KEY, file_te);
123
124   /* Set the E_FS_CALLER_PTR_KEY for the new dialog to point to our caller. */
125   OBJECT_SET_DATA(fs, E_FS_CALLER_PTR_KEY, caller);
126
127   /* Set the E_FILE_SEL_DIALOG_PTR_KEY for the caller to point to us */
128   OBJECT_SET_DATA(caller, E_FILE_SEL_DIALOG_PTR_KEY, fs);
129
130   /* Call a handler when the file selection box is destroyed, so we can inform
131      our caller, if any, that it's been destroyed. */
132   SIGNAL_CONNECT(fs, "destroy", GTK_SIGNAL_FUNC(file_select_destroy_cb), 
133                  file_te);
134
135 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
136   if (gtk_dialog_run(GTK_DIALOG(fs)) == GTK_RESPONSE_ACCEPT)
137   {
138       f_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
139       gtk_entry_set_text(GTK_ENTRY(file_te), f_name);
140       g_free(f_name);
141   }
142   gtk_widget_destroy(fs);
143 #else
144   SIGNAL_CONNECT(GTK_FILE_SELECTION(fs)->ok_button, "clicked", 
145                  file_select_ok_cb, fs);
146
147   /* Connect the cancel_button to destroy the widget */
148   SIGNAL_CONNECT(GTK_FILE_SELECTION(fs)->cancel_button, "clicked",
149                  file_select_cancel_cb, fs);
150
151   /* Catch the "key_press_event" signal in the window, so that we can catch
152      the ESC key being pressed and act as if the "Cancel" button had
153      been selected. */
154   dlg_set_cancel(fs, GTK_FILE_SELECTION(fs)->cancel_button);
155
156   gtk_widget_show(fs);
157 #endif
158 }
159
160 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 4) || GTK_MAJOR_VERSION < 2
161 static void
162 file_select_ok_cb(GtkWidget *w _U_, gpointer data)
163 {
164   gchar     *f_name;
165
166   f_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (data)));
167
168   /* Perhaps the user specified a directory instead of a file.
169      Check whether they did. */
170   if (test_for_directory(f_name) == EISDIR) {
171         /* It's a directory - set the file selection box to display it. */
172         set_last_open_dir(f_name);
173         g_free(f_name);
174         gtk_file_selection_set_filename(GTK_FILE_SELECTION(data),
175                                         last_open_dir);
176         return;
177   }
178
179   gtk_entry_set_text(GTK_ENTRY(OBJECT_GET_DATA(data, PRINT_FILE_TE_KEY)),
180                      f_name);
181   gtk_widget_destroy(GTK_WIDGET(data));
182
183   g_free(f_name);
184 }
185
186 static void
187 file_select_cancel_cb(GtkWidget *w _U_, gpointer data)
188 {
189   gtk_widget_destroy(GTK_WIDGET(data));
190 }
191 #endif
192
193 static void
194 file_select_destroy_cb(GtkWidget *win, GtkWidget* file_te)
195 {
196   GtkWidget *caller;
197
198   /* Get the widget that requested that we be popped up.
199      (It should arrange to destroy us if it's destroyed, so
200      that we don't get a pointer to a non-existent window here.) */
201   caller = OBJECT_GET_DATA(win, E_FS_CALLER_PTR_KEY);
202
203   /* Tell it we no longer exist. */
204   OBJECT_SET_DATA(caller, E_FILE_SEL_DIALOG_PTR_KEY, NULL);
205
206   /* Now nuke this window. */
207   gtk_grab_remove(GTK_WIDGET(win));
208   gtk_widget_destroy(GTK_WIDGET(win));
209
210   /* Give the focus to the file text entry widget so the user can just press
211      Return to print to the file. */
212   gtk_widget_grab_focus(file_te);
213 }
214
215 /*
216  * Keep a static pointer to the current "Open Capture File" window, if
217  * any, so that if somebody tries to do "File:Open" while there's already
218  * an "Open Capture File" window up, we just pop up the existing one,
219  * rather than creating a new one.
220  */
221 static GtkWidget *file_open_w;
222
223 /* Open a file */
224 void
225 file_open_cmd(GtkWidget *w)
226 {
227   GtkWidget     *main_vb, *filter_hbox, *filter_bt, *filter_te,
228                 *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
229 #if GTK_MAJOR_VERSION < 2
230   GtkAccelGroup *accel_group;
231 #endif
232   /* No Apply button, and "OK" just sets our text widget, it doesn't
233      activate it (i.e., it doesn't cause us to try to open the file). */
234   static construct_args_t args = {
235         "Ethereal: Read Filter",
236         FALSE,
237         FALSE
238   };
239
240   if (file_open_w != NULL) {
241     /* There's already an "Open Capture File" dialog box; reactivate it. */
242     reactivate_window(file_open_w);
243     return;
244   }
245
246   file_open_w = file_selection_new("Ethereal: Open Capture File",
247                                    FILE_SELECTION_OPEN);
248   SIGNAL_CONNECT(file_open_w, "destroy", file_open_destroy_cb, NULL);
249
250 #if GTK_MAJOR_VERSION < 2
251   /* Accelerator group for the accelerators (or, as they're called in
252      Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
253      Ctrl+<key> is an accelerator). */
254   accel_group = gtk_accel_group_new();
255   gtk_window_add_accel_group(GTK_WINDOW(file_open_w), accel_group);
256 #endif
257
258   switch (prefs.gui_fileopen_style) {
259
260   case FO_STYLE_LAST_OPENED:
261     /* The user has specified that we should start out in the last directory
262        we looked in.  If we've already opened a file, use its containing
263        directory, if we could determine it, as the directory, otherwise
264        use the "last opened" directory saved in the preferences file if
265        there was one. */
266     if (last_open_dir)
267       file_selection_set_current_folder(file_open_w, last_open_dir);
268     break;
269
270   case FO_STYLE_SPECIFIED:
271     /* The user has specified that we should always start out in a
272        specified directory; if they've specified that directory,
273        start out by showing the files in that dir. */
274     if (prefs.gui_fileopen_dir[0] != '\0')
275       file_selection_set_current_folder(file_open_w, prefs.gui_fileopen_dir);
276     break;
277   }
278     
279   /* Container for each row of widgets */
280   main_vb = gtk_vbox_new(FALSE, 3);
281   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
282   file_selection_set_extra_widget(file_open_w, main_vb);
283   gtk_widget_show(main_vb);
284
285   filter_hbox = gtk_hbox_new(FALSE, 1);
286   gtk_container_border_width(GTK_CONTAINER(filter_hbox), 0);
287   gtk_box_pack_start(GTK_BOX(main_vb), filter_hbox, FALSE, FALSE, 0);
288   gtk_widget_show(filter_hbox);
289
290   filter_bt = BUTTON_NEW_FROM_STOCK(ETHEREAL_STOCK_DISPLAY_FILTER_ENTRY);
291   SIGNAL_CONNECT(filter_bt, "clicked", display_filter_construct_cb, &args);
292   SIGNAL_CONNECT(filter_bt, "destroy", filter_button_destroy_cb, NULL);
293   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
294   gtk_widget_show(filter_bt);
295
296   filter_te = gtk_entry_new();
297   OBJECT_SET_DATA(filter_bt, E_FILT_TE_PTR_KEY, filter_te);
298   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
299   SIGNAL_CONNECT(filter_te, "changed", filter_te_syntax_check_cb, NULL);
300   gtk_widget_show(filter_te);
301
302 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
303   OBJECT_SET_DATA(file_open_w, E_RFILTER_TE_KEY, filter_te);
304 #else
305   OBJECT_SET_DATA(GTK_FILE_SELECTION(file_open_w)->ok_button,
306                   E_RFILTER_TE_KEY, filter_te);
307 #endif
308
309   m_resolv_cb = CHECK_BUTTON_NEW_WITH_MNEMONIC("Enable _MAC name resolution", accel_group);
310   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_resolv_cb),
311         g_resolv_flags & RESOLV_MAC);
312   gtk_box_pack_start(GTK_BOX(main_vb), m_resolv_cb, FALSE, FALSE, 0);
313 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
314   OBJECT_SET_DATA(file_open_w,
315                   E_FILE_M_RESOLVE_KEY, m_resolv_cb);
316 #else
317   OBJECT_SET_DATA(GTK_FILE_SELECTION(file_open_w)->ok_button,
318                   E_FILE_M_RESOLVE_KEY, m_resolv_cb);
319 #endif
320   gtk_widget_show(m_resolv_cb);
321
322   n_resolv_cb = CHECK_BUTTON_NEW_WITH_MNEMONIC("Enable _network name resolution", accel_group);
323   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(n_resolv_cb),
324         g_resolv_flags & RESOLV_NETWORK);
325   gtk_box_pack_start(GTK_BOX(main_vb), n_resolv_cb, FALSE, FALSE, 0);
326   gtk_widget_show(n_resolv_cb);
327 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
328   OBJECT_SET_DATA(file_open_w, E_FILE_N_RESOLVE_KEY, n_resolv_cb);
329 #else
330   OBJECT_SET_DATA(GTK_FILE_SELECTION(file_open_w)->ok_button,
331                   E_FILE_N_RESOLVE_KEY, n_resolv_cb);
332 #endif
333
334   t_resolv_cb = CHECK_BUTTON_NEW_WITH_MNEMONIC("Enable _transport name resolution", accel_group);
335   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(t_resolv_cb),
336         g_resolv_flags & RESOLV_TRANSPORT);
337   gtk_box_pack_start(GTK_BOX(main_vb), t_resolv_cb, FALSE, FALSE, 0);
338   gtk_widget_show(t_resolv_cb);
339 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
340   OBJECT_SET_DATA(file_open_w, E_FILE_T_RESOLVE_KEY, t_resolv_cb);
341 #else
342   OBJECT_SET_DATA(GTK_FILE_SELECTION(file_open_w)->ok_button,
343                   E_FILE_T_RESOLVE_KEY, t_resolv_cb);
344 #endif
345
346 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
347   OBJECT_SET_DATA(file_open_w, E_DFILTER_TE_KEY,
348                   OBJECT_GET_DATA(w, E_DFILTER_TE_KEY));
349   if (gtk_dialog_run(GTK_DIALOG(file_open_w)) == GTK_RESPONSE_ACCEPT)
350   {
351     file_open_ok_cb(file_open_w, file_open_w);
352   }
353   else gtk_widget_destroy(file_open_w);
354 #else
355   /* Connect the ok_button to file_open_ok_cb function and pass along a
356      pointer to the file selection box widget */
357   SIGNAL_CONNECT(GTK_FILE_SELECTION(file_open_w)->ok_button, "clicked",
358                  file_open_ok_cb, file_open_w);
359
360   OBJECT_SET_DATA(GTK_FILE_SELECTION(file_open_w)->ok_button,
361                   E_DFILTER_TE_KEY, OBJECT_GET_DATA(w, E_DFILTER_TE_KEY));
362
363   /* Connect the cancel_button to destroy the widget */
364   SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(file_open_w)->cancel_button,
365                         "clicked", (GtkSignalFunc)gtk_widget_destroy,
366                         file_open_w);
367
368   /* Catch the "key_press_event" signal in the window, so that we can catch
369      the ESC key being pressed and act as if the "Cancel" button had
370      been selected. */
371   dlg_set_cancel(file_open_w, GTK_FILE_SELECTION(file_open_w)->cancel_button);
372
373   gtk_widget_show(file_open_w);
374 #endif
375 }
376
377 void file_open_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
378 {
379     switch(btn) {
380     case(ESD_BTN_YES):
381         /* save file first */
382         file_save_as_cmd(after_save_open_dialog, data);
383         break;
384     case(ESD_BTN_NO):
385         cf_close(&cfile);
386         file_open_cmd(data);
387         break;
388     case(ESD_BTN_CANCEL):
389         break;
390     default:
391         g_assert_not_reached();
392     }
393 }
394
395 void
396 file_open_cmd_cb(GtkWidget *widget, gpointer data _U_) {
397   gpointer  dialog;
398
399   if((cfile.state != FILE_CLOSED) && !cfile.user_saved) {
400     /* user didn't saved his current file, ask him */
401     dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_YES_NO_CANCEL,
402                 PRIMARY_TEXT_START "Save capture file before opening a new one?" PRIMARY_TEXT_END "\n\n"
403                 "If you open a new capture file without saving, your capture data will be discarded.");
404     simple_dialog_set_cb(dialog, file_open_answered_cb, widget);
405   } else {
406     /* unchanged file, just open a new one */
407     file_open_cmd(widget);
408   }
409 }
410
411 static void
412 file_open_ok_cb(GtkWidget *w, gpointer fs) {
413   gchar     *cf_name, *rfilter, *s;
414   GtkWidget *filter_te, *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
415   dfilter_t *rfcode = NULL;
416   int        err;
417
418 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
419   cf_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
420 #else
421   cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
422 #endif
423   filter_te = OBJECT_GET_DATA(w, E_RFILTER_TE_KEY);
424   rfilter = (gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
425   if (!dfilter_compile(rfilter, &rfcode)) {
426     bad_dfilter_alert_box(rfilter);
427     g_free(cf_name);
428     return;
429   }
430
431   /* Perhaps the user specified a directory instead of a file.
432      Check whether they did. */
433   if (test_for_directory(cf_name) == EISDIR) {
434         /* It's a directory - set the file selection box to display that
435            directory, don't try to open the directory as a capture file. */
436         set_last_open_dir(cf_name);
437         g_free(cf_name);
438         gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
439         return;
440   }
441
442   /* Try to open the capture file. */
443   if ((err = cf_open(cf_name, FALSE, &cfile)) != 0) {
444     /* We couldn't open it; don't dismiss the open dialog box,
445        just leave it around so that the user can, after they
446        dismiss the alert box popped up for the open error,
447        try again. */
448     if (rfcode != NULL)
449       dfilter_free(rfcode);
450     g_free(cf_name);
451     return;
452   }
453
454   /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
455      it closed the previous capture file, and thus destroyed any
456      previous read filter attached to "cf"). */
457   cfile.rfcode = rfcode;
458
459   /* Set the global resolving variable */
460   g_resolv_flags = prefs.name_resolve & RESOLV_CONCURRENT;
461   m_resolv_cb = OBJECT_GET_DATA(w, E_FILE_M_RESOLVE_KEY);
462   g_resolv_flags |= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (m_resolv_cb)) ? RESOLV_MAC : RESOLV_NONE;
463   n_resolv_cb = OBJECT_GET_DATA(w, E_FILE_N_RESOLVE_KEY);
464   g_resolv_flags |= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (n_resolv_cb)) ? RESOLV_NETWORK : RESOLV_NONE;
465   t_resolv_cb = OBJECT_GET_DATA(w, E_FILE_T_RESOLVE_KEY);
466   g_resolv_flags |= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (t_resolv_cb)) ? RESOLV_TRANSPORT : RESOLV_NONE;
467
468   /* We've crossed the Rubicon; get rid of the file selection box. */
469   gtk_widget_hide(GTK_WIDGET (fs));
470   gtk_widget_destroy(GTK_WIDGET (fs));
471
472   switch (cf_read(&cfile)) {
473
474   case READ_SUCCESS:
475   case READ_ERROR:
476     /* Just because we got an error, that doesn't mean we were unable
477        to read any of the file; we handle what we could get from the
478        file. */
479     break;
480
481   case READ_ABORTED:
482     /* The user bailed out of re-reading the capture file; the
483        capture file has been closed - just free the capture file name
484        string and return (without changing the last containing
485        directory). */
486     g_free(cf_name);
487     return;
488   }
489
490   /* Save the name of the containing directory specified in the path name,
491      if any; we can write over cf_name, which is a good thing, given that
492      "get_dirname()" does write over its argument. */
493   s = get_dirname(cf_name);
494   set_last_open_dir(s);
495   gtk_widget_grab_focus(packet_list);
496
497   g_free(cf_name);
498 }
499
500 static void
501 file_open_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
502 {
503   /* Note that we no longer have a "Open Capture File" dialog box. */
504   file_open_w = NULL;
505 }
506
507 void file_close_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
508 {
509     switch(btn) {
510     case(ESD_BTN_YES):
511         /* save file first */
512         file_save_as_cmd(after_save_close_file, NULL);
513         break;
514     case(ESD_BTN_NO):
515         cf_close(&cfile);
516         break;
517     case(ESD_BTN_CANCEL):
518         break;
519     default:
520         g_assert_not_reached();
521     }
522 }
523
524 /* Close a file */
525 void
526 file_close_cmd_cb(GtkWidget *widget _U_, gpointer data _U_) {
527   gpointer  dialog;
528
529   if((cfile.state != FILE_CLOSED) && !cfile.user_saved) {
530     /* user didn't saved his current file, ask him */
531     dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_YES_NO_CANCEL,
532                 PRIMARY_TEXT_START "Save capture file before closing it?" PRIMARY_TEXT_END "\n\n"
533                 "If you close without saving, your capture data will be discarded.");
534
535     simple_dialog_set_cb(dialog, file_close_answered_cb, NULL);
536   } else {
537     /* unchanged file, just close it */
538     cf_close(&cfile);
539   }
540 }
541
542 void
543 file_save_cmd_cb(GtkWidget *w, gpointer data) {
544   /* If the file's already been saved, do nothing.  */
545   if (cfile.user_saved)
546     return;
547
548   /* Do a "Save As". */
549   file_save_as_cmd_cb(w, data);
550 }
551
552 /* XXX - can we make these not be static? */
553 static packet_range_t range;
554 static gboolean color_marked;
555 static int filetype;
556 static GtkWidget *captured_bt;
557 static GtkWidget *displayed_bt;
558 static GtkWidget *select_all_rb;
559 static GtkWidget *select_all_c_lb;
560 static GtkWidget *select_all_d_lb;
561 static GtkWidget *select_curr_rb;
562 static GtkWidget *select_curr_c_lb;
563 static GtkWidget *select_curr_d_lb;
564 static GtkWidget *select_marked_only_rb;
565 static GtkWidget *select_marked_only_c_lb;
566 static GtkWidget *select_marked_only_d_lb;
567 static GtkWidget *select_marked_range_rb;
568 static GtkWidget *select_marked_range_c_lb;
569 static GtkWidget *select_marked_range_d_lb;
570 static GtkWidget *select_user_range_rb;
571 static GtkWidget *select_user_range_c_lb;
572 static GtkWidget *select_user_range_d_lb;
573 static GtkWidget *select_user_range_entry;
574 static GtkWidget *cfmark_cb;
575 static GtkWidget *ft_om;
576
577 static gboolean
578 can_save_with_wiretap(int ft)
579 {
580   /* To save a file with Wiretap, Wiretap has to handle that format,
581      and its code to handle that format must be able to write a file
582      with this file's encapsulation type. */
583   return wtap_dump_can_open(ft) && wtap_dump_can_write_encap(ft, cfile.lnk_t);
584 }
585
586 static void
587 file_set_save_dynamics(void) {
588   gboolean      filtered_active;
589   gchar         label_text[100];
590   gint          selected_num;
591
592
593   filtered_active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(displayed_bt));
594
595   gtk_widget_set_sensitive(displayed_bt, can_save_with_wiretap(filetype));
596
597   gtk_widget_set_sensitive(select_all_c_lb, !filtered_active);
598   g_snprintf(label_text, sizeof(label_text), "%u", cfile.count);
599   gtk_label_set_text(GTK_LABEL(select_all_c_lb), label_text);
600   gtk_widget_set_sensitive(select_all_d_lb, filtered_active);
601   g_snprintf(label_text, sizeof(label_text), "%u", range.displayed_cnt);
602
603   gtk_label_set_text(GTK_LABEL(select_all_d_lb), label_text);
604
605   selected_num = (cfile.current_frame) ? cfile.current_frame->num : 0;
606   /* XXX: how to update the radio button label but keep the mnemonic? */
607 /*  g_snprintf(label_text, sizeof(label_text), "_Selected packet #%u only", selected_num);
608   gtk_label_set_text(GTK_LABEL(GTK_BIN(select_curr_rb)->child), label_text);*/
609   gtk_widget_set_sensitive(select_curr_rb, selected_num);
610   g_snprintf(label_text, sizeof(label_text), "%u", selected_num ? 1 : 0);
611   gtk_label_set_text(GTK_LABEL(select_curr_c_lb), label_text);
612   gtk_widget_set_sensitive(select_curr_c_lb, selected_num && !filtered_active);
613   g_snprintf(label_text, sizeof(label_text), "%u", selected_num ? 1 : 0);
614   gtk_label_set_text(GTK_LABEL(select_curr_d_lb), label_text);
615   gtk_widget_set_sensitive(select_curr_d_lb, selected_num && filtered_active);
616
617   gtk_widget_set_sensitive(select_marked_only_rb, cfile.marked_count);
618   g_snprintf(label_text, sizeof(label_text), "%u", cfile.marked_count);
619   gtk_label_set_text(GTK_LABEL(select_marked_only_c_lb), label_text);
620   gtk_widget_set_sensitive(select_marked_only_c_lb, cfile.marked_count && !filtered_active);
621   g_snprintf(label_text, sizeof(label_text), "%u", range.displayed_marked_cnt);
622   gtk_label_set_text(GTK_LABEL(select_marked_only_d_lb), label_text);
623   gtk_widget_set_sensitive(select_marked_only_d_lb, range.displayed_marked_cnt && filtered_active);
624
625   gtk_widget_set_sensitive(select_marked_range_rb, range.mark_range_cnt);
626   g_snprintf(label_text, sizeof(label_text), "%u", range.mark_range_cnt);
627   gtk_label_set_text(GTK_LABEL(select_marked_range_c_lb), label_text);
628   gtk_widget_set_sensitive(select_marked_range_c_lb, range.mark_range_cnt && !filtered_active);
629   g_snprintf(label_text, sizeof(label_text), "%u", range.displayed_mark_range_cnt);
630   gtk_label_set_text(GTK_LABEL(select_marked_range_d_lb), label_text);
631   gtk_widget_set_sensitive(select_marked_range_d_lb, range.displayed_mark_range_cnt && filtered_active);
632
633   gtk_widget_set_sensitive(select_user_range_rb, TRUE);
634   g_snprintf(label_text, sizeof(label_text), "%u", range.user_range_cnt);
635   gtk_label_set_text(GTK_LABEL(select_user_range_c_lb), label_text);
636   gtk_widget_set_sensitive(select_user_range_c_lb, !filtered_active);
637   g_snprintf(label_text, sizeof(label_text), "%u", range.displayed_user_range_cnt);
638   gtk_label_set_text(GTK_LABEL(select_user_range_d_lb), label_text);
639   gtk_widget_set_sensitive(select_user_range_d_lb, filtered_active);
640 }
641
642 /* Generate a list of the file types we can save this file as.
643
644    "filetype" is the type it has now.
645
646    "encap" is the encapsulation for its packets (which could be
647    "unknown" or "per-packet").
648
649    "filtered" is TRUE if we're to save only the packets that passed
650    the display filter (in which case we have to save it using Wiretap)
651    and FALSE if we're to save the entire file (in which case, if we're
652    saving it in the type it has already, we can just copy it).
653
654    The same applies for sel_curr, sel_all, sel_m_only, sel_m_range and sel_man_range
655 */
656 static void
657 set_file_type_list(GtkWidget *option_menu)
658 {
659   GtkWidget *ft_menu, *ft_menu_item;
660   int ft;
661   guint index;
662   guint item_to_select;
663
664   /* Default to the first supported file type, if the file's current
665      type isn't supported. */
666   item_to_select = 0;
667
668   ft_menu = gtk_menu_new();
669
670   /* Check all file types. */
671   index = 0;
672   for (ft = 0; ft < WTAP_NUM_FILE_TYPES; ft++) {
673     if (!packet_range_process_all(&range) || ft != cfile.cd_t) {
674       /* not all unfiltered packets or a different file type.  We have to use Wiretap. */
675       if (!can_save_with_wiretap(ft))
676         continue;       /* We can't. */
677     }
678
679     /* OK, we can write it out in this type. */
680     ft_menu_item = gtk_menu_item_new_with_label(wtap_file_type_string(ft));
681     if (ft == filetype) {
682       /* Default to the same format as the file, if it's supported. */
683       item_to_select = index;
684     }
685     SIGNAL_CONNECT(ft_menu_item, "activate", select_file_type_cb,
686                    GINT_TO_POINTER(ft));
687     gtk_menu_append(GTK_MENU(ft_menu), ft_menu_item);
688     gtk_widget_show(ft_menu_item);
689     index++;
690   }
691
692   gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), ft_menu);
693   gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu), item_to_select);
694 }
695
696 static void
697 select_file_type_cb(GtkWidget *w _U_, gpointer data)
698 {
699   int new_filetype = GPOINTER_TO_INT(data);
700
701   if (filetype != new_filetype) {
702     /* We can select only the filtered or marked packets to be saved if we can
703        use Wiretap to save the file. */
704     gtk_widget_set_sensitive(displayed_bt, can_save_with_wiretap(new_filetype));
705     filetype = new_filetype;
706     file_set_save_marked_sensitive();
707   }
708 }
709
710 static void
711 toggle_captured_cb(GtkWidget *widget, gpointer data _U_)
712 {
713   /* is the button now active? */
714   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget))) {
715     /* They changed the state of the "captured" button. */
716     range.process_filtered = FALSE;
717     /* XXX: the following line fails, I have no idea why */
718     /* set_file_type_list(ft_om);*/
719
720     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(captured_bt), TRUE);
721     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(displayed_bt), FALSE);
722
723     file_set_save_dynamics();
724   }
725 }
726
727 static void
728 toggle_filtered_cb(GtkWidget *widget, gpointer data _U_)
729 {
730   /* is the button now active? */
731   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget))) {
732     range.process_filtered = TRUE;
733     set_file_type_list(ft_om);
734
735     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(captured_bt), FALSE);
736     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(displayed_bt), TRUE);
737     
738     file_set_save_dynamics();
739   }
740 }
741
742 static void
743 toggle_select_all(GtkWidget *widget, gpointer data _U_)
744 {
745   /* is the button now active? */
746   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget))) {
747     range.process = range_process_all;
748     set_file_type_list(ft_om);
749     file_set_save_dynamics();
750   }
751 }
752
753 static void
754 toggle_select_selected(GtkWidget *widget, gpointer data _U_)
755 {
756   /* is the button now active? */
757   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget))) {
758     range.process = range_process_selected;
759     set_file_type_list(ft_om);
760     file_set_save_dynamics();
761   }
762 }
763
764 static void
765 toggle_select_marked_only(GtkWidget *widget, gpointer data _U_)
766 {
767   /* is the button now active? */
768   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget))) {
769     range.process = range_process_marked;
770     set_file_type_list(ft_om);
771     file_set_save_dynamics();
772   }
773 }
774
775 static void
776 toggle_select_marked_range(GtkWidget *widget, gpointer data _U_)
777 {
778   /* is the button now active? */
779   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget))) {
780     range.process = range_process_marked_range;
781     set_file_type_list(ft_om);
782     file_set_save_dynamics();
783   }
784 }
785
786 static void
787 toggle_select_user_range(GtkWidget *widget, gpointer data _U_)
788 {
789   /* is the button now active? */
790   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget))) {
791     range.process = range_process_user_range;
792     set_file_type_list(ft_om);
793     file_set_save_dynamics();
794   }
795         
796   /* Make the entry widget sensitive or insensitive */
797   gtk_widget_set_sensitive(select_user_range_entry, range.process == range_process_user_range);
798
799   /* When selecting user specified range, then focus on the entry */
800   if (range.process == range_process_user_range)
801       gtk_widget_grab_focus(select_user_range_entry);
802
803 }
804
805 static void
806 range_entry(GtkWidget *entry)
807 {
808   const gchar *entry_text;
809
810   entry_text = gtk_entry_get_text (GTK_ENTRY (entry));
811   packet_range_convert_str(&range, entry_text);
812   file_set_save_dynamics();
813 }
814
815 /*
816  * Set the "Save only marked packets" toggle button as appropriate for
817  * the current output file type and count of marked packets.
818  *
819  * Called when the "Save As..." dialog box is created and when either
820  * the file type or the marked count changes.
821  */
822 void
823 file_set_save_marked_sensitive(void)
824 {
825   if (file_save_as_w == NULL) {
826     /* We don't currently have a "Save As..." dialog box up. */
827     return;
828   }
829         
830   /* We can request that only the marked packets be saved only if we
831      can use Wiretap to save the file and if there *are* marked packets. */
832   if (can_save_with_wiretap(filetype) && cfile.marked_count != 0) {
833     gtk_widget_set_sensitive(select_marked_only_rb, TRUE);
834     gtk_widget_set_sensitive(select_marked_range_rb, TRUE);       
835   }
836   else {
837     /* Force the "Save only marked packets" toggle to "false", turn
838        off the flag it controls, and update the list of types we can
839        save the file as. */
840     range.process = range_process_all;
841     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_marked_only_rb), FALSE);
842     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_marked_range_rb), FALSE);       
843     set_file_type_list(ft_om);
844     gtk_widget_set_sensitive(select_marked_only_rb,  FALSE);
845     gtk_widget_set_sensitive(select_marked_range_rb, FALSE);      
846   }
847 }
848
849 action_after_save_e action_after_save_g;
850 gpointer            action_after_save_data_g;
851
852
853 void
854 file_save_as_cmd(action_after_save_e action_after_save, gpointer action_after_save_data)
855 {
856   GtkWidget     *main_vb, *ft_hb, *ft_lb, *range_fr, *range_tb;
857   GtkTooltips   *tooltips;
858
859 #if GTK_MAJOR_VERSION < 2
860   GtkAccelGroup *accel_group;
861 #endif
862           
863   if (file_save_as_w != NULL) {
864     /* There's already an "Save Capture File As" dialog box; reactivate it. */
865     reactivate_window(file_save_as_w);
866     return;
867   }
868
869   /* Default to saving all packets, in the file's current format. */
870   filetype = cfile.cd_t;
871
872   /* init the packet range */
873   packet_range_init(&range);
874
875   /* Enable tooltips */
876   tooltips = gtk_tooltips_new();
877           
878   /* build the file selection */
879   file_save_as_w = file_selection_new ("Ethereal: Save Capture File As",
880                                        FILE_SELECTION_SAVE);
881   SIGNAL_CONNECT(file_save_as_w, "destroy", file_save_as_destroy_cb, NULL);
882
883   /* as the dialog might already be gone, when using this values, we cannot
884    * set data to the dialog object, but keep global values */
885   action_after_save_g       = action_after_save;
886   action_after_save_data_g  = action_after_save_data;
887
888 #if GTK_MAJOR_VERSION < 2
889   accel_group = gtk_accel_group_new();
890   gtk_window_add_accel_group(GTK_WINDOW(file_save_as_w), accel_group);
891 #endif
892         
893   /* If we've opened a file, start out by showing the files in the directory
894      in which that file resided. */
895   if (last_open_dir)
896     file_selection_set_current_folder(file_save_as_w, last_open_dir);
897
898   /* Container for each row of widgets */
899        
900   main_vb = gtk_vbox_new(FALSE, 5);
901   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
902   file_selection_set_extra_widget(file_save_as_w, main_vb);
903   gtk_widget_show(main_vb);     
904                 
905   /*** Packet Range frame ***/
906   range_fr = gtk_frame_new("Packet Range");
907   gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 0);
908   gtk_widget_show(range_fr);
909   
910   /* range table */
911   range_tb = gtk_table_new(7, 3, FALSE);
912   gtk_container_border_width(GTK_CONTAINER(range_tb), 5);
913   gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
914   gtk_widget_show(range_tb);
915
916   /* captured button */
917   captured_bt = TOGGLE_BUTTON_NEW_WITH_MNEMONIC("_Captured", accel_group);
918   gtk_table_attach_defaults(GTK_TABLE(range_tb), captured_bt, 1, 2, 0, 1);
919   SIGNAL_CONNECT(captured_bt, "toggled", toggle_captured_cb, NULL);
920   gtk_tooltips_set_tip (tooltips,captured_bt,("Process all the below chosen packets"), NULL);
921   gtk_widget_show(captured_bt);
922
923   /* displayed button */
924   displayed_bt = TOGGLE_BUTTON_NEW_WITH_MNEMONIC("_Displayed", accel_group);
925   gtk_table_attach_defaults(GTK_TABLE(range_tb), displayed_bt, 2, 3, 0, 1);
926   SIGNAL_CONNECT(displayed_bt, "toggled", toggle_filtered_cb, NULL);
927   gtk_tooltips_set_tip (tooltips,displayed_bt,("Process only the below chosen packets, which also passes the current display filter"), NULL);
928   gtk_widget_show(displayed_bt);
929
930
931   /* Process all packets */
932   select_all_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "_All packets", accel_group);
933   gtk_table_attach_defaults(GTK_TABLE(range_tb), select_all_rb, 0, 1, 1, 2);
934   gtk_tooltips_set_tip (tooltips,select_all_rb,("Process all packets"), NULL);
935   SIGNAL_CONNECT(select_all_rb, "toggled", toggle_select_all, NULL);
936   gtk_widget_show(select_all_rb);
937
938   select_all_c_lb = gtk_label_new("?");
939   gtk_table_attach_defaults(GTK_TABLE(range_tb), select_all_c_lb, 1, 2, 1, 2);
940   gtk_widget_show(select_all_c_lb);
941   select_all_d_lb = gtk_label_new("?");
942   gtk_table_attach_defaults(GTK_TABLE(range_tb), select_all_d_lb, 2, 3, 1, 2);
943   gtk_widget_show(select_all_d_lb);
944
945         
946   /* Process currently selected */
947   select_curr_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(select_all_rb, "_Selected packet only", accel_group);
948   gtk_table_attach_defaults(GTK_TABLE(range_tb), select_curr_rb, 0, 1, 2, 3);
949   gtk_tooltips_set_tip (tooltips,select_curr_rb,("Process the currently selected packet only"), NULL);
950   SIGNAL_CONNECT(select_curr_rb, "toggled", toggle_select_selected, NULL);
951   gtk_widget_show(select_curr_rb);
952         
953   select_curr_c_lb = gtk_label_new("?");
954   gtk_table_attach_defaults(GTK_TABLE(range_tb), select_curr_c_lb, 1, 2, 2, 3);
955   gtk_widget_show(select_curr_c_lb);
956   select_curr_d_lb = gtk_label_new("?");
957   gtk_table_attach_defaults(GTK_TABLE(range_tb), select_curr_d_lb, 2, 3, 2, 3);
958   gtk_widget_show(select_curr_d_lb);
959
960
961   /* Process marked packets */
962   select_marked_only_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(select_all_rb, "_Marked packets only", accel_group);
963   gtk_table_attach_defaults(GTK_TABLE(range_tb), select_marked_only_rb, 0, 1, 3, 4);
964   gtk_tooltips_set_tip (tooltips,select_marked_only_rb,("Process marked packets only"), NULL);
965   SIGNAL_CONNECT(select_marked_only_rb, "toggled", toggle_select_marked_only, NULL);
966   gtk_widget_show(select_marked_only_rb);
967
968   select_marked_only_c_lb = gtk_label_new("?");
969   gtk_table_attach_defaults(GTK_TABLE(range_tb), select_marked_only_c_lb, 1, 2, 3, 4);
970   gtk_widget_show(select_marked_only_c_lb);
971   select_marked_only_d_lb = gtk_label_new("?");
972   gtk_table_attach_defaults(GTK_TABLE(range_tb), select_marked_only_d_lb, 2, 3, 3, 4);
973   gtk_widget_show(select_marked_only_d_lb);
974
975   
976   /* Process packet range between first and last packet */
977   select_marked_range_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(select_all_rb, "From first _to last marked packet", accel_group);
978   gtk_table_attach_defaults(GTK_TABLE(range_tb), select_marked_range_rb, 0, 1, 4, 5);
979   gtk_tooltips_set_tip (tooltips,select_marked_range_rb,("Process all packets between the first and last marker"), NULL);
980   SIGNAL_CONNECT(select_marked_range_rb, "toggled", toggle_select_marked_range, NULL);
981   gtk_widget_show(select_marked_range_rb);
982
983   select_marked_range_c_lb = gtk_label_new("?");
984   gtk_table_attach_defaults(GTK_TABLE(range_tb), select_marked_range_c_lb, 1, 2, 4, 5);
985   gtk_widget_show(select_marked_range_c_lb);
986   select_marked_range_d_lb = gtk_label_new("?");
987   gtk_table_attach_defaults(GTK_TABLE(range_tb), select_marked_range_d_lb, 2, 3, 4, 5);
988   gtk_widget_show(select_marked_range_d_lb);
989
990
991   /* Process a user specified provided packet range : -10,30,40-70,80- */
992   select_user_range_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(select_all_rb, "Specify a packet _range:", accel_group);
993   gtk_table_attach_defaults(GTK_TABLE(range_tb), select_user_range_rb, 0, 1, 5, 6);
994   gtk_tooltips_set_tip (tooltips,select_user_range_rb,("Process a specified packet range"), NULL);
995   SIGNAL_CONNECT(select_user_range_rb, "toggled", toggle_select_user_range, NULL);
996   gtk_widget_show(select_user_range_rb);
997
998   select_user_range_c_lb = gtk_label_new("?");
999   gtk_table_attach_defaults(GTK_TABLE(range_tb), select_user_range_c_lb, 1, 2, 5, 6);
1000   gtk_widget_show(select_user_range_c_lb);
1001   select_user_range_d_lb = gtk_label_new("?");
1002   gtk_table_attach_defaults(GTK_TABLE(range_tb), select_user_range_d_lb, 2, 3, 5, 6);
1003   gtk_widget_show(select_user_range_d_lb);
1004
1005
1006   /* The entry part */
1007   select_user_range_entry = gtk_entry_new();
1008   gtk_entry_set_max_length (GTK_ENTRY (select_user_range_entry), 254);
1009   gtk_table_attach_defaults(GTK_TABLE(range_tb), select_user_range_entry, 0, 1, 6, 7);
1010   gtk_tooltips_set_tip (tooltips,select_user_range_entry, 
1011         ("Specify a range of packet numbers :     \nExample :  1-10,18,25-100,332-"), NULL);
1012   SIGNAL_CONNECT(select_user_range_entry,"changed", range_entry, select_user_range_entry);      
1013   gtk_widget_set_sensitive(select_user_range_entry, FALSE);
1014   gtk_widget_show(select_user_range_entry);
1015
1016   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(captured_bt), TRUE);
1017   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(displayed_bt), FALSE);
1018   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_all_rb),  TRUE);
1019   
1020   /* File type row */
1021   ft_hb = gtk_hbox_new(FALSE, 3);
1022   gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
1023   gtk_widget_show(ft_hb);
1024
1025   ft_lb = gtk_label_new("File type:");
1026   gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
1027   gtk_widget_show(ft_lb);
1028
1029   ft_om = gtk_option_menu_new();
1030
1031   /* Generate the list of file types we can save. */
1032   set_file_type_list(ft_om);
1033   gtk_box_pack_start(GTK_BOX(ft_hb), ft_om, FALSE, FALSE, 0);
1034   gtk_widget_show(ft_om);
1035
1036   /*
1037    * Set the sensitivity of the "Save only marked packets" toggle
1038    * button
1039    *
1040    * This has to be done after we create the file type menu option,
1041    * as the routine that sets it also sets that menu.
1042    */
1043   file_set_save_marked_sensitive();
1044         
1045   /* dynamic values in the range frame */
1046   file_set_save_dynamics();
1047
1048 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1049   if (gtk_dialog_run(GTK_DIALOG(file_save_as_w)) == GTK_RESPONSE_ACCEPT)
1050   {
1051     file_save_as_ok_cb(file_save_as_w, file_save_as_w);
1052   }
1053   else gtk_widget_destroy(file_save_as_w);
1054 #else
1055   /* Connect the ok_button to file_save_as_ok_cb function and pass along a
1056      pointer to the file selection box widget */
1057   SIGNAL_CONNECT(GTK_FILE_SELECTION (file_save_as_w)->ok_button, "clicked",
1058                  file_save_as_ok_cb, file_save_as_w);
1059
1060   /* Connect the cancel_button to destroy the widget */
1061   SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(file_save_as_w)->cancel_button,
1062                         "clicked", (GtkSignalFunc)gtk_widget_destroy,
1063                         file_save_as_w);
1064
1065   /* Catch the "key_press_event" signal in the window, so that we can catch
1066      the ESC key being pressed and act as if the "Cancel" button had
1067      been selected. */
1068   dlg_set_cancel(file_save_as_w, GTK_FILE_SELECTION(file_save_as_w)->cancel_button);
1069
1070   gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_save_as_w), "");
1071
1072   gtk_widget_show(file_save_as_w);
1073 #endif
1074 }
1075
1076 void
1077 file_save_as_cmd_cb(GtkWidget *w _U_, gpointer data _U_)
1078 {
1079   file_save_as_cmd(after_save_no_action, NULL);
1080 }
1081
1082 static void
1083 file_save_as_ok_cb(GtkWidget *w _U_, gpointer fs) {
1084   gchar *cf_name;
1085   gchar *dirname;
1086
1087 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1088   cf_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
1089 #else
1090   cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
1091 #endif
1092
1093   /* Perhaps the user specified a directory instead of a file.
1094      Check whether they did. */
1095   if (test_for_directory(cf_name) == EISDIR) {
1096         /* It's a directory - set the file selection box to display that
1097            directory, and leave the selection box displayed. */
1098         set_last_open_dir(cf_name);
1099         g_free(cf_name);
1100         gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
1101         return;
1102   }
1103
1104   /* don't show the dialog while saving */
1105   gtk_widget_hide(GTK_WIDGET (fs));
1106
1107   /* Write out the packets (all, or only the ones from the current
1108      range) to the file with the specified name. */
1109   if (! cf_save(cf_name, &cfile, &range, filetype)) {
1110     /* The write failed; don't dismiss the open dialog box,
1111        just leave it around so that the user can, after they
1112        dismiss the alert box popped up for the error, try again. */
1113     g_free(cf_name);
1114     gtk_widget_show(GTK_WIDGET (fs));
1115     return;
1116   }
1117
1118   /* The write succeeded; get rid of the file selection box. */
1119   /* cf_save might already closed our dialog! */
1120   if (file_save_as_w)
1121     gtk_widget_destroy(GTK_WIDGET (fs));
1122
1123   /* Save the directory name for future file dialogs. */
1124   dirname = get_dirname(cf_name);  /* Overwrites cf_name */
1125   set_last_open_dir(dirname);
1126   g_free(cf_name);
1127
1128   /* we have finished saving, do we have pending things to do? */
1129   switch(action_after_save_g) {
1130   case(after_save_no_action):
1131       break;
1132   case(after_save_open_dialog):
1133       file_open_cmd(action_after_save_data_g);
1134       break;
1135   case(after_save_open_recent_file):
1136       menu_open_recent_file_cmd(action_after_save_data_g);
1137       break;
1138   case(after_save_open_dnd_file):
1139       dnd_open_file_cmd(action_after_save_data_g);
1140       break;
1141 #ifdef HAVE_LIBPCAP
1142   case(after_save_capture_dialog):
1143       capture_prep();
1144       break;
1145 #endif
1146   case(after_save_close_file):
1147       cf_close(&cfile);
1148       break;
1149   case(after_save_exit):
1150       main_do_quit();
1151       break;
1152   default:
1153       g_assert_not_reached();
1154   }
1155
1156   action_after_save_g = after_save_no_action;
1157 }
1158
1159 void
1160 file_save_as_destroy(void)
1161 {
1162   if (file_save_as_w)
1163     gtk_widget_destroy(file_save_as_w);
1164 }
1165
1166 static void
1167 file_save_as_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1168 {
1169   /* Note that we no longer have a "Save Capture File As" dialog box. */
1170   file_save_as_w = NULL;
1171 }
1172
1173 /* Reload a file using the current read and display filters */
1174 void
1175 file_reload_cmd_cb(GtkWidget *w _U_, gpointer data _U_) {
1176   gchar *filename;
1177   gboolean is_tempfile;
1178
1179   /* If the file could be opened, "cf_open()" calls "cf_close()"
1180      to get rid of state for the old capture file before filling in state
1181      for the new capture file.  "cf_close()" will remove the file if
1182      it's a temporary file; we don't want that to happen (for one thing,
1183      it'd prevent subsequent reopens from working).  Remember whether it's
1184      a temporary file, mark it as not being a temporary file, and then
1185      reopen it as the type of file it was.
1186
1187      Also, "cf_close()" will free "cfile.filename", so we must make
1188      a copy of it first. */
1189   filename = g_strdup(cfile.filename);
1190   is_tempfile = cfile.is_tempfile;
1191   cfile.is_tempfile = FALSE;
1192   if (cf_open(filename, is_tempfile, &cfile) == 0) {
1193     switch (cf_read(&cfile)) {
1194
1195     case READ_SUCCESS:
1196     case READ_ERROR:
1197       /* Just because we got an error, that doesn't mean we were unable
1198          to read any of the file; we handle what we could get from the
1199          file. */
1200       break;
1201
1202     case READ_ABORTED:
1203       /* The user bailed out of re-reading the capture file; the
1204          capture file has been closed - just free the capture file name
1205          string and return (without changing the last containing
1206          directory). */
1207       g_free(filename);
1208       return;
1209     }
1210   } else {
1211     /* The open failed, so "cfile.is_tempfile" wasn't set to "is_tempfile".
1212        Instead, the file was left open, so we should restore "cfile.is_tempfile"
1213        ourselves.
1214
1215        XXX - change the menu?  Presumably "cf_open()" will do that;
1216        make sure it does! */
1217     cfile.is_tempfile = is_tempfile;
1218   }
1219   /* "cf_open()" made a copy of the file name we handed it, so
1220      we should free up our copy. */
1221   g_free(filename);
1222 }
1223
1224 /******************** Color Filters *********************************/
1225 /*
1226  * Keep a static pointer to the current "Color Export" window, if
1227  * any, so that if somebody tries to do "Export"
1228  * while there's already a "Color Export" window up, we just pop
1229  * up the existing one, rather than creating a new one.
1230  */
1231 static GtkWidget *file_color_import_w;
1232
1233 /* sets the file path to the global color filter file.
1234    WARNING: called by both the import and the export dialog.
1235 */
1236 static void
1237 color_global_cb(GtkWidget *widget _U_, gpointer data)
1238 {
1239   GtkWidget *fs_widget = data;
1240   gchar *path;
1241
1242   /* decide what file to open (from dfilter code) */
1243   path = get_datafile_path("colorfilters");
1244
1245 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1246   gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(fs_widget), path);
1247 #else
1248   gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs_widget), path);
1249 #endif
1250   g_free((gchar *)path);
1251 }
1252
1253 /* Import color filters */
1254 void
1255 file_color_import_cmd_cb(GtkWidget *w _U_, gpointer data)
1256 {
1257   GtkWidget     *main_vb, *cfglobal_but;
1258 #if GTK_MAJOR_VERSION < 2
1259   GtkAccelGroup *accel_group;
1260 #endif
1261   /* No Apply button, and "OK" just sets our text widget, it doesn't
1262      activate it (i.e., it doesn't cause us to try to open the file). */
1263
1264   if (file_color_import_w != NULL) {
1265     /* There's already an "Import Color Filters" dialog box; reactivate it. */
1266     reactivate_window(file_color_import_w);
1267     return;
1268   }
1269
1270   file_color_import_w = file_selection_new("Ethereal: Import Color Filters",
1271                                            FILE_SELECTION_OPEN);
1272   SIGNAL_CONNECT(file_color_import_w, "destroy", file_color_import_destroy_cb, NULL);
1273
1274 #if GTK_MAJOR_VERSION < 2
1275   /* Accelerator group for the accelerators (or, as they're called in
1276      Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
1277      Ctrl+<key> is an accelerator). */
1278   accel_group = gtk_accel_group_new();
1279   gtk_window_add_accel_group(GTK_WINDOW(file_color_import_w), accel_group);
1280 #endif
1281
1282   /* If we've opened a file, start out by showing the files in the directory
1283      in which that file resided. */
1284   if (last_open_dir)
1285     file_selection_set_current_folder(file_color_import_w, last_open_dir);
1286
1287   /* Container for each row of widgets */
1288   main_vb = gtk_vbox_new(FALSE, 3);
1289   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
1290   file_selection_set_extra_widget(file_color_import_w, main_vb);
1291   gtk_widget_show(main_vb);
1292
1293
1294   cfglobal_but = gtk_button_new_with_label("Global Color Filter File");
1295   gtk_container_add(GTK_CONTAINER(main_vb), cfglobal_but);
1296   SIGNAL_CONNECT(cfglobal_but, "clicked", color_global_cb, file_color_import_w);
1297   gtk_widget_show(cfglobal_but);
1298
1299 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1300   OBJECT_SET_DATA(file_color_import_w, ARGUMENT_CL, data);
1301
1302   if (gtk_dialog_run(GTK_DIALOG(file_color_import_w)) == GTK_RESPONSE_ACCEPT)
1303   {
1304       file_color_import_ok_cb(file_color_import_w, file_color_import_w);
1305   }
1306   else gtk_widget_destroy(file_color_import_w);
1307 #else
1308   /* Connect the ok_button to file_open_ok_cb function and pass along a
1309      pointer to the file selection box widget */
1310   SIGNAL_CONNECT(GTK_FILE_SELECTION(file_color_import_w)->ok_button, "clicked",
1311                  file_color_import_ok_cb, file_color_import_w);
1312
1313   OBJECT_SET_DATA(GTK_FILE_SELECTION(file_color_import_w)->ok_button,
1314                   ARGUMENT_CL, data);
1315
1316   /* Connect the cancel_button to destroy the widget */
1317   SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(file_color_import_w)->cancel_button,
1318                         "clicked", (GtkSignalFunc)gtk_widget_destroy,
1319                         file_color_import_w);
1320
1321   /* Catch the "key_press_event" signal in the window, so that we can catch
1322      the ESC key being pressed and act as if the "Cancel" button had
1323      been selected. */
1324   dlg_set_cancel(file_color_import_w, GTK_FILE_SELECTION(file_color_import_w)->cancel_button);
1325
1326   gtk_widget_show(file_color_import_w);
1327 #endif
1328 }
1329
1330 static void
1331 file_color_import_ok_cb(GtkWidget *w, gpointer fs) {
1332   gchar     *cf_name, *s;
1333   gpointer  argument;
1334
1335   argument = OBJECT_GET_DATA(w, ARGUMENT_CL);     /* to be passed back into read_other_filters */
1336   
1337 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1338   cf_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
1339 #else
1340   cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
1341 #endif
1342   /* Perhaps the user specified a directory instead of a file.
1343      Check whether they did. */
1344   if (test_for_directory(cf_name) == EISDIR) {
1345         /* It's a directory - set the file selection box to display that
1346            directory, don't try to open the directory as a capture file. */
1347         set_last_open_dir(cf_name);
1348         g_free(cf_name);
1349         gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
1350         return;
1351   }
1352
1353   /* Try to open the capture file. */
1354
1355   if (!read_other_filters(cf_name, argument)) {
1356     /* We couldn't open it; don't dismiss the open dialog box,
1357        just leave it around so that the user can, after they
1358        dismiss the alert box popped up for the open error,
1359        try again. */
1360     g_free(cf_name);
1361     return;
1362   }
1363
1364   /* We've crossed the Rubicon; get rid of the file selection box. */
1365   gtk_widget_hide(GTK_WIDGET (fs));
1366   gtk_widget_destroy(GTK_WIDGET (fs));
1367
1368   /* Save the name of the containing directory specified in the path name,
1369      if any; we can write over cf_name, which is a good thing, given that
1370      "get_dirname()" does write over its argument. */
1371   s = get_dirname(cf_name);
1372   set_last_open_dir(s);
1373   gtk_widget_grab_focus(packet_list);
1374
1375   g_free(cf_name);
1376 }
1377
1378 static void
1379 file_color_import_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1380 {
1381   /* Note that we no longer have a "Open Capture File" dialog box. */
1382   file_color_import_w = NULL;
1383 }
1384
1385 static GtkWidget *file_color_export_w;
1386 /*
1387  * Set the "Export only marked filters" toggle button as appropriate for
1388  * the current output file type and count of marked filters.
1389  *
1390  * Called when the "Export" dialog box is created and when the marked
1391  * count changes.
1392  */
1393 void
1394 color_set_export_marked_sensitive(GtkWidget * cfmark_cb)
1395 {
1396   if (file_color_export_w == NULL) {
1397     /* We don't currently have an "Export" dialog box up. */
1398     return;
1399   }
1400
1401   /* We can request that only the marked filters be saved only if
1402         there *are* marked filters. */
1403   if (color_marked_count() != 0)
1404     gtk_widget_set_sensitive(cfmark_cb, TRUE);
1405   else {
1406     /* Force the "Export only marked filters" toggle to "false", turn
1407        off the flag it controls. */
1408     color_marked = FALSE;
1409     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfmark_cb), FALSE);
1410     gtk_widget_set_sensitive(cfmark_cb, FALSE);
1411   }
1412 }
1413
1414 static void
1415 color_toggle_marked_cb(GtkWidget *widget, gpointer data _U_)
1416 {
1417   color_marked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
1418 }
1419
1420 void
1421 file_color_export_cmd_cb(GtkWidget *w _U_, gpointer data _U_)
1422 {
1423   GtkWidget *main_vb, *cfglobal_but;
1424
1425   if (file_color_export_w != NULL) {
1426     /* There's already an "Color Filter Export" dialog box; reactivate it. */
1427     reactivate_window(file_color_export_w);
1428     return;
1429   }
1430
1431   /* Default to saving all packets, in the file's current format. */
1432   color_marked   = FALSE;
1433   filetype = cfile.cd_t;
1434
1435   file_color_export_w = file_selection_new("Ethereal: Export Color Filters",
1436                                            FILE_SELECTION_SAVE);
1437   SIGNAL_CONNECT(file_color_export_w, "destroy", file_color_export_destroy_cb, NULL);
1438
1439   /* If we've opened a file, start out by showing the files in the directory
1440      in which that file resided. */
1441   if (last_open_dir)
1442     file_selection_set_current_folder(file_color_export_w, last_open_dir);
1443
1444   /* Container for each row of widgets */
1445   main_vb = gtk_vbox_new(FALSE, 3);
1446   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
1447   file_selection_set_extra_widget(file_color_export_w, main_vb);
1448   gtk_widget_show(main_vb);
1449
1450   cfmark_cb = gtk_check_button_new_with_label("Export only marked filters");
1451   gtk_container_add(GTK_CONTAINER(main_vb), cfmark_cb);
1452   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfmark_cb), FALSE);
1453   SIGNAL_CONNECT(cfmark_cb, "toggled", color_toggle_marked_cb, NULL);
1454   gtk_widget_show(cfmark_cb);
1455   color_set_export_marked_sensitive(cfmark_cb);
1456
1457   cfglobal_but = gtk_button_new_with_label("Global Color Filter File");
1458   gtk_container_add(GTK_CONTAINER(main_vb), cfglobal_but);
1459   SIGNAL_CONNECT(cfglobal_but, "clicked", color_global_cb, file_color_export_w);
1460   gtk_widget_show(cfglobal_but);
1461
1462 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1463   if (gtk_dialog_run(GTK_DIALOG(file_color_export_w)) == GTK_RESPONSE_ACCEPT)
1464   {
1465       file_color_export_ok_cb(file_color_export_w, file_color_export_w);
1466   }
1467   else gtk_widget_destroy(file_color_export_w);
1468 #else
1469   /* Connect the ok_button to file_export_ok_cb function and pass along a
1470      pointer to the file selection box widget */
1471   SIGNAL_CONNECT(GTK_FILE_SELECTION (file_color_export_w)->ok_button, "clicked",
1472                  file_color_export_ok_cb, file_color_export_w);
1473
1474   /* Connect the cancel_button to destroy the widget */
1475   SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(file_color_export_w)->cancel_button,
1476                         "clicked", (GtkSignalFunc)gtk_widget_destroy,
1477                         file_color_export_w);
1478
1479   /* Catch the "key_press_event" signal in the window, so that we can catch
1480      the ESC key being pressed and act as if the "Cancel" button had
1481      been selected. */
1482   dlg_set_cancel(file_color_export_w, GTK_FILE_SELECTION(file_color_export_w)->cancel_button);
1483
1484   gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_color_export_w), "");
1485
1486   gtk_widget_show(file_color_export_w);
1487 #endif
1488 }
1489
1490 static void
1491 file_color_export_ok_cb(GtkWidget *w _U_, gpointer fs) {
1492   gchar *cf_name;
1493   gchar *dirname;
1494
1495 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1496   cf_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
1497 #else
1498   cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
1499 #endif
1500
1501   /* Perhaps the user specified a directory instead of a file.
1502      Check whether they did. */
1503   if (test_for_directory(cf_name) == EISDIR) {
1504         /* It's a directory - set the file selection box to display that
1505            directory, and leave the selection box displayed. */
1506         set_last_open_dir(cf_name);
1507         g_free(cf_name);
1508         gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
1509         return;
1510   }
1511
1512   /* Write out the filters (all, or only the ones that are currently
1513      displayed or marked) to the file with the specified name. */
1514
1515    if (!write_other_filters(cf_name, color_marked))
1516    {
1517     /* The write failed; don't dismiss the open dialog box,
1518        just leave it around so that the user can, after they
1519        dismiss the alert box popped up for the error, try again. */
1520
1521        g_free(cf_name);
1522        return;
1523    }
1524
1525   /* The write succeeded; get rid of the file selection box. */
1526   gtk_widget_hide(GTK_WIDGET (fs));
1527   gtk_widget_destroy(GTK_WIDGET (fs));
1528
1529   /* Save the directory name for future file dialogs. */
1530   dirname = get_dirname(cf_name);  /* Overwrites cf_name */
1531   set_last_open_dir(dirname);
1532   g_free(cf_name);
1533 }
1534
1535 static void
1536 file_color_export_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1537 {
1538   file_color_export_w = NULL;
1539 }