Make the routines internal to the filter-editing dialog box static.
[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.19 2000/02/12 06:46:52 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * 
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
33
34 #ifdef HAVE_DIRECT_H
35 #include <direct.h>
36 #endif
37
38 #include <string.h>
39
40 #ifndef __GLOBALS_H__
41 #include "globals.h"
42 #endif
43 #ifndef __GTKGLOBALS_H__
44 #include "gtkglobals.h"
45 #endif
46
47 #ifndef __KEYS_H__
48 #include "keys.h"
49 #endif
50
51 #include "filter_prefs.h"
52
53 #ifndef __DIALOG_H__
54 #include "simple_dialog.h"
55 #endif
56
57 #ifndef __MENU_H__
58 #include "menu.h"
59 #endif
60
61 #ifndef __MAIN_H__
62 #include "main.h"
63 #endif
64
65 #ifndef __UTIL_H__
66 #include "util.h"
67 #endif
68
69 static void file_open_ok_cb(GtkWidget *w, GtkFileSelection *fs);
70 static void select_file_type_cb(GtkWidget *w, gpointer data);
71 static void file_save_as_ok_cb(GtkWidget *w, GtkFileSelection *fs);
72
73 /* Open a file */
74 void
75 file_open_cmd_cb(GtkWidget *w, gpointer data) {
76   GtkWidget *filter_hbox, *filter_bt, *filter_te;
77
78   /* XXX - GTK+'s file selection dialog box doesn't let you set the
79      initial directory it should show; it always uses the current
80      directory.  We want to start out by showing the user the files
81      in the last directory in which they looked, so we have to "chdir()"
82      there.
83
84      This means means that if Ethereal dumps core, the core file will be
85      dumped in whatever directory it last "chdir()"red to, rather
86      than in the directory in which you started it.
87
88      It also means, for better or worse, that *all* file selection
89      dialog boxes will start in that directory. */
90   if (last_open_dir)
91           chdir(last_open_dir);
92
93   file_sel = gtk_file_selection_new ("Ethereal: Open Capture File");
94   
95   /* Connect the ok_button to file_open_ok_cb function and pass along a
96      pointer to the file selection box widget */
97   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (file_sel)->ok_button),
98     "clicked", (GtkSignalFunc) file_open_ok_cb, file_sel );
99
100   gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_sel)->ok_button),
101       E_DFILTER_TE_KEY, gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY));
102
103   filter_hbox = gtk_hbox_new(FALSE, 1);
104   gtk_container_border_width(GTK_CONTAINER(filter_hbox), 0);
105   gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(file_sel)->action_area),
106     filter_hbox, FALSE, FALSE, 0);
107   gtk_widget_show(filter_hbox);
108
109   filter_bt = gtk_button_new_with_label("Filter:");
110   gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
111     GTK_SIGNAL_FUNC(filter_dialog_cb), NULL);
112   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
113   gtk_widget_show(filter_bt);
114   
115   filter_te = gtk_entry_new();
116   gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
117   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
118   gtk_widget_show(filter_te);
119
120   gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_sel)->ok_button),
121     E_RFILTER_TE_KEY, filter_te);
122
123   /* Connect the cancel_button to destroy the widget */
124   gtk_signal_connect_object(GTK_OBJECT (GTK_FILE_SELECTION
125     (file_sel)->cancel_button), "clicked", (GtkSignalFunc)
126     gtk_widget_destroy, GTK_OBJECT (file_sel));
127
128   gtk_widget_show(file_sel);
129 }
130
131 static void
132 file_open_ok_cb(GtkWidget *w, GtkFileSelection *fs) {
133   gchar     *cf_name, *rfilter, *s;
134   GtkWidget *filter_te;
135   dfilter   *rfcode = NULL;
136   int        err;
137
138   cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
139   filter_te = gtk_object_get_data(GTK_OBJECT(w), E_RFILTER_TE_KEY);
140   rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
141   if (dfilter_compile(rfilter, &rfcode) != 0) {
142     simple_dialog(ESD_TYPE_WARN, NULL, dfilter_error_msg);
143     return;
144   }
145
146   /* Perhaps the user specified a directory instead of a file.
147    * See if we can chdir to it. */
148   if (chdir(cf_name) == 0) {
149         int     cf_name_len;
150
151         g_free(last_open_dir);
152
153         /* Append a slash, even if not needed */
154         cf_name_len = strlen(cf_name);
155         last_open_dir = g_malloc(cf_name_len + 2);
156         strcpy(last_open_dir, cf_name);
157         strcat(last_open_dir, "/");
158         g_free(cf_name);
159         gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
160         return;
161   }
162
163   /* Try to open the capture file. */
164   if ((err = open_cap_file(cf_name, FALSE, &cf)) != 0) {
165     /* We couldn't open it; don't dismiss the open dialog box,
166        just leave it around so that the user can, after they
167        dismiss the alert box popped up for the open error,
168        try again. */
169     if (rfcode != NULL)
170       dfilter_destroy(rfcode);
171     g_free(cf_name);
172     return;
173   }
174
175   /* Attach the new read filter to "cf" ("open_cap_file()" succeeded, so
176      it closed the previous capture file, and thus destroyed any
177      previous read filter attached to "cf"). */
178   cf.rfcode = rfcode;
179
180   /* We've crossed the Rubicon; get rid of the file selection box. */
181   gtk_widget_hide(GTK_WIDGET (fs));
182   gtk_widget_destroy(GTK_WIDGET (fs));
183
184   err = read_cap_file(&cf);
185   /* Save the name of the containing directory specified in the path name,
186      if any; we can write over cf_name, which is a good thing, given that
187      "get_dirname()" does write over its argument. */
188   s = get_dirname(cf_name);
189   if (s != NULL) {
190     /* Well, there is a directory in there... */
191     if (last_open_dir != NULL) {
192       /* ...and we already have one saved... */
193       if (strcmp(last_open_dir, s) != 0) {
194         /* ...and it's not the same as this one, so free the old one
195            and assign a copy of the new one to it. */
196         g_free(last_open_dir);
197         last_open_dir = g_strdup(s);
198       }
199     } else {
200       /* ...and we don't already have one saved, so just save this one. */
201       last_open_dir = g_strdup(s);
202     }
203   } else {
204     /* There was no directory in there. */
205     last_open_dir = NULL;
206   }
207   g_free(cf_name);
208 }
209
210 /* Close a file */
211 void
212 file_close_cmd_cb(GtkWidget *widget, gpointer data) {
213   close_cap_file(&cf, info_bar);
214 }
215
216 void
217 file_save_cmd_cb(GtkWidget *w, gpointer data) {
218   file_sel = gtk_file_selection_new ("Ethereal: Save Capture File");
219
220   /* If the file's already been saved, do nothing.  */
221   if (cf.user_saved)
222     return;
223
224   /* Do a "Save As". */
225   file_save_as_cmd_cb(w, data);
226 }
227
228 /* XXX - can we make these not be static? */
229 static gboolean filtered;
230 static int filetype;
231 static GtkWidget *filter_cb;
232 static GtkWidget *ft_om;
233
234 static gboolean
235 can_save_with_wiretap(int ft)
236 {
237   /* To save a file with Wiretap, Wiretap has to handle that format,
238      and its code to handle that format must be able to write a file
239      with this file's encapsulation type. */
240   return wtap_dump_can_open(ft) && wtap_dump_can_write_encap(ft, cf.lnk_t);
241 }
242
243 /* Generate a list of the file types we can save this file as.
244
245    "filetype" is the type it has now.
246
247    "encap" is the encapsulation for its packets (which could be
248    "unknown" or "per-packet").
249
250    "filtered" is TRUE if we're to save only the packets that passed
251    the display filter (in which case we have to save it using Wiretap)
252    and FALSE if we're to save the entire file (in which case, if we're
253    saving it in the type it has already, we can just copy it). */
254 static void
255 set_file_type_list(GtkWidget *option_menu)
256 {
257   GtkWidget *ft_menu, *ft_menu_item;
258   int ft;
259   guint index;
260   guint item_to_select;
261
262   /* Default to the first supported file type, if the file's current
263      type isn't supported. */
264   item_to_select = 0;
265
266   ft_menu = gtk_menu_new();
267
268   /* Check all file types. */
269   index = 0;
270   for (ft = 0; ft < WTAP_NUM_FILE_TYPES; ft++) {
271     if (filtered || ft != cf.cd_t) {
272       /* Filtered, or a different file type.  We have to use Wiretap. */
273       if (!can_save_with_wiretap(ft))
274         continue;       /* We can't. */
275     }
276
277     /* OK, we can write it out in this type. */
278     ft_menu_item = gtk_menu_item_new_with_label(wtap_file_type_string(ft));
279     if (ft == filetype) {
280       /* Default to the same format as the file, if it's supported. */
281       item_to_select = index;
282     }
283     gtk_signal_connect(GTK_OBJECT(ft_menu_item), "activate",
284       GTK_SIGNAL_FUNC(select_file_type_cb), (gpointer)ft);
285     gtk_menu_append(GTK_MENU(ft_menu), ft_menu_item);
286     gtk_widget_show(ft_menu_item);
287     index++;
288   }
289
290   gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), ft_menu);
291   gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu), item_to_select);
292 }
293
294 static void
295 select_file_type_cb(GtkWidget *w, gpointer data)
296 {
297   int new_filetype = (int)data;
298
299   if (filetype != new_filetype) {
300     /* We can select only the filtered packets to be saved iff we can
301        use Wiretap to save the file. */
302     gtk_widget_set_sensitive(filter_cb, can_save_with_wiretap(new_filetype));
303     filetype = new_filetype;
304   }
305 }
306
307 static void
308 toggle_filtered_cb(GtkWidget *widget, gpointer data)
309 {
310   gboolean new_filtered;
311
312   new_filtered = GTK_TOGGLE_BUTTON (widget)->active;
313
314   if (filtered != new_filtered) {
315     /* They changed the state of the "filtered" button. */
316     filtered = new_filtered;
317     set_file_type_list(ft_om);
318   }
319 }
320
321 void
322 file_save_as_cmd_cb(GtkWidget *w, gpointer data)
323 {
324   GtkWidget *ok_bt, *main_vb, *ft_hb, *ft_lb;
325
326   /* Default to saving all packets, in the file's current format. */
327   filtered = FALSE;
328   filetype = cf.cd_t;
329
330   file_sel = gtk_file_selection_new ("Ethereal: Save Capture File As");
331
332   /* Connect the ok_button to file_save_as_ok_cb function and pass along a
333      pointer to the file selection box widget */
334   ok_bt = GTK_FILE_SELECTION (file_sel)->ok_button;
335   gtk_signal_connect (GTK_OBJECT (ok_bt), "clicked",
336     (GtkSignalFunc) file_save_as_ok_cb, file_sel );
337
338   /* Container for each row of widgets */
339   main_vb = gtk_vbox_new(FALSE, 3);
340   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
341   gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(file_sel)->action_area),
342     main_vb, FALSE, FALSE, 0);
343   gtk_widget_show(main_vb);
344   
345   filter_cb = gtk_check_button_new_with_label("Save only packets currently being displayed");
346   gtk_container_add(GTK_CONTAINER(main_vb), filter_cb);
347   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(filter_cb), FALSE);
348   gtk_signal_connect(GTK_OBJECT(filter_cb), "toggled",
349                         GTK_SIGNAL_FUNC(toggle_filtered_cb), NULL);
350   gtk_widget_set_sensitive(filter_cb, can_save_with_wiretap(filetype));
351   gtk_widget_show(filter_cb);
352
353   /* File type row */
354   ft_hb = gtk_hbox_new(FALSE, 3);
355   gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
356   gtk_widget_show(ft_hb);
357   
358   ft_lb = gtk_label_new("File type:");
359   gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
360   gtk_widget_show(ft_lb);
361
362   ft_om = gtk_option_menu_new();
363
364   /* Generate the list of file types we can save. */
365   set_file_type_list(ft_om);
366   gtk_box_pack_start(GTK_BOX(ft_hb), ft_om, FALSE, FALSE, 0);
367   gtk_widget_show(ft_om);
368
369   /* Connect the cancel_button to destroy the widget */
370   gtk_signal_connect_object(GTK_OBJECT (GTK_FILE_SELECTION
371     (file_sel)->cancel_button), "clicked", (GtkSignalFunc)
372     gtk_widget_destroy, GTK_OBJECT (file_sel));
373
374   gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_sel), "");
375   gtk_widget_show(file_sel);
376 }
377
378 static void
379 file_save_as_ok_cb(GtkWidget *w, GtkFileSelection *fs) {
380   gchar *cf_name;
381
382   cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
383   gtk_widget_hide(GTK_WIDGET (fs));
384   gtk_widget_destroy(GTK_WIDGET (fs));
385
386   /* Write out the packets (all, or only the ones that are currently
387      displayed) to the file with the specified name. */
388   save_cap_file(cf_name, &cf, filtered, filetype);
389
390   /* If "save_cap_file()" saved the file name we handed it, it saved
391      a copy, so we should free up our copy. */
392   g_free(cf_name);
393 }
394
395 /* Reload a file using the current read and display filters */
396 void
397 file_reload_cmd_cb(GtkWidget *w, gpointer data) {
398   /*GtkWidget *filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);*/
399   GtkWidget *filter_te;
400   gchar *filename;
401   gboolean is_tempfile;
402
403   filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
404
405   if (cf.dfilter)
406     g_free(cf.dfilter);
407   cf.dfilter = g_strdup(gtk_entry_get_text(GTK_ENTRY(filter_te)));
408
409   /* If the file could be opened, "open_cap_file()" calls "close_cap_file()"
410      to get rid of state for the old capture file before filling in state
411      for the new capture file.  "close_cap_file()" will remove the file if
412      it's a temporary file; we don't want that to happen (for one thing,
413      it'd prevent subsequent reopens from working).  Remember whether it's
414      a temporary file, mark it as not being a temporary file, and then
415      reopen it as the type of file it was.
416
417      Also, "close_cap_file()" will free "cf.filename", so we must make
418      a copy of it first. */
419   filename = strdup(cf.filename);
420   is_tempfile = cf.is_tempfile;
421   cf.is_tempfile = FALSE;
422   if (open_cap_file(filename, is_tempfile, &cf) == 0)
423     read_cap_file(&cf);
424   else {
425     /* The open failed, so "cf.is_tempfile" wasn't set to "is_tempfile".
426        Instead, the file was left open, so we should restore "cf.is_tempfile"
427        ourselves.
428
429        XXX - change the menu?  Presumably "open_cap_file()" will do that;
430        make sure it does! */
431     cf.is_tempfile = is_tempfile;
432   }
433   /* "open_cap_file()" made a copy of the file name we handed it, so
434      we should free up our copy. */
435   g_free(filename);
436 }