- add a new GUI preference in the gtk2 port. It allows to use
[obnox/wireshark/wip.git] / gtk2 / 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.5 2002/09/14 10:07:39 oabad Exp $
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@ethereal.com>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <string.h>
32
33 #include <gtk/gtk.h>
34
35 #include <epan/filesystem.h>
36
37 #include "filters.h"
38 #include "gtk/main.h"
39 #include "filter_prefs.h"
40 #include "dlg_utils.h"
41 #include "ui_util.h"
42 #include "simple_dialog.h"
43 #include "dfilter_expr_dlg.h"
44
45 #define E_FILT_PARENT_FILTER_TE_KEY "filter_parent_filter_te"
46 #define E_FILT_CONSTRUCT_ARGS_KEY   "filter_construct_args"
47 #define E_FILT_LIST_ITEM_MODEL_KEY  "filter_list_item_model"
48 #define E_FILT_LBL_KEY              "filter_label"
49 #define E_FILT_FILTER_L_KEY         "filter_filter_l"
50 #define E_FILT_CHG_BT_KEY           "filter_chg_bt"
51 #define E_FILT_COPY_BT_KEY          "filter_copy_bt"
52 #define E_FILT_DEL_BT_KEY           "filter_del_bt"
53 #define E_FILT_NAME_TE_KEY          "filter_name_te"
54 #define E_FILT_FILTER_TE_KEY        "filter_filter_te"
55 #define E_FILT_DBLFUNC_KEY          "filter_dblfunc"
56 #define E_FILT_DBLARG_KEY           "filter_dblarg"
57 #define E_FILT_DBLACTIVATE_KEY      "filter_dblactivate"
58
59 typedef struct _filter_cb_data {
60     GList     *fl;
61     GtkWidget *win;
62 } filter_cb_data;
63
64 static GtkWidget *filter_dialog_new(GtkWidget *caller, GtkWidget *filter_te,
65                                     filter_list_type_t list,
66                                     construct_args_t *construct_args);
67 static void filter_dlg_dclick(GtkWidget *dummy, gpointer main_w_arg,
68                               gpointer activate);
69 static void filter_dlg_ok_cb(GtkWidget *ok_bt, gpointer dummy);
70 static void filter_dlg_apply_cb(GtkWidget *apply_bt, gpointer dummy);
71 static void filter_apply(GtkWidget *main_w, gboolean destroy);
72 static void filter_dlg_save_cb(GtkWidget *save_bt, gpointer parent_w);
73 static void filter_dlg_close_cb(GtkWidget *close_bt, gpointer parent_w);
74 static void filter_dlg_destroy(GtkWidget *win, gpointer data);
75
76 static gint filter_sel_list_button_cb(GtkWidget *, GdkEventButton *,
77                                       gpointer);
78 static void filter_sel_list_cb(GtkTreeSelection *, gpointer);
79 static void filter_list_destroy_cb(GtkWidget *, gpointer);
80 static void filter_new_bt_clicked_cb(GtkWidget *, gpointer);
81 static void filter_chg_bt_clicked_cb(GtkWidget *, gpointer);
82 static void filter_chg_bt_destroy_cb(GtkWidget *, gpointer);
83 static void filter_copy_bt_clicked_cb(GtkWidget *, gpointer);
84 static void filter_copy_bt_destroy_cb(GtkWidget *, gpointer);
85 static void filter_del_bt_clicked_cb(GtkWidget *, gpointer);
86 static void filter_del_bt_destroy_cb(GtkWidget *, gpointer);
87 static void filter_expr_cb(GtkWidget *, gpointer);
88 static void filter_name_te_destroy_cb(GtkWidget *, gpointer);
89 static void filter_filter_te_destroy_cb(GtkWidget *, gpointer);
90
91 #ifdef HAVE_LIBPCAP
92 /* Create a filter dialog for constructing a capture filter.
93
94    This is to be used as a callback for a button next to a text entry box,
95    which, when clicked, pops up this dialog to allow you to construct a
96    display filter by browsing the list of saved filters (the dialog
97    for constructing expressions assumes display filter syntax, not
98    capture filter syntax).  The "OK" button sets the text entry box to the
99    constructed filter and activates that text entry box (which should have
100    no effect in the main capture dialog); this dialog is then dismissed. */
101 void
102 capture_filter_construct_cb(GtkWidget *w, gpointer user_data _U_)
103 {
104     GtkWidget *caller = gtk_widget_get_toplevel(w);
105     GtkWidget *filter_browse_w;
106     GtkWidget *parent_filter_te;
107     /* No Apply button, and "OK" just sets our text widget, it doesn't
108        activate it (i.e., it doesn't cause us to try to open the file). */
109     static construct_args_t args = {
110         "Ethereal: Capture Filter",
111         FALSE,
112         FALSE
113     };
114
115     /* Has a filter dialog box already been opened for that top-level
116        widget? */
117     filter_browse_w = gtk_object_get_data(GTK_OBJECT(caller),
118                                           E_FILT_DIALOG_PTR_KEY);
119
120     if (filter_browse_w != NULL) {
121         /* Yes.  Just re-activate that dialog box. */
122         reactivate_window(filter_browse_w);
123         return;
124     }
125
126     /* No.  Get the text entry attached to the button. */
127     parent_filter_te = gtk_object_get_data(GTK_OBJECT(w), E_FILT_TE_PTR_KEY);
128
129     /* Now create a new dialog, without an "Add Expression..." button. */
130     filter_browse_w = filter_dialog_new(caller, parent_filter_te,
131                                         CFILTER_LIST, &args);
132
133     /* Set the E_FILT_CALLER_PTR_KEY for the new dialog to point to
134        our caller. */
135     gtk_object_set_data(GTK_OBJECT(filter_browse_w), E_FILT_CALLER_PTR_KEY,
136                         caller);
137
138     /* Set the E_FILT_DIALOG_PTR_KEY for the caller to point to us */
139     gtk_object_set_data(GTK_OBJECT(caller), E_FILT_DIALOG_PTR_KEY,
140                         filter_browse_w);
141 }
142 #endif
143
144 /* Create a filter dialog for constructing a display filter.
145
146    This is to be used as a callback for a button next to a text entry box,
147    which, when clicked, pops up this dialog to allow you to construct a
148    display filter by browsing the list of saved filters and/or by adding
149    test expressions constructed with another dialog.  The "OK" button
150    sets the text entry box to the constructed filter and activates that
151    text entry box, causing the filter to be used; this dialog is then
152    dismissed.
153
154    If "wants_apply_button" is non-null, we add an "Apply" button that
155    acts like "OK" but doesn't dismiss this dialog. */
156 void
157 display_filter_construct_cb(GtkWidget *w, gpointer construct_args_ptr)
158 {
159     construct_args_t *construct_args = construct_args_ptr;
160     GtkWidget *caller = gtk_widget_get_toplevel(w);
161     GtkWidget *filter_browse_w;
162     GtkWidget *parent_filter_te;
163
164     /* Has a filter dialog box already been opened for that top-level
165        widget? */
166     filter_browse_w = gtk_object_get_data(GTK_OBJECT(caller),
167                                           E_FILT_DIALOG_PTR_KEY);
168
169     if (filter_browse_w != NULL) {
170         /* Yes.  Just re-activate that dialog box. */
171         reactivate_window(filter_browse_w);
172         return;
173     }
174
175     /* No.  Get the text entry attached to the button. */
176     parent_filter_te = gtk_object_get_data(GTK_OBJECT(w), E_FILT_TE_PTR_KEY);
177
178     /* Now create a new dialog, possibly with an "Apply" button, and
179        definitely with an "Add Expression..." button. */
180     filter_browse_w = filter_dialog_new(caller, parent_filter_te,
181                                         DFILTER_LIST, construct_args);
182
183     /* Set the E_FILT_CALLER_PTR_KEY for the new dialog to point to
184        our caller. */
185     gtk_object_set_data(GTK_OBJECT(filter_browse_w), E_FILT_CALLER_PTR_KEY,
186                         caller);
187
188     /* Set the E_FILT_DIALOG_PTR_KEY for the caller to point to us */
189     gtk_object_set_data(GTK_OBJECT(caller), E_FILT_DIALOG_PTR_KEY,
190                         filter_browse_w);
191 }
192
193 #ifdef HAVE_LIBPCAP
194 static GtkWidget *global_cfilter_w;
195
196 /* Create a filter dialog for editing capture filters; this is to be used
197    as a callback for menu items, toolbars, etc.. */
198 void
199 cfilter_dialog_cb(GtkWidget *w _U_)
200 {
201     /* No Apply button, and there's no text widget to set, much less
202        activate, on "OK". */
203     static construct_args_t args = {
204         "Ethereal: Edit Capture Filter List",
205         FALSE,
206         FALSE
207     };
208
209     /* Has a filter dialog box already been opened for editing
210        capture filters? */
211     if (global_cfilter_w != NULL) {
212         /* Yes.  Just reactivate it. */
213         reactivate_window(global_cfilter_w);
214         return;
215     }
216
217     /*
218      * No.  Create one; we didn't pop this up as a result of pressing
219      * a button next to some text entry field, so don't associate it
220      * with a text entry field.
221      */
222     global_cfilter_w = filter_dialog_new(NULL, NULL, CFILTER_LIST, &args);
223 }
224 #endif
225
226 static GtkWidget *global_dfilter_w;
227
228 /* Create a filter dialog for editing display filters; this is to be used
229    as a callback for menu items, toolbars, etc.. */
230 void
231 dfilter_dialog_cb(GtkWidget *w _U_)
232 {
233     /* No Apply button, and there's no text widget to set, much less
234        activate, on "OK". */
235     static construct_args_t args = {
236         "Ethereal: Edit Display Filter List",
237         FALSE,
238         FALSE
239     };
240
241     /* Has a filter dialog box already been opened for editing
242        display filters? */
243     if (global_dfilter_w != NULL) {
244         /* Yes.  Just reactivate it. */
245         reactivate_window(global_dfilter_w);
246         return;
247     }
248
249     /*
250      * No.  Create one; we didn't pop this up as a result of pressing
251      * a button next to some text entry field, so don't associate it
252      * with a text entry field.
253      */
254     global_dfilter_w = filter_dialog_new(NULL, NULL, DFILTER_LIST, &args);
255 }
256
257 /* List of capture filter dialogs, so that if the list of filters changes
258   (the model, if you will), we can update all of their lists displaying
259    the filters (the views). */
260 static GList *cfilter_dialogs;
261
262 /* List of display filter dialogs, so that if the list of filters changes
263   (the model, if you will), we can update all of their lists displaying
264    the filters (the views). */
265 static GList *dfilter_dialogs;
266
267 static void
268 remember_filter_dialog(GtkWidget *main_w, GList **filter_dialogs)
269 {
270     *filter_dialogs = g_list_append(*filter_dialogs, main_w);
271 }
272
273 /* Remove a filter dialog from the specified list of filter_dialogs. */
274 static void
275 forget_filter_dialog(GtkWidget *main_w, filter_list_type_t list)
276 {
277     switch (list) {
278
279     case CFILTER_LIST:
280         cfilter_dialogs = g_list_remove(cfilter_dialogs, main_w);
281         break;
282
283     case DFILTER_LIST:
284         dfilter_dialogs = g_list_remove(dfilter_dialogs, main_w);
285         break;
286
287     default:
288         g_assert_not_reached();
289         break;
290     }
291 }
292
293 /* Get the dialog list corresponding to a particular filter list. */
294 static GList *
295 get_filter_dialog_list(filter_list_type_t list)
296 {
297     switch (list) {
298
299     case CFILTER_LIST:
300         return cfilter_dialogs;
301
302     case DFILTER_LIST:
303         return dfilter_dialogs;
304
305     default:
306         g_assert_not_reached();
307         return NULL;
308     }
309 }
310
311 static GtkWidget *
312 filter_dialog_new(GtkWidget *caller _U_, GtkWidget *parent_filter_te,
313                   filter_list_type_t list, construct_args_t *construct_args)
314 {
315     GtkWidget                *main_w,           /* main window */
316                              *main_vb,          /* main container */
317                              *bbox,             /* button container */
318                              *ok_bt,            /* "OK" button */
319                              *apply_bt,         /* "Apply" button */
320                              *save_bt,          /* "Save" button */
321                              *close_bt;         /* "Cancel" button */
322     GtkWidget                *filter_pg = NULL; /* filter settings box */
323     GtkWidget                *top_hb,
324                              *list_bb,
325                              *new_bt,
326                              *chg_bt,
327                              *copy_bt,
328                              *del_bt,
329                              *filter_sc,
330                              *filter_l,
331                              *middle_hb,
332                              *name_lb,
333                              *name_te,
334                              *bottom_hb,
335                              *filter_lb,
336                              *filter_te,
337                              *add_expression_bt;
338     gboolean                  l_select = FALSE;
339     GList                    *fl_entry;
340     filter_def               *filt;
341     const gchar              *filter_te_str = NULL;
342     GList                   **filter_dialogs;
343     static filter_list_type_t cfilter_list = CFILTER_LIST;
344     static filter_list_type_t dfilter_list = DFILTER_LIST;
345     filter_list_type_t       *filter_list_p;
346     GtkListStore             *store;
347     GtkCellRenderer          *renderer;
348     GtkTreeViewColumn        *column;
349     GtkTreeSelection         *sel;
350     GtkTreeIter               iter;
351
352     /* Get a pointer to a static variable holding the type of filter on
353        which we're working, so we can pass that pointer to callback
354        routines. */
355     switch (list) {
356
357     case CFILTER_LIST:
358         filter_dialogs = &cfilter_dialogs;
359         filter_list_p = &cfilter_list;
360         break;
361
362     case DFILTER_LIST:
363         filter_dialogs = &dfilter_dialogs;
364         filter_list_p = &dfilter_list;
365         break;
366
367     default:
368         g_assert_not_reached();
369         filter_dialogs = NULL;
370         filter_list_p = NULL;
371         break;
372     }
373
374     main_w = dlg_window_new(construct_args->title);
375     gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_CONSTRUCT_ARGS_KEY,
376                         construct_args);
377
378     /* Call a handler when we're destroyed, so we can inform
379        our caller, if any, that we've been destroyed. */
380     g_signal_connect(G_OBJECT(main_w), "destroy",
381                      G_CALLBACK(filter_dlg_destroy), filter_list_p);
382
383     main_vb = gtk_vbox_new(FALSE, 5);
384     gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
385     gtk_container_add(GTK_CONTAINER(main_w), main_vb);
386     gtk_widget_show(main_vb);
387
388     /* Make sure everything is set up */
389     if (parent_filter_te)
390         filter_te_str = gtk_entry_get_text(GTK_ENTRY(parent_filter_te));
391
392     /* Container for each row of widgets */
393     filter_pg = gtk_vbox_new(FALSE, 5);
394     gtk_container_border_width(GTK_CONTAINER(filter_pg), 5);
395     gtk_widget_show(filter_pg);
396
397     /* Top row: Filter list and buttons */
398     top_hb = gtk_hbox_new(FALSE, 5);
399     gtk_container_add(GTK_CONTAINER(filter_pg), top_hb);
400     gtk_widget_show(top_hb);
401
402     list_bb = gtk_vbutton_box_new();
403     gtk_button_box_set_layout (GTK_BUTTON_BOX (list_bb), GTK_BUTTONBOX_START);
404     gtk_container_add(GTK_CONTAINER(top_hb), list_bb);
405     gtk_widget_show(list_bb);
406
407     new_bt = gtk_button_new_from_stock(GTK_STOCK_NEW);
408     g_signal_connect(G_OBJECT(new_bt), "clicked",
409                      G_CALLBACK(filter_new_bt_clicked_cb), filter_list_p);
410     gtk_container_add(GTK_CONTAINER(list_bb), new_bt);
411     gtk_widget_show(new_bt);
412
413     chg_bt = gtk_button_new_with_label ("Change");
414     gtk_widget_set_sensitive(chg_bt, FALSE);
415     g_signal_connect(G_OBJECT(chg_bt), "clicked",
416                      G_CALLBACK(filter_chg_bt_clicked_cb), filter_list_p);
417     gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_CHG_BT_KEY, chg_bt);
418     g_signal_connect(G_OBJECT(chg_bt), "destroy",
419                      G_CALLBACK(filter_chg_bt_destroy_cb), NULL);
420     gtk_container_add(GTK_CONTAINER(list_bb), chg_bt);
421     gtk_widget_show(chg_bt);
422
423     copy_bt = gtk_button_new_from_stock(GTK_STOCK_COPY);
424     gtk_widget_set_sensitive(copy_bt, FALSE);
425     g_signal_connect(G_OBJECT(copy_bt), "clicked",
426                      G_CALLBACK(filter_copy_bt_clicked_cb), filter_list_p);
427     gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_COPY_BT_KEY, copy_bt);
428     g_signal_connect(G_OBJECT(copy_bt), "destroy",
429                      G_CALLBACK(filter_copy_bt_destroy_cb), NULL);
430     gtk_container_add(GTK_CONTAINER(list_bb), copy_bt);
431     gtk_widget_show(copy_bt);
432
433     del_bt = gtk_button_new_from_stock(GTK_STOCK_DELETE);
434     gtk_widget_set_sensitive(del_bt, FALSE);
435     g_signal_connect(G_OBJECT(del_bt), "clicked",
436                      G_CALLBACK(filter_del_bt_clicked_cb), filter_list_p);
437     gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_DEL_BT_KEY, del_bt);
438     g_signal_connect(G_OBJECT(del_bt), "destroy",
439                      G_CALLBACK(filter_del_bt_destroy_cb), NULL);
440     gtk_container_add(GTK_CONTAINER(list_bb), del_bt);
441     gtk_widget_show(del_bt);
442
443     if (list == DFILTER_LIST) {
444         /* Create the "Add Expression..." button, to pop up a dialog
445            for constructing filter comparison expressions. */
446         add_expression_bt = gtk_button_new_with_label("Add Expression...");
447         g_signal_connect(G_OBJECT(add_expression_bt), "clicked",
448                          G_CALLBACK(filter_expr_cb), main_w);
449         gtk_container_add(GTK_CONTAINER(list_bb), add_expression_bt);
450         gtk_widget_show(add_expression_bt);
451     }
452
453     filter_sc = gtk_scrolled_window_new(NULL, NULL);
454     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(filter_sc),
455                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
456     gtk_widget_set_size_request(filter_sc, 250, 150);
457     gtk_container_add(GTK_CONTAINER(top_hb), filter_sc);
458     gtk_widget_show(filter_sc);
459
460     store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
461     filter_l = tree_view_new(GTK_TREE_MODEL(store));
462     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(filter_l), FALSE);
463     renderer = gtk_cell_renderer_text_new();
464     column = gtk_tree_view_column_new_with_attributes("", renderer, "text",
465                                                       0, NULL);
466     gtk_tree_view_column_set_sort_column_id(column, 0);
467     gtk_tree_view_append_column(GTK_TREE_VIEW(filter_l), column);
468     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
469     gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
470     g_signal_connect(G_OBJECT(sel), "changed",
471                      G_CALLBACK(filter_sel_list_cb), filter_pg);
472     gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_FILTER_L_KEY, filter_l);
473     g_signal_connect(G_OBJECT(filter_l), "destroy",
474                      G_CALLBACK(filter_list_destroy_cb), NULL);
475     g_signal_connect(G_OBJECT(filter_l), "button_press_event",
476                      G_CALLBACK(filter_sel_list_button_cb), NULL);
477     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(filter_sc),
478                                           filter_l);
479     gtk_widget_show(filter_l);
480
481     gtk_object_set_data(GTK_OBJECT(filter_l), E_FILT_DBLFUNC_KEY,
482                         filter_dlg_dclick);
483     gtk_object_set_data(GTK_OBJECT(filter_l), E_FILT_DBLARG_KEY, main_w);
484     /* This is a Boolean, but we make it a non-null pointer for TRUE
485        and a null pointer for FALSE, as object data is a pointer. */
486     gtk_object_set_data(GTK_OBJECT(filter_l), E_FILT_DBLACTIVATE_KEY,
487                         construct_args->activate_on_ok? "" : NULL);
488
489     fl_entry = get_filter_list_first(list);
490     while (fl_entry != NULL) {
491         filt    = (filter_def *) fl_entry->data;
492         gtk_list_store_append(store, &iter);
493         gtk_list_store_set(store, &iter, 0, filt->name,
494                            1, fl_entry, -1);
495         if (filter_te_str && filt->strval) {
496             if (strcmp(filter_te_str, filt->strval) == 0)
497             {
498                 gtk_tree_selection_select_iter(sel, &iter);
499                 l_select = TRUE;
500             }
501         }
502
503         fl_entry = fl_entry->next;
504     }
505     g_object_unref(G_OBJECT(store));
506
507     /* Middle row: Filter name entry */
508     middle_hb = gtk_hbox_new(FALSE, 5);
509     gtk_container_add(GTK_CONTAINER(filter_pg), middle_hb);
510     gtk_widget_show(middle_hb);
511
512     name_lb = gtk_label_new("Filter name:");
513     gtk_box_pack_start(GTK_BOX(middle_hb), name_lb, FALSE, FALSE, 3);
514     gtk_widget_show(name_lb);
515
516     name_te = gtk_entry_new();
517     gtk_box_pack_start(GTK_BOX(middle_hb), name_te, TRUE, TRUE, 3);
518     gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_NAME_TE_KEY, name_te);
519     g_signal_connect(G_OBJECT(name_te), "destroy",
520                      G_CALLBACK(filter_name_te_destroy_cb), NULL);
521     gtk_widget_show(name_te);
522
523     /* Bottom row: Filter text entry */
524     bottom_hb = gtk_hbox_new(FALSE, 5);
525     gtk_container_add(GTK_CONTAINER(filter_pg), bottom_hb);
526     gtk_widget_show(bottom_hb);
527
528     filter_lb = gtk_label_new("Filter string:");
529     gtk_box_pack_start(GTK_BOX(bottom_hb), filter_lb, FALSE, FALSE, 3);
530     gtk_widget_show(filter_lb);
531
532     filter_te = gtk_entry_new();
533     gtk_box_pack_start(GTK_BOX(bottom_hb), filter_te, TRUE, TRUE, 3);
534     gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_FILTER_TE_KEY, filter_te);
535
536     g_signal_connect(G_OBJECT(filter_te), "destroy",
537                      G_CALLBACK(filter_filter_te_destroy_cb), NULL);
538     gtk_widget_show(filter_te);
539
540     if (!l_select && filter_te_str && filter_te_str[0]) {
541         gtk_entry_set_text(GTK_ENTRY(name_te), "New filter");
542         gtk_entry_set_text(GTK_ENTRY(filter_te), filter_te_str);
543     }
544
545     gtk_box_pack_start(GTK_BOX(main_vb), filter_pg, TRUE, TRUE, 0);
546     gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY,
547                         parent_filter_te);
548
549     bbox = gtk_hbutton_box_new();
550     gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
551     gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
552     gtk_container_add(GTK_CONTAINER(main_vb), bbox);
553     gtk_widget_show(bbox);
554
555     if (parent_filter_te != NULL) {
556         /*
557          * We have a filter text entry that we can fill in if
558          * the "OK" button is clicked, so put in an "OK" button.
559          */
560         ok_bt = gtk_button_new_from_stock(GTK_STOCK_OK);
561         g_signal_connect(G_OBJECT(ok_bt), "clicked",
562                          G_CALLBACK(filter_dlg_ok_cb), NULL);
563         GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
564         gtk_box_pack_start(GTK_BOX(bbox), ok_bt, TRUE, TRUE, 0);
565         gtk_widget_grab_default(ok_bt);
566         gtk_widget_show(ok_bt);
567
568         /* Catch the "activate" signal on the filter name and filter
569            expression text entries, so that if the user types Return
570            there, we act as if the "OK" button had been selected, as
571            happens if Return is typed if some widget that *doesn't*
572            handle the Return key has the input focus. */
573         dlg_set_activate(name_te, ok_bt);
574         dlg_set_activate(filter_te, ok_bt);
575     }
576
577     if (construct_args->wants_apply_button) {
578         apply_bt = gtk_button_new_from_stock(GTK_STOCK_APPLY);
579         g_signal_connect(G_OBJECT(apply_bt), "clicked",
580                          G_CALLBACK(filter_dlg_apply_cb), NULL);
581         GTK_WIDGET_SET_FLAGS(apply_bt, GTK_CAN_DEFAULT);
582         gtk_box_pack_start(GTK_BOX(bbox), apply_bt, TRUE, TRUE, 0);
583         gtk_widget_show(apply_bt);
584     }
585
586     save_bt = gtk_button_new_from_stock(GTK_STOCK_SAVE);
587     g_signal_connect(G_OBJECT(save_bt), "clicked",
588                      G_CALLBACK(filter_dlg_save_cb), filter_list_p);
589     GTK_WIDGET_SET_FLAGS(save_bt, GTK_CAN_DEFAULT);
590     gtk_box_pack_start(GTK_BOX(bbox), save_bt, TRUE, TRUE, 0);
591     gtk_widget_show(save_bt);
592
593     close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
594     g_signal_connect(G_OBJECT(close_bt), "clicked",
595                      G_CALLBACK(filter_dlg_close_cb), GTK_OBJECT(main_w));
596     GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
597     gtk_box_pack_start(GTK_BOX(bbox), close_bt, TRUE, TRUE, 0);
598     gtk_widget_show(close_bt);
599
600     /*
601      * Catch the "key_press_event" signal in the window, so that we can
602      * catch the ESC key being pressed and act as if the "Close" button
603      * had been selected.
604      */
605     dlg_set_cancel(main_w, close_bt);
606
607     remember_filter_dialog(main_w, filter_dialogs);
608
609     gtk_widget_show(main_w);
610
611     return main_w;
612 }
613
614 static void
615 filter_dlg_dclick(GtkWidget *filter_l, gpointer main_w_arg, gpointer activate)
616 {
617     GtkWidget        *main_w = GTK_WIDGET(main_w_arg);
618     GtkWidget        *parent_filter_te =
619         gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY);
620     GList            *flp;
621     filter_def       *filt;
622     GtkTreeSelection *sel;
623     GtkTreeModel     *model;
624     GtkTreeIter       iter;
625
626     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
627
628     if (parent_filter_te != NULL) {
629         /*
630          * We have a text entry widget associated with this dialog
631          * box; is one of the filters in the list selected?
632          */
633
634         /* if something was selected */
635         if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
636             /*
637              * Yes.  Is there a filter definition for that filter?
638              */
639             gtk_tree_model_get(model, &iter, 1, &flp, -1);
640             if (flp) {
641                 /*
642                  * Yes - put it in the text entry widget.
643                  */
644                 filt = (filter_def *) flp->data;
645                 gtk_entry_set_text(GTK_ENTRY(parent_filter_te),
646                                    filt->strval);
647
648                 /*
649                  * Are we supposed to cause the filter we
650                  * put there to be applied?
651                  */
652                 if (activate != NULL) {
653                     /*
654                      * Yes - do so.
655                      */
656                     g_signal_emit_by_name(G_OBJECT(parent_filter_te),
657                                           "activate");
658                 }
659             }
660         }
661     }
662
663     gtk_widget_destroy(main_w);
664 }
665
666 static void
667 filter_dlg_ok_cb(GtkWidget *ok_bt, gpointer data _U_)
668 {
669     /*
670      * Destroy the dialog box and apply the filter.
671      */
672     filter_apply(gtk_widget_get_toplevel(ok_bt), TRUE);
673 }
674
675 static void
676 filter_dlg_apply_cb(GtkWidget *apply_bt, gpointer dummy _U_)
677 {
678     /*
679      * Apply the filter, but don't destroy the dialog box.
680      */
681     filter_apply(gtk_widget_get_toplevel(apply_bt), FALSE);
682 }
683
684 static void
685 filter_apply(GtkWidget *main_w, gboolean destroy)
686 {
687     construct_args_t *construct_args =
688         gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_CONSTRUCT_ARGS_KEY);
689     GtkWidget  *parent_filter_te =
690         gtk_object_get_data(GTK_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY);
691     GtkWidget   *filter_te;
692     const gchar *filter_string;
693
694     if (parent_filter_te != NULL) {
695         /*
696          * We have a text entry widget associated with this dialog
697          * box; put the filter in our text entry widget into that
698          * text entry widget.
699          */
700         filter_te = gtk_object_get_data(GTK_OBJECT(main_w),
701                                         E_FILT_FILTER_TE_KEY);
702         filter_string = gtk_entry_get_text(GTK_ENTRY(filter_te));
703         gtk_entry_set_text(GTK_ENTRY(parent_filter_te), filter_string);
704
705     }
706
707     if (destroy) {
708         /*
709          * Destroy the filter dialog box.
710          */
711         gtk_widget_destroy(main_w);
712     }
713
714     if (parent_filter_te != NULL) {
715         /*
716          * We have a text entry widget associated with this dialog
717          * box; activate that widget to cause the filter we put
718          * there to be applied if we're supposed to do so.
719          *
720          * We do this after dismissing the filter dialog box,
721          * as activating the widget the dialog box to which
722          * it belongs to be dismissed, and that may cause it
723          * to destroy our dialog box if the filter succeeds.
724          * This means that our subsequent attempt to destroy
725          * it will fail.
726          *
727          * We don't know whether it'll destroy our dialog box,
728          * so we can't rely on it to do so.  Instead, we
729          * destroy it ourselves, which will clear the
730          * E_FILT_DIALOG_PTR_KEY pointer for their dialog box,
731          * meaning they won't think it has one and won't try
732          * to destroy it.
733          */
734         if (construct_args->activate_on_ok) {
735             g_signal_emit_by_name(G_OBJECT(parent_filter_te), "activate");
736         }
737     }
738 }
739
740 static void
741 filter_dlg_save_cb(GtkWidget *save_bt _U_, gpointer data)
742 {
743     filter_list_type_t list = *(filter_list_type_t *)data;
744     char *pf_dir_path;
745     char *f_path;
746     int f_save_errno;
747     char *filter_type;
748
749     /* Create the directory that holds personal configuration files,
750        if necessary.  */
751     if (create_persconffile_dir(&pf_dir_path) == -1) {
752         simple_dialog(ESD_TYPE_WARN, NULL,
753                       "Can't create directory\n\"%s\"\nfor filter files: %s.",
754                       pf_dir_path, strerror(errno));
755         g_free(pf_dir_path);
756         return;
757     }
758
759     save_filter_list(list, &f_path, &f_save_errno);
760     if (f_path != NULL) {
761         /* We had an error saving the filter. */
762         switch (list) {
763
764         case CFILTER_LIST:
765             filter_type = "capture";
766             break;
767
768         case DFILTER_LIST:
769             filter_type = "display";
770             break;
771
772         default:
773             g_assert_not_reached();
774             filter_type = NULL;
775             break;
776         }
777         simple_dialog(ESD_TYPE_CRIT, NULL,
778                       "Could not save to your %s filter file\n\"%s\": %s.",
779                       filter_type, f_path, strerror(f_save_errno));
780         g_free(f_path);
781     }
782 }
783
784 static void
785 filter_dlg_close_cb(GtkWidget *close_bt _U_, gpointer parent_w)
786 {
787     gtk_widget_destroy(GTK_WIDGET(parent_w));
788 }
789
790 static void
791 filter_dlg_destroy(GtkWidget *win, gpointer data)
792 {
793     filter_list_type_t list = *(filter_list_type_t *)data;
794     GtkWidget *caller;
795
796     /* Get the widget that requested that we be popped up, if any.
797        (It should arrange to destroy us if it's destroyed, so
798        that we don't get a pointer to a non-existent window here.) */
799     caller = gtk_object_get_data(GTK_OBJECT(win), E_FILT_CALLER_PTR_KEY);
800
801     if (caller != NULL) {
802         /* Tell it we no longer exist. */
803         gtk_object_set_data(GTK_OBJECT(caller), E_FILT_DIALOG_PTR_KEY,
804                             NULL);
805     } else {
806         /* This is an editing dialog popped up from, for example,
807            a menu item; note that we no longer have one. */
808         switch (list) {
809
810 #ifdef HAVE_LIBPCAP
811         case CFILTER_LIST:
812             g_assert(win == global_cfilter_w);
813             global_cfilter_w = NULL;
814             break;
815 #endif
816
817         case DFILTER_LIST:
818             g_assert(win == global_dfilter_w);
819             global_dfilter_w = NULL;
820             break;
821
822         default:
823             g_assert_not_reached();
824             break;
825         }
826     }
827
828     /* Remove this from the list of filter dialog windows. */
829     forget_filter_dialog(win, list);
830
831     /* Now nuke this window. */
832     gtk_grab_remove(GTK_WIDGET(win));
833     gtk_widget_destroy(GTK_WIDGET(win));
834 }
835
836 static gint
837 filter_sel_list_button_cb(GtkWidget *list, GdkEventButton *event,
838                           gpointer data _U_)
839 {
840     void (* func)(GtkWidget *, gpointer, gpointer);
841     gpointer func_arg;
842     gpointer func_activate;
843
844     if (event->type == GDK_2BUTTON_PRESS) {
845         func = gtk_object_get_data(GTK_OBJECT(list), E_FILT_DBLFUNC_KEY);
846         func_arg = gtk_object_get_data(GTK_OBJECT(list), E_FILT_DBLARG_KEY);
847         func_activate = gtk_object_get_data(GTK_OBJECT(list),
848                                             E_FILT_DBLACTIVATE_KEY);
849
850         if (func)
851             (*func)(list, func_arg, func_activate);
852     }
853
854     return FALSE;
855 }
856
857 static void
858 filter_sel_list_cb(GtkTreeSelection *sel, gpointer data _U_)
859 {
860     GtkWidget    *filter_l = GTK_WIDGET(gtk_tree_selection_get_tree_view(sel));
861     GtkWidget    *main_w = gtk_widget_get_toplevel(filter_l);
862     GtkWidget    *name_te = gtk_object_get_data(GTK_OBJECT(main_w),
863                                                 E_FILT_NAME_TE_KEY);
864     GtkWidget    *filter_te = gtk_object_get_data(GTK_OBJECT(main_w),
865                                                   E_FILT_FILTER_TE_KEY);
866     GtkWidget    *chg_bt = gtk_object_get_data(GTK_OBJECT(main_w),
867                                                E_FILT_CHG_BT_KEY);
868     GtkWidget    *copy_bt = gtk_object_get_data(GTK_OBJECT(main_w),
869                                                 E_FILT_COPY_BT_KEY);
870     GtkWidget    *del_bt = gtk_object_get_data(GTK_OBJECT(main_w),
871                                                E_FILT_DEL_BT_KEY);
872     filter_def   *filt;
873     gchar        *name = "", *strval = "";
874     GList        *flp;
875     gint          sensitivity = FALSE;
876     GtkTreeModel *model;
877     GtkTreeIter   iter;
878
879     /* if something was selected */
880     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
881         gtk_tree_model_get(model, &iter, 1, &flp, -1);
882         if (flp) {
883             filt   = (filter_def *) flp->data;
884             name   = filt->name;
885             strval = filt->strval;
886             sensitivity = TRUE;
887         }
888     }
889
890     /*
891      * Did you know that this function is called when the window is destroyed?
892      * Funny, that.
893      * This means that we have to:
894      *
895      *  attach to the top-level window data items containing pointers to
896      *  the widgets we affect here;
897      *
898      *  give each of those widgets their own destroy callbacks;
899      *
900      *  clear that pointer when the widget is destroyed;
901      *
902      *  don't do anything to the widget if the pointer we get back is
903      *  null;
904      *
905      * so that if we're called after any of the widgets we'd affect are
906      * destroyed, we know that we shouldn't do anything to those widgets.
907      */
908     if (name_te != NULL)
909         gtk_entry_set_text(GTK_ENTRY(name_te), name);
910     if (filter_te != NULL)
911         gtk_entry_set_text(GTK_ENTRY(filter_te), strval);
912     if (chg_bt != NULL)
913         gtk_widget_set_sensitive(chg_bt, sensitivity);
914     if (copy_bt != NULL)
915         gtk_widget_set_sensitive(copy_bt, sensitivity);
916     if (del_bt != NULL)
917         gtk_widget_set_sensitive(del_bt, sensitivity);
918 }
919
920 static void
921 filter_list_destroy_cb(GtkWidget *l, gpointer data _U_)
922 {
923     GtkWidget  *main_w = gtk_widget_get_toplevel(l);
924
925     gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_FILTER_L_KEY, NULL);
926 }
927
928 /* To do: add input checking to each of these callbacks */
929
930 /* Structure containing arguments to be passed to "new_filter_cb()".
931
932    "active_filter_l" is the list in the dialog box in which "New" or
933    "Copy" was clicked; in that dialog box, but not in any other dialog
934    box, we select the newly created list item.
935
936    "nflp" is the GList member in the model (filter list) for the new
937    filter. */
938 typedef struct {
939     GtkWidget *active_filter_l;
940     GList     *nflp;
941 } new_filter_cb_args_t;
942
943 static void
944 new_filter_cb(gpointer data, gpointer user_data)
945 {
946     GtkWidget            *main_w = data;
947     GtkTreeView          *filter_l;
948     new_filter_cb_args_t *args = user_data;
949     filter_def           *nfilt = args->nflp->data;
950     GtkListStore         *store;
951     GtkTreeIter           iter;
952
953     filter_l = GTK_TREE_VIEW(gtk_object_get_data(GTK_OBJECT(main_w),
954                                                  E_FILT_FILTER_L_KEY));
955     store = GTK_LIST_STORE(gtk_tree_view_get_model(filter_l));
956     gtk_list_store_append(store, &iter);
957     gtk_list_store_set(store, &iter, 0, nfilt->name, 1, args->nflp, -1);
958     if (GTK_WIDGET(filter_l) == args->active_filter_l) {
959         /* Select the item. */
960         gtk_tree_selection_select_iter(gtk_tree_view_get_selection(filter_l),
961                                        &iter);
962     }
963 }
964
965 static void
966 filter_new_bt_clicked_cb(GtkWidget *w, gpointer data)
967 {
968     GtkWidget  *main_w = gtk_widget_get_toplevel(w);
969     GtkWidget  *name_te = gtk_object_get_data(GTK_OBJECT(main_w),
970                                               E_FILT_NAME_TE_KEY);
971     GtkWidget  *filter_te = gtk_object_get_data(GTK_OBJECT(main_w),
972                                                 E_FILT_FILTER_TE_KEY);
973     GtkWidget  *filter_l = gtk_object_get_data(GTK_OBJECT(main_w),
974                                                E_FILT_FILTER_L_KEY);
975     filter_list_type_t list = *(filter_list_type_t *)data;
976     GList      *fl_entry;
977     const gchar *name, *strval;
978     new_filter_cb_args_t args;
979
980     name   = gtk_entry_get_text(GTK_ENTRY(name_te));
981     strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
982
983     if (strlen(name) > 0 && strlen(strval) > 0) {
984         /* Add a new entry to the filter list. */
985         fl_entry = add_to_filter_list(list, (char *)name, (char *)strval);
986
987         /* Update all the filter list widgets, not just the one in
988            the dialog box in which we clicked on "Copy". */
989         args.active_filter_l = filter_l;
990         args.nflp = fl_entry;
991         g_list_foreach(get_filter_dialog_list(list), new_filter_cb, &args);
992     } else {
993         /* Give the user some basic directions on how to use the 'new' button */
994         simple_dialog(ESD_TYPE_WARN, NULL,
995                       "You have left either the 'Filter name' or 'Filter string' field empty.\n"
996                       "To add a new filter, enter a name for the filter, and enter the expression\n"
997                       "for the filter or use the 'Add Expression...' button to construct an\n"
998                       "expression, then click 'New'.");
999     }
1000 }
1001
1002 static gboolean
1003 chg_list_item_cb(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
1004                  gpointer data)
1005 {
1006     GList      *flp = data;
1007     filter_def *filt = flp->data;
1008     GList      *nl_model;
1009
1010     gtk_tree_model_get(model, iter, 1, &nl_model, -1);
1011     /* Is this the item corresponding to the filter list item in question? */
1012     if (flp == nl_model) {
1013         /* Yes - change the label to correspond to the new name for the
1014          * filter. */
1015         gtk_list_store_set(GTK_LIST_STORE(model), iter, 0, filt->name, -1);
1016         return TRUE;
1017     }
1018     return FALSE;
1019 }
1020
1021 static void
1022 chg_filter_cb(gpointer data, gpointer user_data)
1023 {
1024     GtkWidget  *main_w = data;
1025     GtkWidget  *filter_l = gtk_object_get_data(GTK_OBJECT(main_w),
1026                                                E_FILT_FILTER_L_KEY);
1027
1028     gtk_tree_model_foreach(gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l)),
1029                            chg_list_item_cb, user_data);
1030 }
1031
1032 static void
1033 filter_chg_bt_clicked_cb(GtkWidget *w, gpointer data)
1034 {
1035     GtkWidget         *main_w = gtk_widget_get_toplevel(w);
1036     GtkWidget         *name_te = gtk_object_get_data(GTK_OBJECT(main_w),
1037                                                      E_FILT_NAME_TE_KEY);
1038     GtkWidget         *filter_te = gtk_object_get_data(GTK_OBJECT(main_w),
1039                                                        E_FILT_FILTER_TE_KEY);
1040     GtkWidget         *filter_l = gtk_object_get_data(GTK_OBJECT(main_w),
1041                                                       E_FILT_FILTER_L_KEY);
1042     filter_def        *filt;
1043     const gchar       *name, *strval;
1044     GList             *fl_entry;
1045     filter_list_type_t list = *(filter_list_type_t *)data;
1046     GtkTreeSelection  *sel;
1047     GtkTreeModel      *model;
1048     GtkTreeIter        iter;
1049
1050     name   = gtk_entry_get_text(GTK_ENTRY(name_te));
1051     strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
1052
1053     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
1054
1055     /* if something was selected */
1056     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1057         gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
1058         if (fl_entry != NULL) {
1059             filt = (filter_def *) fl_entry->data;
1060
1061             if (strlen(name) > 0 && strlen(strval) > 0 && filt) {
1062                 g_free(filt->name);
1063                 g_free(filt->strval);
1064                 filt->name   = g_strdup(name);
1065                 filt->strval = g_strdup(strval);
1066
1067                 /* Update all the filter list widgets, not just the one in
1068                    the dialog box in which we clicked on "Copy". */
1069                 g_list_foreach(get_filter_dialog_list(list), chg_filter_cb,
1070                                fl_entry);
1071             }
1072         }
1073     }
1074 }
1075
1076 static void
1077 filter_chg_bt_destroy_cb(GtkWidget *chg_bt, gpointer data _U_)
1078 {
1079     GtkWidget  *main_w = gtk_widget_get_toplevel(chg_bt);
1080
1081     gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_CHG_BT_KEY, NULL);
1082 }
1083
1084 static void
1085 filter_copy_bt_clicked_cb(GtkWidget *w, gpointer data)
1086 {
1087     GtkWidget           *main_w = gtk_widget_get_toplevel(w);
1088     GtkWidget           *filter_l = gtk_object_get_data(GTK_OBJECT(main_w),
1089                                                         E_FILT_FILTER_L_KEY);
1090     GList               *fl_entry, *nfl_entry;
1091     gchar               *prefix = "Copy of ", *name;
1092     filter_def          *filt;
1093     filter_list_type_t   list = *(filter_list_type_t *)data;
1094     new_filter_cb_args_t args;
1095     GtkTreeSelection    *sel;
1096     GtkTreeModel        *model;
1097     GtkTreeIter          iter;
1098
1099     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
1100
1101     /* if something was selected */
1102     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1103         gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
1104         if (fl_entry != NULL) {
1105             /* Add a new entry, copying the existing entry, to the
1106              * filter list. */
1107             filt = (filter_def *) fl_entry->data;
1108             name = g_malloc(strlen(prefix) + strlen(filt->name) + 1);
1109             sprintf(name, "%s%s", prefix, filt->name);
1110             nfl_entry = add_to_filter_list(list, name, filt->strval);
1111             g_free(name);
1112
1113             /* Update all the filter list widgets, not just the one in
1114                the dialog box in which we clicked on "Copy". */
1115             args.active_filter_l = filter_l;
1116             args.nflp = nfl_entry;
1117             g_list_foreach(get_filter_dialog_list(list), new_filter_cb, &args);
1118         }
1119     }
1120 }
1121
1122 static void
1123 filter_copy_bt_destroy_cb(GtkWidget *copy_bt, gpointer data _U_)
1124 {
1125     GtkWidget  *main_w = gtk_widget_get_toplevel(copy_bt);
1126
1127     gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_COPY_BT_KEY, NULL);
1128 }
1129
1130 static void
1131 delete_filter_cb(gpointer data, gpointer user_data)
1132 {
1133     GtkWidget    *main_w = data;
1134     GtkWidget    *filter_l = gtk_object_get_data(GTK_OBJECT(main_w),
1135                                                  E_FILT_FILTER_L_KEY);
1136     gchar        *pos = (gchar *)user_data;
1137     GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l));
1138     GtkTreeIter   iter;
1139
1140     gtk_tree_model_get_iter_from_string(model, &iter, pos);
1141     gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
1142 }
1143
1144 static void
1145 filter_del_bt_clicked_cb(GtkWidget *w, gpointer data)
1146 {
1147     GtkWidget         *main_w = gtk_widget_get_toplevel(w);
1148     GtkWidget         *filter_l = gtk_object_get_data(GTK_OBJECT(main_w),
1149                                                       E_FILT_FILTER_L_KEY);
1150     filter_list_type_t list = *(filter_list_type_t *)data;
1151     GList             *fl_entry;
1152     gchar             *pos;
1153     GtkTreeSelection  *sel;
1154     GtkTreeModel      *model;
1155     GtkTreeIter        iter;
1156     GtkTreePath       *path;
1157
1158     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
1159     /* If something was selected */
1160     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1161         gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
1162         path = gtk_tree_model_get_path(model, &iter);
1163         pos = gtk_tree_path_to_string(path);
1164         gtk_tree_path_free(path);
1165         if (fl_entry != NULL) {
1166             /* Remove the entry from the filter list. */
1167             remove_from_filter_list(list, fl_entry);
1168
1169             /* Update all the filter list widgets, not just the one in
1170                the dialog box in which we clicked on "Delete". */
1171             g_list_foreach(get_filter_dialog_list(list), delete_filter_cb, pos);
1172         }
1173         g_free(pos);
1174     }
1175 }
1176
1177 static void
1178 filter_del_bt_destroy_cb(GtkWidget *del_bt, gpointer data _U_)
1179 {
1180     GtkWidget  *main_w = gtk_widget_get_toplevel(del_bt);
1181
1182     gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_DEL_BT_KEY, NULL);
1183 }
1184
1185 static void
1186 filter_expr_cb(GtkWidget *w _U_, gpointer main_w_arg)
1187 {
1188     GtkWidget  *main_w = GTK_WIDGET(main_w_arg);
1189     GtkWidget  *filter_te;
1190
1191     filter_te = gtk_object_get_data(GTK_OBJECT(main_w),
1192                                     E_FILT_FILTER_TE_KEY);
1193     dfilter_expr_dlg_new(filter_te);
1194 }
1195
1196 static void
1197 filter_name_te_destroy_cb(GtkWidget *name_te, gpointer data _U_)
1198 {
1199     GtkWidget  *main_w = gtk_widget_get_toplevel(name_te);
1200
1201     gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_NAME_TE_KEY, NULL);
1202 }
1203
1204 static void
1205 filter_filter_te_destroy_cb(GtkWidget *filter_te, gpointer data _U_)
1206 {
1207     GtkWidget  *main_w = gtk_widget_get_toplevel(filter_te);
1208
1209     gtk_object_set_data(GTK_OBJECT(main_w), E_FILT_FILTER_TE_KEY, NULL);
1210 }