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