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