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