0d550489b712b7f66298b8b5ce0cc7dab95ccf63
[obnox/wireshark/wip.git] / gtk / filter_prefs.c
1 /* filter_prefs.c
2  * Dialog boxes for filter editing
3  * (This used to be a notebook page under "Preferences", hence the
4  * "prefs" in the file name.)
5  *
6  * $Id: filter_prefs.c,v 1.8 2000/01/29 16:41:27 gram Exp $
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@zing.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * 
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  * 
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  * 
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <gtk/gtk.h>
33
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/stat.h>
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40 #include <ctype.h>
41 #ifdef HAVE_DIRECT_H
42 #include <direct.h>
43 #endif
44
45 #include "gtk/main.h"
46 #include "filter_prefs.h"
47 #include "packet.h"
48 #include "file.h"
49 #include "util.h"
50 #include "prefs_dlg.h"
51
52 #define E_FILT_NAME_KEY "filter_name"
53 #define E_FILT_LBL_KEY  "filter_label"
54 #define E_FILT_CM_KEY   "in_cancel_mode"
55 #define E_FILTER_WIDGET_KEY "filter_widget"
56
57 typedef struct _filter_def {
58   char *name;
59   char *strval;
60 } filter_def;
61
62 typedef struct _filter_cb_data {
63   GList     *fl;
64   GtkWidget *win;
65 } filter_cb_data;
66
67
68 static GtkWidget   *filter_l, *chg_bt, *copy_bt, *del_bt, *name_te, *filter_te, *apply_bt;
69 static GList       *fl = NULL;
70
71 static void get_filter_list(void);
72 static void filter_dlg_ok(GtkWidget *ok_bt, gpointer parent_w);
73 static void filter_dlg_save(GtkWidget *save_bt, gpointer parent_w);
74 static void filter_dlg_cancel(GtkWidget *cancel_bt, gpointer parent_w);
75 static void filter_sel_apply_cb(GtkWidget *cancel_bt, gpointer parent_w);
76
77 void
78 get_filter_list() {
79   filter_def *filt;
80   FILE       *ff;
81   gchar      *ff_path, *ff_name = PF_DIR "/filters", f_buf[256];
82   gchar      *name_begin, *name_end, *filt_begin;
83   int         len, line = 0;
84
85   if (fl) return;
86   
87   /* To do: generalize this */
88   ff_path = (gchar *) g_malloc(strlen(get_home_dir()) + strlen(ff_name) +  4);
89   sprintf(ff_path, "%s/%s", get_home_dir(), ff_name);
90
91   if ((ff = fopen(ff_path, "r")) == NULL) {
92     g_free(ff_path);
93     return;
94   }
95
96   while (fgets(f_buf, 256, ff)) {
97     line++;
98     len = strlen(f_buf);
99     if (f_buf[len - 1] == '\n') {
100       len--;
101       f_buf[len] = '\0';
102     }
103     name_begin = strchr(f_buf, '"');
104     /* Empty line */
105     if (name_begin == NULL)
106       continue;
107     name_end = strchr(name_begin + 1, '"');
108     /* No terminating quote */
109     if (name_end == NULL) {
110       g_warning("Malformed filter in '%s' line %d.", ff_path, line);
111       continue;
112     }
113     name_begin++;
114     name_end[0] = '\0';
115     filt_begin  = name_end + 1;
116     while(isspace(filt_begin[0])) filt_begin++;
117     /* No filter string */
118     if (filt_begin[0] == '\0') {
119       g_warning("Malformed filter in '%s' line %d.", ff_path, line);
120       continue;
121     }
122     filt         = (filter_def *) g_malloc(sizeof(filter_def));
123     filt->name   = g_strdup(name_begin);
124     filt->strval = g_strdup(filt_begin);
125     fl = g_list_append(fl, filt);
126   }
127   fclose(ff);
128   g_free(ff_path);
129 }
130 /* the window that pops up for filter editing/applying */
131 void
132 filter_dialog_cb(GtkWidget *w)
133 {
134         GtkWidget       *main_w,        /* main window */
135                         *main_vb,       /* main container */
136                         *bbox,          /* button container */
137                         *ok_bt,         /* ok button */
138                         *save_bt,       /* save button */
139                         *cancel_bt;     /* cancel button */ 
140         GtkWidget *filter_te = NULL;    /* filter text entry */
141         GtkWidget *filter_pg = NULL;    /* filter settings box */
142
143         /* get the text entry widget from the caller */
144         if(w != NULL) {
145                 filter_te = gtk_object_get_data(GTK_OBJECT(w), E_FILT_TE_PTR_KEY);
146         }
147
148         main_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
149         gtk_window_set_title(GTK_WINDOW(main_w), "Ethereal: Filters");
150
151         main_vb = gtk_vbox_new(FALSE, 5);
152         gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
153         gtk_container_add(GTK_CONTAINER(main_w), main_vb);
154         gtk_widget_show(main_vb);
155
156         filter_pg = filter_prefs_show(filter_te);
157         gtk_box_pack_start(GTK_BOX(main_vb), filter_pg, TRUE, TRUE, 0);
158         gtk_object_set_data(GTK_OBJECT(filter_pg), E_FILT_TE_PTR_KEY, filter_te);
159         gtk_object_set_data(GTK_OBJECT(main_w), E_FILTER_WIDGET_KEY, filter_pg);
160         gtk_widget_show(filter_te);
161
162         bbox = gtk_hbutton_box_new();
163         gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
164         gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
165         gtk_container_add(GTK_CONTAINER(main_vb), bbox);
166         gtk_widget_show(bbox);
167
168         ok_bt = gtk_button_new_with_label ("OK");
169         gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
170                 GTK_SIGNAL_FUNC(filter_dlg_ok), GTK_OBJECT(main_w));
171         GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
172         gtk_box_pack_start(GTK_BOX(bbox), ok_bt, TRUE, TRUE, 0);
173         gtk_widget_grab_default(ok_bt);
174         gtk_widget_show(ok_bt);
175
176         save_bt = gtk_button_new_with_label ("Save");
177         gtk_signal_connect(GTK_OBJECT(save_bt), "clicked",
178                 GTK_SIGNAL_FUNC(filter_dlg_save), GTK_OBJECT(main_w));
179         GTK_WIDGET_SET_FLAGS(save_bt, GTK_CAN_DEFAULT);
180         gtk_box_pack_start(GTK_BOX(bbox), save_bt, TRUE, TRUE, 0);
181         gtk_widget_show(save_bt);
182
183         cancel_bt = gtk_button_new_with_label ("Cancel");
184         gtk_signal_connect(GTK_OBJECT(cancel_bt), "clicked",
185                 GTK_SIGNAL_FUNC(filter_dlg_cancel), GTK_OBJECT(main_w));
186         GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
187         gtk_box_pack_start(GTK_BOX(bbox), cancel_bt, TRUE, TRUE, 0);
188         gtk_widget_show(cancel_bt);
189
190
191         gtk_widget_show(main_w);
192 }
193
194 static void
195 filter_dlg_ok(GtkWidget *ok_bt, gpointer parent_w)
196 {
197         filter_prefs_ok(gtk_object_get_data(GTK_OBJECT(parent_w), E_FILTER_WIDGET_KEY));
198         gtk_widget_destroy(GTK_WIDGET(parent_w));
199 }
200
201 static void
202 filter_dlg_save(GtkWidget *save_bt, gpointer parent_w)
203 {
204         filter_prefs_save(gtk_object_get_data(GTK_OBJECT(parent_w), E_FILTER_WIDGET_KEY));
205 }
206
207 static void
208 filter_dlg_cancel(GtkWidget *cancel_bt, gpointer parent_w)
209 {
210         filter_prefs_cancel(gtk_object_get_data(GTK_OBJECT(parent_w),  E_FILTER_WIDGET_KEY));
211         gtk_widget_destroy(GTK_WIDGET(parent_w));
212 }
213         
214 /* Create and display the filter selection widgets. */
215 GtkWidget *
216 filter_prefs_show(GtkWidget *w) {
217   GtkWidget  *main_vb, *top_hb, *list_bb, *new_bt, *filter_sc,
218              *nl_item, *nl_lb, *middle_hb, *name_lb, *bottom_hb,
219              *filter_lb;
220   GtkWidget  *l_select = NULL;
221   GList      *flp = NULL;
222   filter_def *filt;
223   gchar      *filter_te_str = NULL;
224
225   /* Make sure everything is set up */  
226   get_filter_list();
227   if (w)
228     filter_te_str = gtk_entry_get_text(GTK_ENTRY(w));
229
230   /* Container for each row of widgets */
231   main_vb = gtk_vbox_new(FALSE, 5);
232   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
233   gtk_widget_show(main_vb);
234   gtk_object_set_data(GTK_OBJECT(main_vb), E_FILT_CM_KEY, (gpointer)FALSE);
235   
236   /* Top row: Filter list and buttons */
237   top_hb = gtk_hbox_new(FALSE, 5);
238   gtk_container_add(GTK_CONTAINER(main_vb), top_hb);
239   gtk_widget_show(top_hb);
240   
241   list_bb = gtk_vbutton_box_new();
242   gtk_button_box_set_layout (GTK_BUTTON_BOX (list_bb), GTK_BUTTONBOX_START);
243   gtk_container_add(GTK_CONTAINER(top_hb), list_bb);
244   gtk_widget_show(list_bb);
245
246   new_bt = gtk_button_new_with_label ("New");
247   gtk_signal_connect(GTK_OBJECT(new_bt), "clicked",
248     GTK_SIGNAL_FUNC(filter_sel_new_cb), NULL);
249   gtk_container_add(GTK_CONTAINER(list_bb), new_bt);
250   gtk_widget_show(new_bt);
251   
252   chg_bt = gtk_button_new_with_label ("Change");
253   gtk_widget_set_sensitive(chg_bt, FALSE);
254   gtk_signal_connect(GTK_OBJECT(chg_bt), "clicked",
255     GTK_SIGNAL_FUNC(filter_sel_chg_cb), NULL);
256   gtk_container_add(GTK_CONTAINER(list_bb), chg_bt);
257   gtk_widget_show(chg_bt);
258   
259   copy_bt = gtk_button_new_with_label ("Copy");
260   gtk_widget_set_sensitive(copy_bt, FALSE);
261   gtk_signal_connect(GTK_OBJECT(copy_bt), "clicked",
262     GTK_SIGNAL_FUNC(filter_sel_copy_cb), NULL);
263   gtk_container_add(GTK_CONTAINER(list_bb), copy_bt);
264   gtk_widget_show(copy_bt);
265   
266   del_bt = gtk_button_new_with_label ("Delete");
267   gtk_widget_set_sensitive(del_bt, FALSE);
268   gtk_signal_connect(GTK_OBJECT(del_bt), "clicked",
269     GTK_SIGNAL_FUNC(filter_sel_del_cb), NULL);
270   gtk_container_add(GTK_CONTAINER(list_bb), del_bt);
271   gtk_widget_show(del_bt);
272
273   apply_bt = gtk_button_new_with_label("Apply");
274   gtk_widget_set_sensitive(apply_bt, FALSE);
275   gtk_signal_connect(GTK_OBJECT(apply_bt), "clicked",
276     GTK_SIGNAL_FUNC(filter_sel_apply_cb), w);
277   gtk_container_add(GTK_CONTAINER(list_bb), apply_bt);
278   gtk_widget_show(apply_bt);
279
280   filter_sc = gtk_scrolled_window_new(NULL, NULL);
281   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(filter_sc),
282     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
283   gtk_widget_set_usize(filter_sc, 250, 150);
284   gtk_container_add(GTK_CONTAINER(top_hb), filter_sc);
285   gtk_widget_show(filter_sc);
286
287   filter_l = gtk_list_new();
288   gtk_list_set_selection_mode(GTK_LIST(filter_l), GTK_SELECTION_SINGLE);
289   gtk_signal_connect(GTK_OBJECT(filter_l), "selection_changed",
290     GTK_SIGNAL_FUNC(filter_sel_list_cb), main_vb);
291   gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(filter_sc),
292     filter_l);
293   gtk_widget_show(filter_l);
294
295   flp = g_list_first(fl);
296   while (flp) {
297     filt    = (filter_def *) flp->data;
298     nl_lb   = gtk_label_new(filt->name);
299     nl_item = gtk_list_item_new();
300     gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
301     gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
302     gtk_widget_show(nl_lb);
303     gtk_container_add(GTK_CONTAINER(filter_l), nl_item);
304     gtk_widget_show(nl_item);
305     gtk_object_set_data(GTK_OBJECT(nl_item), E_FILT_LBL_KEY, nl_lb);
306     gtk_object_set_data(GTK_OBJECT(nl_item), E_FILT_NAME_KEY, flp);
307  
308     if (filter_te_str && filt->strval)
309       if (strcmp(filter_te_str, filt->strval) == 0)
310         l_select = nl_item;
311
312     flp = flp->next;
313   }
314   
315   /* Middle row: Filter name entry */
316   middle_hb = gtk_hbox_new(FALSE, 5);
317   gtk_container_add(GTK_CONTAINER(main_vb), middle_hb);
318   gtk_widget_show(middle_hb);
319   
320   name_lb = gtk_label_new("Filter name:");
321   gtk_box_pack_start(GTK_BOX(middle_hb), name_lb, FALSE, FALSE, 3);
322   gtk_widget_show(name_lb);
323   
324   name_te = gtk_entry_new();
325   gtk_box_pack_start(GTK_BOX(middle_hb), name_te, TRUE, TRUE, 3);
326   gtk_widget_show(name_te);
327
328   /* Bottom row: Filter text entry */
329   bottom_hb = gtk_hbox_new(FALSE, 5);
330   gtk_container_add(GTK_CONTAINER(main_vb), bottom_hb);
331   gtk_widget_show(bottom_hb);
332   
333   filter_lb = gtk_label_new("Filter string:");
334   gtk_box_pack_start(GTK_BOX(bottom_hb), filter_lb, FALSE, FALSE, 3);
335   gtk_widget_show(filter_lb);
336   
337   filter_te = gtk_entry_new();
338   gtk_box_pack_start(GTK_BOX(bottom_hb), filter_te, TRUE, TRUE, 3);
339   gtk_widget_show(filter_te);
340
341   if (l_select)
342   {
343     gtk_list_select_child(GTK_LIST(filter_l), l_select);
344   } else if (filter_te_str && filter_te_str[0]) {
345     gtk_entry_set_text(GTK_ENTRY(name_te), "New filter");
346     gtk_entry_set_text(GTK_ENTRY(filter_te), filter_te_str);
347   }
348     
349   return(main_vb);
350 }
351
352 void
353 filter_sel_list_cb(GtkWidget *l, gpointer data) {
354   filter_def *filt;
355   gchar      *name = "", *strval = "";
356   GList      *sl, *flp;
357   GtkObject  *l_item;
358   gint        sensitivity = FALSE;
359
360   if (l)
361           sl = GTK_LIST(l)->selection;
362   else
363           sl = NULL;
364           
365   if (sl) {  /* Something was selected */
366     l_item = GTK_OBJECT(sl->data);
367     flp    = (GList *) gtk_object_get_data(l_item, E_FILT_NAME_KEY);
368     if (flp) {
369       filt   = (filter_def *) flp->data;
370       name   = filt->name;
371       strval = filt->strval;
372       sensitivity = TRUE;
373     }
374   }
375
376   /* Did you know that this function is called when the window is destroyed? */
377   /* Funny, that. */
378   if (!gtk_object_get_data(GTK_OBJECT(data), E_FILT_CM_KEY)) {
379     gtk_entry_set_text(GTK_ENTRY(name_te), name);
380     gtk_entry_set_text(GTK_ENTRY(filter_te), strval);
381     gtk_widget_set_sensitive(chg_bt, sensitivity);
382     gtk_widget_set_sensitive(copy_bt, sensitivity);
383     gtk_widget_set_sensitive(del_bt, sensitivity);
384     gtk_widget_set_sensitive(apply_bt, sensitivity);
385   }
386 }
387
388 /* To do: add input checking to each of these callbacks */
389  
390 void
391 filter_sel_new_cb(GtkWidget *w, gpointer data) {
392   filter_def *filt;
393   gchar      *name, *strval;
394   GtkWidget  *nl_item, *nl_lb;
395   
396   name   = gtk_entry_get_text(GTK_ENTRY(name_te));
397   strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
398   
399   if (strlen(name) > 0 && strlen(strval) > 0) {
400     filt         = (filter_def *) g_malloc(sizeof(filter_def));
401     filt->name   = g_strdup(name);
402     filt->strval = g_strdup(strval);
403     fl           = g_list_append(fl, filt);
404     nl_lb        = gtk_label_new(filt->name);
405     nl_item      = gtk_list_item_new();
406     gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
407     gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
408     gtk_widget_show(nl_lb);
409     gtk_container_add(GTK_CONTAINER(filter_l), nl_item);
410     gtk_widget_show(nl_item);
411     gtk_object_set_data(GTK_OBJECT(nl_item), E_FILT_LBL_KEY, nl_lb);
412     gtk_object_set_data(GTK_OBJECT(nl_item), E_FILT_NAME_KEY, g_list_last(fl));
413     gtk_list_select_child(GTK_LIST(filter_l), nl_item);
414   }
415 }
416
417 void
418 filter_sel_chg_cb(GtkWidget *w, gpointer data) {
419   filter_def *filt;
420   gchar      *name = "", *strval = "";
421   GList      *sl, *flp;
422   GtkObject  *l_item;
423   GtkLabel   *nl_lb;
424
425   sl     = GTK_LIST(filter_l)->selection;
426   name   = gtk_entry_get_text(GTK_ENTRY(name_te));
427   strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
428
429   if (sl) {  /* Something was selected */
430     l_item = GTK_OBJECT(sl->data);
431     flp    = (GList *) gtk_object_get_data(l_item, E_FILT_NAME_KEY);
432     nl_lb  = (GtkLabel *) gtk_object_get_data(l_item, E_FILT_LBL_KEY);
433     if (flp && nl_lb) {
434       filt = (filter_def *) flp->data;
435       
436       if (strlen(name) > 0 && strlen(strval) > 0 && filt) {
437         g_free(filt->name);
438         g_free(filt->strval);
439         filt->name   = g_strdup(name);
440         filt->strval = g_strdup(strval);
441         gtk_label_set(nl_lb, filt->name);
442       }
443     }
444   }
445 }
446
447 void
448 filter_sel_copy_cb(GtkWidget *w, gpointer data) {
449   GList      *sl, *flp;
450   filter_def *filt, *nfilt;
451   gchar      *prefix = "Copy of ";
452   GtkObject  *l_item;
453   GtkWidget  *nl_item, *nl_lb;
454   
455   sl     = GTK_LIST(filter_l)->selection;
456   if (sl) {  /* Something was selected */
457     l_item = GTK_OBJECT(sl->data);
458     flp    = (GList *) gtk_object_get_data(l_item, E_FILT_NAME_KEY);
459     if (flp) {
460       filt          = (filter_def *) flp->data;
461       nfilt         = (filter_def *) g_malloc(sizeof(filter_def));
462       nfilt->name   = g_malloc(strlen(prefix) + strlen(filt->name) + 1);
463       sprintf(nfilt->name, "%s%s", prefix, filt->name);
464       nfilt->strval = g_strdup(filt->strval);
465       fl            = g_list_append(fl, nfilt);
466       nl_lb         = gtk_label_new(nfilt->name);
467       nl_item       = gtk_list_item_new();
468       gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
469       gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
470       gtk_widget_show(nl_lb);
471       gtk_container_add(GTK_CONTAINER(filter_l), nl_item);
472       gtk_widget_show(nl_item);
473       gtk_object_set_data(GTK_OBJECT(nl_item), E_FILT_LBL_KEY, nl_lb);
474       gtk_object_set_data(GTK_OBJECT(nl_item), E_FILT_NAME_KEY, g_list_last(fl));
475       gtk_list_select_child(GTK_LIST(filter_l), nl_item);
476     }
477   }
478 }
479
480 void
481 filter_sel_del_cb(GtkWidget *w, gpointer data) {
482   GList      *sl, *flp;
483   filter_def *filt;
484   GtkObject  *l_item;
485   gint        pos;
486   
487   sl = GTK_LIST(filter_l)->selection;
488   if (sl) {  /* Something was selected */
489     l_item = GTK_OBJECT(sl->data);
490     pos    = gtk_list_child_position(GTK_LIST(filter_l),
491       GTK_WIDGET(l_item));
492     flp    = (GList *) gtk_object_get_data(l_item, E_FILT_NAME_KEY);
493     if (flp) {
494       filt = (filter_def *) flp->data;
495       g_free(filt->name);
496       g_free(filt->strval);
497       g_free(filt);
498       fl = g_list_remove_link(fl, flp);
499       gtk_list_clear_items(GTK_LIST(filter_l), pos, pos + 1);
500     } 
501   }
502 }
503
504 void
505 filter_sel_apply_cb(GtkWidget *w, gpointer data)
506 {
507         GList      *flp, *sl;
508         GtkObject  *l_item;
509         filter_def *filt;
510         GtkWidget  *mw_filt = data;
511         
512         sl = GTK_LIST(filter_l)->selection;
513         if (sl != NULL && mw_filt != NULL) {  /* Place something in the filter box. */
514                 l_item = GTK_OBJECT(sl->data);
515                 flp    = (GList *) gtk_object_get_data(l_item, E_FILT_NAME_KEY);
516                 if (flp) {
517                         filt = (filter_def *) flp->data;
518                         gtk_entry_set_text(GTK_ENTRY(mw_filt), filt->strval);
519                         gtk_signal_emit_by_name(GTK_OBJECT(mw_filt), "activate");
520                 }
521         }
522 }
523
524 void
525 filter_prefs_ok(GtkWidget *w) {
526   GList      *flp, *sl;
527   GtkObject  *l_item;
528   filter_def *filt;
529   GtkWidget  *mw_filt = gtk_object_get_data(GTK_OBJECT(w), E_FILT_TE_PTR_KEY);
530
531   sl = GTK_LIST(filter_l)->selection;
532   if (sl && mw_filt) {  /* Place something in the filter box. */
533     l_item = GTK_OBJECT(sl->data);
534     flp    = (GList *) gtk_object_get_data(l_item, E_FILT_NAME_KEY);
535     if (flp) {
536       filt = (filter_def *) flp->data;
537       gtk_entry_set_text(GTK_ENTRY(mw_filt), filt->strval);
538     }
539   }
540
541   filter_prefs_delete(w);
542 }
543
544 void
545 filter_prefs_save(GtkWidget *w) {
546   GList       *flp;
547   filter_def  *filt;
548   gchar       *ff_path, *ff_dir = PF_DIR, *ff_name = "filters";
549   FILE        *ff;
550   struct stat  s_buf;
551   
552   ff_path = (gchar *) g_malloc(strlen(get_home_dir()) + strlen(ff_dir) +  
553     strlen(ff_name) + 4);
554   sprintf(ff_path, "%s/%s", get_home_dir(), ff_dir);
555
556   if (stat(ff_path, &s_buf) != 0)
557 #ifdef WIN32
558     mkdir(ff_path);
559 #else
560     mkdir(ff_path, 0755);
561 #endif
562     
563   sprintf(ff_path, "%s/%s/%s", get_home_dir(), ff_dir, ff_name);
564
565   if ((ff = fopen(ff_path, "w")) != NULL) {
566     flp = g_list_first(fl);
567     while (flp) {
568       filt = (filter_def *) flp->data;
569       fprintf(ff, "\"%s\" %s\n", filt->name, filt->strval);
570       flp = flp->next;
571     }
572     fclose(ff);
573   }
574
575   g_free(ff_path);
576 }
577
578 void
579 filter_prefs_cancel(GtkWidget *w) {
580
581   filter_prefs_delete(w);
582 }
583
584 void
585 filter_prefs_delete(GtkWidget *w) {
586  
587   /* Let the list cb know we're about to destroy the widget tree, so it */
588   /* doesn't operate on widgets that don't exist. */  
589   gtk_object_set_data(GTK_OBJECT(w), E_FILT_CM_KEY, (gpointer)TRUE);
590   gtk_widget_destroy(GTK_WIDGET(w));
591