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