Change the filter dialog (capture and display filters), so it has a real Cancel butto...
[obnox/wireshark/wip.git] / gtk / filter_dlg.c
1 /* filter_dlg.c
2  * Dialog boxes for (display and capture) filter editing
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <string.h>
30
31 #include <gtk/gtk.h>
32
33 #include <epan/filesystem.h>
34
35 #include "filters.h"
36 #include "gtk/main.h"
37 #include "filter_dlg.h"
38 #include "dlg_utils.h"
39 #include "gui_utils.h"
40 #include "simple_dialog.h"
41 #include "dfilter_expr_dlg.h"
42 #include "compat_macros.h"
43 #include "gtkglobals.h"
44 #include "help_dlg.h"
45
46 #define E_FILT_DIALOG_PTR_KEY       "filter_dialog_ptr"
47 #define E_FILT_BUTTON_PTR_KEY       "filter_button_ptr"
48 #define E_FILT_PARENT_FILTER_TE_KEY "filter_parent_filter_te"
49 #define E_FILT_CONSTRUCT_ARGS_KEY   "filter_construct_args"
50 #define E_FILT_LIST_ITEM_MODEL_KEY  "filter_list_item_model"
51 #define E_FILT_LBL_KEY              "filter_label"
52 #define E_FILT_FILTER_L_KEY         "filter_filter_l"
53 #define E_FILT_CHG_BT_KEY           "filter_chg_bt"
54 #define E_FILT_COPY_BT_KEY          "filter_copy_bt"
55 #define E_FILT_DEL_BT_KEY           "filter_del_bt"
56 #define E_FILT_NAME_TE_KEY          "filter_name_te"
57 #define E_FILT_DBLFUNC_KEY          "filter_dblfunc"
58 #define E_FILT_DBLARG_KEY           "filter_dblarg"
59 #define E_FILT_DBLACTIVATE_KEY      "filter_dblactivate"
60
61 typedef struct _filter_cb_data {
62   GList     *fl;
63   GtkWidget *win;
64 } filter_cb_data;
65
66 static GtkWidget *filter_dialog_new(GtkWidget *button, GtkWidget *filter_te,
67                                     filter_list_type_t list_type,
68                                     construct_args_t *construct_args);
69 static void filter_dlg_dclick(GtkWidget *dummy, gpointer main_w_arg,
70                               gpointer activate);
71 static void filter_dlg_ok_cb(GtkWidget *ok_bt, gpointer dummy);
72 static void filter_dlg_apply_cb(GtkWidget *apply_bt, gpointer dummy);
73 static void filter_apply(GtkWidget *main_w, gboolean destroy);
74 static void filter_dlg_save_cb(GtkWidget *save_bt, gpointer parent_w);
75 static void filter_dlg_destroy_cb(GtkWidget *win, gpointer data);
76
77 static gboolean
78 filter_dlg_delete_event_cb(GtkWidget *prefs_w, GdkEvent *event, gpointer data);
79 static void
80 filter_dlg_cancel_cb(GtkWidget *cancel_bt, gpointer data);
81
82 static gint filter_sel_list_button_cb(GtkWidget *, GdkEventButton *,
83                                       gpointer);
84 #if GTK_MAJOR_VERSION < 2
85 static void filter_sel_list_cb(GtkWidget *, gpointer);
86 #else
87 static void filter_sel_list_cb(GtkTreeSelection *, gpointer);
88 #endif
89 static void filter_new_bt_clicked_cb(GtkWidget *, gpointer);
90 static void filter_del_bt_clicked_cb(GtkWidget *, gpointer);
91 static void filter_name_te_changed_cb(GtkWidget *, gpointer);
92
93 #ifdef HAVE_LIBPCAP
94 /* Create a filter dialog for constructing a capture filter.
95
96    This is to be used as a callback for a button next to a text entry box,
97    which, when clicked, pops up this dialog to allow you to construct a
98    display filter by browsing the list of saved filters (the dialog
99    for constructing expressions assumes display filter syntax, not
100    capture filter syntax).  The "OK" button sets the text entry box to the
101    constructed filter and activates that text entry box (which should have
102    no effect in the main capture dialog); this dialog is then dismissed. */
103 void
104 capture_filter_construct_cb(GtkWidget *w, gpointer user_data _U_)
105 {
106         GtkWidget *filter_browse_w;
107         GtkWidget *parent_filter_te;
108         /* No Apply button, and "OK" just sets our text widget, it doesn't
109            activate it (i.e., it doesn't cause us to try to open the file). */
110         static construct_args_t args = {
111                 "Wireshark: Capture Filter",
112                 FALSE,
113                 FALSE,
114         FALSE
115         };
116
117         /* Has a filter dialog box already been opened for that button? */
118         filter_browse_w = OBJECT_GET_DATA(w, 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 = OBJECT_GET_DATA(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(w, parent_filter_te,
131             CFILTER_LIST, &args);
132 }
133 #endif
134
135 /* Create a filter dialog for constructing a display filter.
136
137    This is to be used as a callback for a button next to a text entry box,
138    which, when clicked, pops up this dialog to allow you to construct a
139    display filter by browsing the list of saved filters and/or by adding
140    test expressions constructed with another dialog.  The "OK" button
141    sets the text entry box to the constructed filter and activates that
142    text entry box, causing the filter to be used; this dialog is then
143    dismissed.
144
145    If "wants_apply_button" is non-null, we add an "Apply" button that
146    acts like "OK" but doesn't dismiss this dialog. */
147 void
148 display_filter_construct_cb(GtkWidget *w, gpointer construct_args_ptr)
149 {
150         construct_args_t *construct_args = construct_args_ptr;
151         GtkWidget *filter_browse_w;
152         GtkWidget *parent_filter_te;
153
154         /* Has a filter dialog box already been opened for the button? */
155         filter_browse_w = OBJECT_GET_DATA(w, E_FILT_DIALOG_PTR_KEY);
156
157         if (filter_browse_w != NULL) {
158                 /* Yes.  Just re-activate that dialog box. */
159                 reactivate_window(filter_browse_w);
160                 return;
161         }
162
163         /* No.  Get the text entry attached to the button. */
164         parent_filter_te = OBJECT_GET_DATA(w, E_FILT_TE_PTR_KEY);
165
166         /* Now create a new dialog, possibly with an "Apply" button, and
167            definitely with an "Add Expression..." button. */
168         filter_browse_w = filter_dialog_new(w, parent_filter_te,
169             DFILTER_LIST, construct_args);
170 }
171
172 /* Should be called when a button that creates filters is destroyed; it
173    destroys any filter dialog created by that button. */
174 void
175 filter_button_destroy_cb(GtkWidget *button, gpointer user_data _U_)
176 {
177         GtkWidget *filter_w;
178
179         /* Is there a filter edit/selection dialog associated with this
180            button? */
181         filter_w = OBJECT_GET_DATA(button, E_FILT_DIALOG_PTR_KEY);
182
183         if (filter_w != NULL) {
184                 /* Yes.  Break the association, and destroy the dialog. */
185                 OBJECT_SET_DATA(button, E_FILT_DIALOG_PTR_KEY, NULL);
186                 window_destroy(filter_w);
187         }
188 }
189
190 #ifdef HAVE_LIBPCAP
191 static GtkWidget *global_cfilter_w;
192
193 /* Create a filter dialog for editing capture filters; this is to be used
194    as a callback for menu items, toolbars, etc.. */
195 void
196 cfilter_dialog_cb(GtkWidget *w _U_)
197 {
198         /* No Apply button, and there's no text widget to set, much less
199            activate, on "OK". */
200         static construct_args_t args = {
201                 "Wireshark: Capture Filter",
202                 FALSE,
203                 FALSE,
204         FALSE
205         };
206
207         /* Has a filter dialog box already been opened for editing
208            capture filters? */
209         if (global_cfilter_w != NULL) {
210                 /* Yes.  Just reactivate it. */
211                 reactivate_window(global_cfilter_w);
212                 return;
213         }
214
215         /*
216          * No.  Create one; we didn't pop this up as a result of pressing
217          * a button next to some text entry field, so don't associate it
218          * with a text entry field or button.
219          */
220         global_cfilter_w = filter_dialog_new(NULL, NULL, CFILTER_LIST, &args);
221 }
222 #endif
223
224 /* Create a filter dialog for editing display filters; this is to be used
225    as a callback for menu items, toolbars, etc.. */
226 void
227 dfilter_dialog_cb(GtkWidget *w _U_)
228 {
229         static construct_args_t args = {
230                 "Wireshark: Display Filter",
231                 TRUE,
232                 TRUE,
233         FALSE
234         };
235
236     display_filter_construct_cb(OBJECT_GET_DATA(top_level, E_FILT_BT_PTR_KEY), &args);
237 }
238
239 /* List of capture filter dialogs, so that if the list of filters changes
240   (the model, if you will), we can update all of their lists displaying
241    the filters (the views). */
242 static GList *cfilter_dialogs;
243
244 /* List of display filter dialogs, so that if the list of filters changes
245   (the model, if you will), we can update all of their lists displaying
246    the filters (the views). */
247 static GList *dfilter_dialogs;
248
249 static void
250 remember_filter_dialog(GtkWidget *main_w, GList **filter_dialogs)
251 {
252         *filter_dialogs = g_list_append(*filter_dialogs, main_w);
253 }
254
255 /* Remove a filter dialog from the specified list of filter_dialogs. */
256 static void
257 forget_filter_dialog(GtkWidget *main_w, filter_list_type_t list_type)
258 {
259         switch (list_type) {
260
261         case CFILTER_EDITED_LIST:
262                 cfilter_dialogs = g_list_remove(cfilter_dialogs, main_w);
263                 break;
264
265         case DFILTER_EDITED_LIST:
266                 dfilter_dialogs = g_list_remove(dfilter_dialogs, main_w);
267                 break;
268
269         default:
270                 g_assert_not_reached();
271                 break;
272         }
273 }
274
275 /* Get the dialog list corresponding to a particular filter list. */
276 static GList *
277 get_filter_dialog_list(filter_list_type_t list_type)
278 {
279         switch (list_type) {
280
281         case CFILTER_EDITED_LIST:
282                 return cfilter_dialogs;
283
284         case DFILTER_EDITED_LIST:
285                 return dfilter_dialogs;
286
287         default:
288                 g_assert_not_reached();
289                 return NULL;
290         }
291 }
292
293 static GtkWidget *
294 filter_dialog_new(GtkWidget *button, GtkWidget *parent_filter_te,
295                   filter_list_type_t list_type, construct_args_t *construct_args)
296 {
297     GtkWidget  *main_w,           /* main window */
298                *main_vb,          /* main container */
299                *bbox,             /* button container */
300                *ok_bt,            /* "OK" button */
301                *apply_bt,         /* "Apply" button */
302                *save_bt,          /* "Save" button */
303                *cancel_bt,        /* "Cancel" button */
304                *help_bt;          /* "Help" button */
305     GtkWidget  *filter_vb,        /* filter settings box */
306                *props_vb;
307     GtkWidget  *top_hb,
308                *list_bb,
309                *new_bt,
310                *del_bt,
311                *filter_sc,
312                *filter_l,
313                *middle_hb,
314                *name_lb,
315                *name_te,
316                *bottom_hb,
317                *filter_lb,
318                *filter_te,
319                *add_expression_bt,
320                *filter_fr,
321                *edit_fr,
322                *props_fr;
323     GtkTooltips *tooltips;
324     GList      *fl_entry;
325     filter_def *filt;
326     static filter_list_type_t cfilter_list_type = CFILTER_EDITED_LIST;
327     static filter_list_type_t dfilter_list_type = DFILTER_EDITED_LIST;
328     filter_list_type_t *filter_list_type_p;
329     GList       **filter_dialogs;
330     const gchar *filter_te_str = NULL;
331 #if GTK_MAJOR_VERSION < 2
332     GtkWidget  *nl_item,
333                *nl_lb,
334                *l_select = NULL;
335 #else
336     gboolean           l_select = FALSE;
337     GtkListStore      *store;
338     GtkCellRenderer   *renderer;
339     GtkTreeViewColumn *column;
340     GtkTreeSelection  *sel;
341     GtkTreeIter        iter;
342     GtkTreeIter        sel_iter;
343 #endif
344
345     /* Get a pointer to a static variable holding the type of filter on
346        which we're working, so we can pass that pointer to callback
347        routines. */
348     switch (list_type) {
349
350     case CFILTER_LIST:
351         filter_dialogs = &cfilter_dialogs;
352         filter_list_type_p = &cfilter_list_type;
353         list_type = CFILTER_EDITED_LIST;
354         break;
355
356     case DFILTER_LIST:
357         filter_dialogs = &dfilter_dialogs;
358         filter_list_type_p = &dfilter_list_type;
359         list_type = DFILTER_EDITED_LIST;
360         break;
361
362     default:
363         g_assert_not_reached();
364         filter_dialogs = NULL;
365         filter_list_type_p = NULL;
366         break;
367     }
368
369     tooltips = gtk_tooltips_new ();
370
371     main_w = dlg_window_new(construct_args->title);
372         gtk_window_set_default_size(GTK_WINDOW(main_w), 400, 400);
373     OBJECT_SET_DATA(main_w, E_FILT_CONSTRUCT_ARGS_KEY, construct_args);
374
375     if(construct_args->modal_and_transient) {
376         GdkWindow*  parent = gtk_widget_get_parent_window(parent_filter_te);
377         gtk_window_set_transient_for(GTK_WINDOW(main_w), GTK_WINDOW(parent));
378         gtk_window_set_modal(GTK_WINDOW(main_w), TRUE);
379     }
380
381     main_vb = gtk_vbox_new(FALSE, 0);
382     gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
383     gtk_container_add(GTK_CONTAINER(main_w), main_vb);
384     gtk_widget_show(main_vb);
385
386     /* Make sure everything is set up */
387     if (parent_filter_te)
388         filter_te_str = gtk_entry_get_text(GTK_ENTRY(parent_filter_te));
389
390     /* Container for each row of widgets */
391     filter_vb = gtk_vbox_new(FALSE, 0);
392     gtk_container_border_width(GTK_CONTAINER(filter_vb), 0);
393     gtk_container_add(GTK_CONTAINER(main_vb), filter_vb);
394     gtk_widget_show(filter_vb);
395
396     /* Top row: Buttons and filter list */
397     top_hb = gtk_hbox_new(FALSE, 0);
398     gtk_container_add(GTK_CONTAINER(filter_vb), top_hb);
399     gtk_widget_show(top_hb);
400
401     edit_fr = gtk_frame_new("Edit");
402     gtk_box_pack_start(GTK_BOX(top_hb), edit_fr, FALSE, FALSE, 0);
403     gtk_widget_show(edit_fr);
404
405     list_bb = gtk_vbox_new(TRUE, 0);
406     gtk_container_border_width(GTK_CONTAINER(list_bb), 5);
407     gtk_container_add(GTK_CONTAINER(edit_fr), list_bb);
408     gtk_widget_show(list_bb);
409
410     new_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_NEW);
411     SIGNAL_CONNECT(new_bt, "clicked", filter_new_bt_clicked_cb, filter_list_type_p);
412 #if GTK_MAJOR_VERSION < 2
413     WIDGET_SET_SIZE(new_bt, 50, 20);
414 #endif
415     gtk_widget_show(new_bt);
416     gtk_box_pack_start (GTK_BOX (list_bb), new_bt, FALSE, FALSE, 0);
417     gtk_tooltips_set_tip (tooltips, new_bt, 
418         "Create a new filter at the end of the list (with the current properties)", NULL);
419
420     del_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_DELETE);
421     gtk_widget_set_sensitive(del_bt, FALSE);
422     SIGNAL_CONNECT(del_bt, "clicked", filter_del_bt_clicked_cb, filter_list_type_p);
423     OBJECT_SET_DATA(main_w, E_FILT_DEL_BT_KEY, del_bt);
424 #if GTK_MAJOR_VERSION < 2
425     WIDGET_SET_SIZE(del_bt, 50, 20);
426 #endif
427     gtk_widget_show(del_bt);
428     gtk_box_pack_start (GTK_BOX (list_bb), del_bt, FALSE, FALSE, 0);
429     gtk_tooltips_set_tip (tooltips, del_bt, ("Delete the selected filter"), NULL);
430
431     filter_fr = gtk_frame_new("Filter");
432     gtk_box_pack_start(GTK_BOX(top_hb), filter_fr, TRUE, TRUE, 0);
433     gtk_widget_show(filter_fr);
434
435     filter_sc = scrolled_window_new(NULL, NULL);
436 #if GTK_MAJOR_VERSION >= 2
437     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(filter_sc), 
438                                    GTK_SHADOW_IN);
439 #endif
440
441     gtk_container_set_border_width  (GTK_CONTAINER (filter_sc), 5);
442     gtk_container_add(GTK_CONTAINER(filter_fr), filter_sc);
443     gtk_widget_show(filter_sc);
444
445 #if GTK_MAJOR_VERSION < 2
446     filter_l = gtk_list_new();
447     gtk_list_set_selection_mode(GTK_LIST(filter_l), GTK_SELECTION_SINGLE);
448     SIGNAL_CONNECT(filter_l, "selection_changed", filter_sel_list_cb,
449                    filter_vb);
450 #else
451     store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
452     filter_l = tree_view_new(GTK_TREE_MODEL(store));
453     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(filter_l), FALSE);
454     renderer = gtk_cell_renderer_text_new();
455     column = gtk_tree_view_column_new_with_attributes("", renderer, "text",
456                                                       0, NULL);
457     gtk_tree_view_column_set_sort_column_id(column, 0);
458     gtk_tree_view_append_column(GTK_TREE_VIEW(filter_l), column);
459     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
460     gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
461     SIGNAL_CONNECT(sel, "changed", filter_sel_list_cb, filter_vb);
462     SIGNAL_CONNECT(filter_l, "button_press_event", filter_sel_list_button_cb,
463                    NULL);
464 #endif
465     OBJECT_SET_DATA(main_w, E_FILT_FILTER_L_KEY, filter_l);
466 #if GTK_MAJOR_VERSION < 2
467     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(filter_sc),
468                                           filter_l);
469 #else
470     gtk_container_add(GTK_CONTAINER(filter_sc), filter_l);
471 #endif
472     gtk_widget_show(filter_l);
473
474     OBJECT_SET_DATA(filter_l, E_FILT_DBLFUNC_KEY, filter_dlg_dclick);
475     OBJECT_SET_DATA(filter_l, E_FILT_DBLARG_KEY, main_w);
476     /* This is a Boolean, but we make it a non-null pointer for TRUE
477        and a null pointer for FALSE, as object data is a pointer. */
478     OBJECT_SET_DATA(filter_l, E_FILT_DBLACTIVATE_KEY,
479                     construct_args->activate_on_ok ? "" : NULL);
480
481     /* fill in data */
482     fl_entry = get_filter_list_first(list_type);
483     while (fl_entry != NULL) {
484         filt    = (filter_def *) fl_entry->data;
485 #if GTK_MAJOR_VERSION < 2
486         nl_lb   = gtk_label_new(filt->name);
487         nl_item = gtk_list_item_new();
488
489         SIGNAL_CONNECT(nl_item, "button_press_event", filter_sel_list_button_cb,
490                        filter_l);
491
492         gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
493         gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
494         gtk_widget_show(nl_lb);
495         gtk_container_add(GTK_CONTAINER(filter_l), nl_item);
496         gtk_widget_show(nl_item);
497         OBJECT_SET_DATA(nl_item, E_FILT_LBL_KEY, nl_lb);
498         OBJECT_SET_DATA(nl_item, E_FILT_LIST_ITEM_MODEL_KEY, fl_entry);
499 #else
500         gtk_list_store_append(store, &iter);
501         gtk_list_store_set(store, &iter, 0, filt->name,
502                            1, fl_entry, -1);
503 #endif
504
505         if (filter_te_str && filt->strval) {
506             if (strcmp(filter_te_str, filt->strval) == 0) {
507 #if GTK_MAJOR_VERSION < 2 
508                 l_select = nl_item;
509 #else
510                 sel_iter = iter;
511                 l_select = TRUE;
512 #endif
513             }
514         }
515
516         fl_entry = fl_entry->next;
517     }
518 #if GTK_MAJOR_VERSION >= 2 
519     g_object_unref(G_OBJECT(store));
520 #endif
521
522
523     props_fr = gtk_frame_new("Properties");
524     gtk_box_pack_start(GTK_BOX(filter_vb), props_fr, FALSE, FALSE, 0);
525     gtk_widget_show(props_fr);
526
527     props_vb = gtk_vbox_new(FALSE, 3);
528     gtk_container_border_width(GTK_CONTAINER(props_vb), 5);
529     gtk_container_add(GTK_CONTAINER(props_fr), props_vb);
530     gtk_widget_show(props_vb);
531
532     /* row: Filter name entry */
533     middle_hb = gtk_hbox_new(FALSE, 3);
534     gtk_container_add(GTK_CONTAINER(props_vb), middle_hb);
535     gtk_widget_show(middle_hb);
536
537     name_lb = gtk_label_new("Filter name:");
538     gtk_box_pack_start(GTK_BOX(middle_hb), name_lb, FALSE, FALSE, 0);
539     gtk_widget_show(name_lb);
540
541     name_te = gtk_entry_new();
542     gtk_box_pack_start(GTK_BOX(middle_hb), name_te, TRUE, TRUE, 0);
543     OBJECT_SET_DATA(main_w, E_FILT_NAME_TE_KEY, name_te);
544     SIGNAL_CONNECT(name_te, "changed", filter_name_te_changed_cb, filter_list_type_p);
545     gtk_widget_show(name_te);
546
547     /* row: Filter text entry */
548     bottom_hb = gtk_hbox_new(FALSE, 3);
549     gtk_container_add(GTK_CONTAINER(props_vb), bottom_hb);
550     gtk_widget_show(bottom_hb);
551
552     filter_lb = gtk_label_new("Filter string:");
553     gtk_box_pack_start(GTK_BOX(bottom_hb), filter_lb, FALSE, FALSE, 0);
554     gtk_widget_show(filter_lb);
555
556     filter_te = gtk_entry_new();
557     gtk_box_pack_start(GTK_BOX(bottom_hb), filter_te, TRUE, TRUE, 0);
558     OBJECT_SET_DATA(main_w, E_FILT_FILTER_TE_KEY, filter_te);
559     SIGNAL_CONNECT(filter_te, "changed", filter_name_te_changed_cb, filter_list_type_p);
560     gtk_widget_show(filter_te);
561
562     OBJECT_SET_DATA(main_w, E_FILT_PARENT_FILTER_TE_KEY, parent_filter_te);
563
564     if (list_type == DFILTER_EDITED_LIST) {
565         gtk_tooltips_set_tip(tooltips, filter_te, 
566             "Enter a display filter. "
567             "The background color of this field is changed by a continuous syntax check (green is valid, red is invalid).", 
568             NULL);
569
570         /* Create the "Add Expression..." button, to pop up a dialog
571            for constructing filter comparison expressions. */
572         add_expression_bt = BUTTON_NEW_FROM_STOCK(WIRESHARK_STOCK_ADD_EXPRESSION);
573         SIGNAL_CONNECT(add_expression_bt, "clicked", filter_add_expr_bt_cb, main_w);
574         gtk_box_pack_start(GTK_BOX(bottom_hb), add_expression_bt, FALSE, FALSE, 0);
575         gtk_widget_show(add_expression_bt);
576         gtk_tooltips_set_tip (tooltips, add_expression_bt, ("Add an expression to the filter string"), NULL);
577     }
578
579
580     /* button row */
581     if (parent_filter_te != NULL) {
582         if (construct_args->wants_apply_button) {
583             bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_SAVE, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
584         } else {
585             bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_SAVE, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
586         }
587     } else {
588         if (construct_args->wants_apply_button) {
589             bbox = dlg_button_row_new(GTK_STOCK_APPLY, GTK_STOCK_SAVE, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
590         } else {
591             bbox = dlg_button_row_new(GTK_STOCK_SAVE, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
592         }
593     }
594     gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
595     gtk_widget_show(bbox);
596
597     ok_bt = NULL;
598     if (parent_filter_te != NULL) {
599         /*
600          * We have a filter text entry that we can fill in if
601          * the "OK" button is clicked, so put in an "OK" button.
602          */
603         ok_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_OK);
604         SIGNAL_CONNECT(ok_bt, "clicked", filter_dlg_ok_cb, NULL);
605         gtk_tooltips_set_tip (tooltips, ok_bt, ("Apply the filters and close this dialog"), NULL);
606
607         /* Catch the "activate" signal on the filter name and filter
608            expression text entries, so that if the user types Return
609            there, we act as if the "OK" button had been selected, as
610            happens if Return is typed if some widget that *doesn't*
611            handle the Return key has the input focus. */
612         dlg_set_activate(name_te, ok_bt);
613         dlg_set_activate(filter_te, ok_bt);
614     }
615
616     if (construct_args->wants_apply_button) {
617         apply_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_APPLY);
618         SIGNAL_CONNECT(apply_bt, "clicked", filter_dlg_apply_cb, NULL);
619         gtk_tooltips_set_tip (tooltips, apply_bt, ("Apply the filters and keep this dialog open"), NULL);
620     }
621
622     save_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_SAVE);
623     SIGNAL_CONNECT(save_bt, "clicked", filter_dlg_save_cb, filter_list_type_p);
624     gtk_tooltips_set_tip (tooltips, save_bt, ("Save the filters permanently and keep this dialog open"), NULL);
625
626     cancel_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_CANCEL);
627     gtk_tooltips_set_tip (tooltips, cancel_bt, ("Cancel the changes"), NULL);
628     SIGNAL_CONNECT(cancel_bt, "clicked", filter_dlg_cancel_cb, filter_list_type_p);
629
630     help_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_HELP);
631     if (list_type == CFILTER_EDITED_LIST) {
632         SIGNAL_CONNECT(help_bt, "clicked", topic_cb, HELP_CAPTURE_FILTERS_DIALOG);
633     } else {
634         SIGNAL_CONNECT(help_bt, "clicked", topic_cb, HELP_DISPLAY_FILTERS_DIALOG);
635     }
636     gtk_tooltips_set_tip (tooltips, help_bt, ("Show topic specific help"), NULL);
637
638     if(ok_bt) {
639         gtk_widget_grab_default(ok_bt);
640     }
641
642     remember_filter_dialog(main_w, filter_dialogs);
643
644     if (button != NULL) {
645         /* This dialog box was created by a "Filter" button.
646            Set the E_FILT_BUTTON_PTR_KEY for the new dialog to point to
647            the button. */
648         OBJECT_SET_DATA(main_w, E_FILT_BUTTON_PTR_KEY, button);
649
650         /* Set the E_FILT_DIALOG_PTR_KEY for the button to point to us */
651         OBJECT_SET_DATA(button, E_FILT_DIALOG_PTR_KEY, main_w);
652     }
653
654     /* DO SELECTION THINGS *AFTER* SHOWING THE DIALOG! */
655     /* otherwise the updatings can get confused */
656 #if GTK_MAJOR_VERSION < 2 
657     if (l_select) {
658         gtk_list_select_child(GTK_LIST(filter_l), l_select);
659     } else if (filter_te_str && filter_te_str[0]) {
660         gtk_entry_set_text(GTK_ENTRY(name_te), "New filter");
661         gtk_entry_set_text(GTK_ENTRY(filter_te), filter_te_str);
662     }
663 #else
664     if (l_select) {
665         gtk_tree_selection_select_iter(sel, &sel_iter);        
666     } else if (filter_te_str && filter_te_str[0]) {
667         gtk_entry_set_text(GTK_ENTRY(name_te), "New filter");
668         gtk_entry_set_text(GTK_ENTRY(filter_te), filter_te_str);
669     }
670 #endif
671
672     SIGNAL_CONNECT(main_w, "delete_event", filter_dlg_delete_event_cb, filter_list_type_p);
673     SIGNAL_CONNECT(main_w, "destroy", filter_dlg_destroy_cb, filter_list_type_p);
674
675     gtk_widget_show(main_w);
676     window_present(main_w);
677
678     return main_w;
679 }
680
681 static void
682 filter_dlg_dclick(GtkWidget *filter_l, gpointer main_w_arg, gpointer activate)
683 {
684     GtkWidget  *main_w = GTK_WIDGET(main_w_arg);
685     GtkWidget  *parent_filter_te =
686         OBJECT_GET_DATA(main_w, E_FILT_PARENT_FILTER_TE_KEY);
687     GList      *flp;
688     filter_def *filt;
689 #if GTK_MAJOR_VERSION < 2
690     GList      *sl;
691     GtkObject  *l_item;
692 #else
693     GtkTreeSelection *sel;
694     GtkTreeModel     *model;
695     GtkTreeIter       iter;
696
697     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
698 #endif
699
700     if (parent_filter_te != NULL) {
701         /*
702          * We have a text entry widget associated with this dialog
703          * box; is one of the filters in the list selected?
704          */
705 #if GTK_MAJOR_VERSION < 2
706         sl = GTK_LIST(filter_l)->selection;
707         if (sl != NULL) {
708 #else
709         if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
710 #endif
711             /*
712              * Yes.  Is there a filter definition for that filter?
713              */
714 #if GTK_MAJOR_VERSION < 2
715             l_item = GTK_OBJECT(sl->data);
716             flp = (GList *)OBJECT_GET_DATA(l_item,
717                                                E_FILT_LIST_ITEM_MODEL_KEY);
718 #else
719             gtk_tree_model_get(model, &iter, 1, &flp, -1);
720 #endif
721             if (flp) {
722                 /*
723                  * Yes - put it in the text entry widget.
724                  */
725                 filt = (filter_def *) flp->data;
726                 gtk_entry_set_text(GTK_ENTRY(parent_filter_te),
727                                    filt->strval);
728
729                 /*
730                  * Are we supposed to cause the filter we
731                  * put there to be applied?
732                  */
733                 if (activate != NULL) {
734                     /*
735                      * Yes - do so.
736                      */
737                     SIGNAL_EMIT_BY_NAME(parent_filter_te, "activate", NULL);
738                 }
739             }
740         }
741     }
742
743     window_destroy(main_w);
744 }
745
746 static void
747 filter_dlg_ok_cb(GtkWidget *ok_bt, gpointer data _U_)
748 {
749         /*
750          * Destroy the dialog box and apply the filter.
751          */
752         filter_apply(gtk_widget_get_toplevel(ok_bt), TRUE);
753 }
754
755 static void
756 filter_dlg_apply_cb(GtkWidget *apply_bt, gpointer dummy _U_)
757 {
758         /*
759          * Apply the filter, but don't destroy the dialog box.
760          */
761         filter_apply(gtk_widget_get_toplevel(apply_bt), FALSE);
762 }
763
764 static void
765 filter_apply(GtkWidget *main_w, gboolean destroy)
766 {
767         construct_args_t *construct_args =
768             OBJECT_GET_DATA(main_w, E_FILT_CONSTRUCT_ARGS_KEY);
769         GtkWidget        *parent_filter_te =
770             OBJECT_GET_DATA(main_w, E_FILT_PARENT_FILTER_TE_KEY);
771         GtkWidget        *filter_te;
772         const gchar      *filter_string;
773
774         if (parent_filter_te != NULL) {
775                 /*
776                  * We have a text entry widget associated with this dialog
777                  * box; put the filter in our text entry widget into that
778                  * text entry widget.
779                  */
780                 filter_te = OBJECT_GET_DATA(main_w, E_FILT_FILTER_TE_KEY);
781                 filter_string =
782                     (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
783                 gtk_entry_set_text(GTK_ENTRY(parent_filter_te), filter_string);
784
785         }
786
787         if (destroy) {
788                 /*
789                  * Destroy the filter dialog box.
790                  */
791                 window_destroy(main_w);
792         }
793
794         if (parent_filter_te != NULL) {
795                 /*
796                  * We have a text entry widget associated with this dialog
797                  * box; activate that widget to cause the filter we put
798                  * there to be applied if we're supposed to do so.
799                  *
800                  * We do this after dismissing the filter dialog box,
801                  * as activating the widget the dialog box to which
802                  * it belongs to be dismissed, and that may cause it
803                  * to destroy our dialog box if the filter succeeds.
804                  * This means that our subsequent attempt to destroy
805                  * it will fail.
806                  *
807                  * We don't know whether it'll destroy our dialog box,
808                  * so we can't rely on it to do so.  Instead, we
809                  * destroy it ourselves, which will clear the
810                  * E_FILT_DIALOG_PTR_KEY pointer for their dialog box,
811                  * meaning they won't think it has one and won't try
812                  * to destroy it.
813                  */
814                 if (construct_args->activate_on_ok) {
815                         SIGNAL_EMIT_BY_NAME(parent_filter_te, "activate", NULL);
816                 }
817         }
818 }
819
820 static void
821 filter_dlg_save_cb(GtkWidget *save_bt _U_, gpointer data)
822 {
823         filter_list_type_t list_type = *(filter_list_type_t *)data;
824         char *pf_dir_path;
825         char *f_path;
826         int f_save_errno;
827         const char *filter_type;
828
829         switch (list_type) {
830
831         case CFILTER_EDITED_LIST:
832                 filter_type = "capture";
833                 list_type = CFILTER_LIST;
834                 copy_filter_list(CFILTER_LIST, CFILTER_EDITED_LIST);
835                 break;
836
837         case DFILTER_EDITED_LIST:
838                 filter_type = "display";
839                 list_type = DFILTER_LIST;
840                 copy_filter_list(DFILTER_LIST, DFILTER_EDITED_LIST);
841                 break;
842
843         default:
844                 g_assert_not_reached();
845                 filter_type = NULL;
846                 break;
847         }
848
849         /* Create the directory that holds personal configuration files,
850            if necessary.  */
851         if (create_persconffile_dir(&pf_dir_path) == -1) {
852                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
853                     "Can't create directory\n\"%s\"\nfor filter files: %s.",
854                     pf_dir_path, strerror(errno));
855                 g_free(pf_dir_path);
856                 return;
857         }
858
859         save_filter_list(list_type, &f_path, &f_save_errno);
860         if (f_path != NULL) {
861                 /* We had an error saving the filter. */
862                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
863                     "Could not save to your %s filter file\n\"%s\": %s.",
864                     filter_type, f_path, strerror(f_save_errno));
865                 g_free(f_path);
866         }
867 }
868
869 /* cancel button pressed, revert changes saved and exit dialog */
870 static void
871 filter_dlg_cancel_cb(GtkWidget *cancel_bt, gpointer data)
872 {
873     filter_list_type_t list_type = *(filter_list_type_t *)data;
874     GtkWidget  *main_w = gtk_widget_get_toplevel(cancel_bt);
875
876
877     /* revert changes in the edited list */
878     /* XXX - how to tell other dialogs that the list changed? */
879     switch (list_type) {
880     case CFILTER_EDITED_LIST:
881             copy_filter_list(CFILTER_EDITED_LIST, CFILTER_LIST);
882             break;
883     case DFILTER_EDITED_LIST:
884             copy_filter_list(DFILTER_EDITED_LIST, DFILTER_LIST);
885             break;
886     default:
887             g_assert_not_reached();
888             break;
889     }
890
891     window_destroy(GTK_WIDGET(main_w));
892 }
893
894 /* Treat this as a cancel, by calling "filter_dlg_cancel_cb()" */
895 static gboolean
896 filter_dlg_delete_event_cb(GtkWidget *main_w, GdkEvent *event _U_,
897                            gpointer data)
898 {
899   filter_dlg_cancel_cb(main_w, data);
900   return FALSE;
901 }
902
903
904 static void
905 filter_dlg_destroy_cb(GtkWidget *win, gpointer data)
906 {
907         filter_list_type_t list_type = *(filter_list_type_t *)data;
908         GtkWidget *button;
909
910         /* Get the button that requested that we be popped up, if any.
911            (It should arrange to destroy us if it's destroyed, so
912            that we don't get a pointer to a non-existent window here.) */
913         button = OBJECT_GET_DATA(win, E_FILT_BUTTON_PTR_KEY);
914
915         if (button != NULL) {
916                 /* Tell it we no longer exist. */
917                 OBJECT_SET_DATA(button, E_FILT_DIALOG_PTR_KEY, NULL);
918         } else {
919                 /* This is an editing dialog popped up from, for example,
920                    a menu item; note that we no longer have one. */
921                 switch (list_type) {
922
923 #ifdef HAVE_LIBPCAP
924                 case CFILTER_EDITED_LIST:
925                         g_assert(win == global_cfilter_w);
926                         global_cfilter_w = NULL;
927                         break;
928 #endif
929                 default:
930                         g_assert_not_reached();
931                         break;
932                 }
933         }
934
935         /* Remove this from the list of filter dialog windows. */
936         forget_filter_dialog(win, list_type);
937 }
938
939 #if GTK_MAJOR_VERSION < 2
940 static gint
941 filter_sel_list_button_cb(GtkWidget *widget, GdkEventButton *event,
942                           gpointer func_data)
943 #else
944 static gint
945 filter_sel_list_button_cb(GtkWidget *list, GdkEventButton *event,
946                           gpointer data _U_)
947 #endif
948 {
949 #if GTK_MAJOR_VERSION < 2
950     GtkWidget *list = func_data;
951 #endif
952     void (* func)(GtkWidget *, gpointer, gpointer);
953     gpointer func_arg;
954     gpointer func_activate;
955
956 #if GTK_MAJOR_VERSION < 2
957     if (!GTK_IS_LIST_ITEM(widget)) return FALSE;
958 #endif
959     if (event->type == GDK_2BUTTON_PRESS) {
960         func = OBJECT_GET_DATA(list, E_FILT_DBLFUNC_KEY);
961         func_arg = OBJECT_GET_DATA(list, E_FILT_DBLARG_KEY);
962         func_activate = OBJECT_GET_DATA(list, E_FILT_DBLACTIVATE_KEY);
963
964         if (func)
965             (*func)(list, func_arg, func_activate);
966     }
967
968     return FALSE;
969 }
970
971 #if GTK_MAJOR_VERSION < 2
972 static void
973 filter_sel_list_cb(GtkWidget *l, gpointer data _U_)
974 #else
975 static void
976 filter_sel_list_cb(GtkTreeSelection *sel, gpointer data _U_)
977 #endif
978 {
979 #if GTK_MAJOR_VERSION < 2
980     GtkWidget    *main_w = gtk_widget_get_toplevel(l);
981     GList        *sl;
982     GtkObject    *l_item;
983 #else
984     GtkWidget    *filter_l = GTK_WIDGET(gtk_tree_selection_get_tree_view(sel));
985     GtkWidget    *main_w = gtk_widget_get_toplevel(filter_l);
986     GtkTreeModel *model;
987     GtkTreeIter   iter;
988 #endif
989     GtkWidget    *name_te = OBJECT_GET_DATA(main_w, E_FILT_NAME_TE_KEY);
990     GtkWidget    *filter_te = OBJECT_GET_DATA(main_w, E_FILT_FILTER_TE_KEY);
991     GtkWidget    *chg_bt = OBJECT_GET_DATA(main_w, E_FILT_CHG_BT_KEY);
992     GtkWidget    *copy_bt = OBJECT_GET_DATA(main_w, E_FILT_COPY_BT_KEY);
993     GtkWidget    *del_bt = OBJECT_GET_DATA(main_w, E_FILT_DEL_BT_KEY);
994     filter_def   *filt;
995     gchar        *name = NULL, *strval = NULL;
996     GList        *flp;
997     gint          sensitivity = FALSE;
998
999 #if GTK_MAJOR_VERSION < 2
1000     if (l)
1001         sl = GTK_LIST(l)->selection;
1002     else
1003         sl = NULL;
1004
1005     if (sl) {  /* Something was selected */
1006         l_item = GTK_OBJECT(sl->data);
1007         flp    = (GList *) OBJECT_GET_DATA(l_item, E_FILT_LIST_ITEM_MODEL_KEY);
1008 #else
1009     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1010         gtk_tree_model_get(model, &iter, 1, &flp, -1);
1011 #endif
1012         if (flp) {
1013             filt   = (filter_def *) flp->data;
1014             name   = g_strdup(filt->name);
1015             strval = g_strdup(filt->strval);
1016             sensitivity = TRUE;
1017         }
1018     }
1019
1020     /*
1021      * Did you know that this function is called when the window is destroyed?
1022      * Funny, that.
1023      * This means that we have to:
1024      *
1025      *  attach to the top-level window data items containing pointers to
1026      *  the widgets we affect here;
1027      *
1028      *  give each of those widgets their own destroy callbacks;
1029      *
1030      *  clear that pointer when the widget is destroyed;
1031      *
1032      *  don't do anything to the widget if the pointer we get back is
1033      *  null;
1034      *
1035      * so that if we're called after any of the widgets we'd affect are
1036      * destroyed, we know that we shouldn't do anything to those widgets.
1037      */
1038     if (name_te != NULL)
1039         gtk_entry_set_text(GTK_ENTRY(name_te), name ? name : "");
1040     if (filter_te != NULL)
1041         gtk_entry_set_text(GTK_ENTRY(filter_te), strval ? strval : "");
1042     if (chg_bt != NULL)
1043         gtk_widget_set_sensitive(chg_bt, sensitivity);
1044     if (copy_bt != NULL)
1045         gtk_widget_set_sensitive(copy_bt, sensitivity);
1046     if (del_bt != NULL)
1047         gtk_widget_set_sensitive(del_bt, sensitivity);
1048     if (name != NULL)
1049         g_free(name);
1050     if (strval != NULL)
1051         g_free(strval);
1052 }
1053
1054 /* To do: add input checking to each of these callbacks */
1055
1056 /* Structure containing arguments to be passed to "new_filter_cb()".
1057
1058    "active_filter_l" is the list in the dialog box in which "New" or
1059    "Copy" was clicked; in that dialog box, but not in any other dialog
1060    box, we select the newly created list item.
1061
1062    "nflp" is the GList member in the model (filter list) for the new
1063    filter. */
1064 typedef struct {
1065         GtkWidget *active_filter_l;
1066         GList     *nflp;
1067 } new_filter_cb_args_t;
1068
1069 static void
1070 new_filter_cb(gpointer data, gpointer user_data)
1071 {
1072     GtkWidget    *main_w = data;
1073 #if GTK_MAJOR_VERSION < 2
1074     GtkWidget    *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1075     GtkWidget    *nl_lb, *nl_item;
1076 #else
1077     GtkTreeView  *filter_l;
1078     GtkListStore *store;
1079     GtkTreeIter   iter;
1080 #endif
1081     new_filter_cb_args_t *args = user_data;
1082     filter_def *nfilt = args->nflp->data;
1083
1084 #if GTK_MAJOR_VERSION < 2
1085     nl_lb        = gtk_label_new(nfilt->name);
1086     nl_item      = gtk_list_item_new();
1087     gtk_misc_set_alignment(GTK_MISC(nl_lb), 0.0, 0.5);
1088     gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
1089     gtk_widget_show(nl_lb);
1090     gtk_container_add(GTK_CONTAINER(filter_l), nl_item);
1091     gtk_widget_show(nl_item);
1092     OBJECT_SET_DATA(nl_item, E_FILT_LBL_KEY, nl_lb);
1093     OBJECT_SET_DATA(GTK_OBJECT(nl_item), E_FILT_LIST_ITEM_MODEL_KEY,
1094                         args->nflp);
1095     if (filter_l == args->active_filter_l) {
1096         /* Select the item. */
1097         gtk_list_select_child(GTK_LIST(filter_l), nl_item);
1098     }
1099 #else
1100     filter_l = GTK_TREE_VIEW(OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY));
1101     store = GTK_LIST_STORE(gtk_tree_view_get_model(filter_l));
1102     gtk_list_store_append(store, &iter);
1103     gtk_list_store_set(store, &iter, 0, nfilt->name, 1, args->nflp, -1);
1104     if (GTK_WIDGET(filter_l) == args->active_filter_l) {
1105         /* Select the item. */
1106         gtk_tree_selection_select_iter(gtk_tree_view_get_selection(filter_l),
1107                                        &iter);
1108     }
1109 #endif
1110 }
1111
1112 static void
1113 filter_new_bt_clicked_cb(GtkWidget *w, gpointer data)
1114 {
1115   GtkWidget  *main_w = gtk_widget_get_toplevel(w);
1116   GtkWidget  *name_te = OBJECT_GET_DATA(main_w, E_FILT_NAME_TE_KEY);
1117   GtkWidget  *filter_te = OBJECT_GET_DATA(main_w, E_FILT_FILTER_TE_KEY);
1118   GtkWidget  *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1119   filter_list_type_t list_type = *(filter_list_type_t *)data;
1120   GList      *fl_entry;
1121   const gchar *name, *strval;
1122   new_filter_cb_args_t args;
1123
1124   name   = gtk_entry_get_text(GTK_ENTRY(name_te));
1125   strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
1126
1127   /* if the user didn't entered a name, set default one */
1128   if (strlen(name) == 0) {
1129     name = "new";
1130   }
1131
1132   /* if the user didn't entered a string value, set default one */
1133   if (strlen(strval) == 0) {
1134     strval = "new";
1135   }
1136
1137     /* Add a new entry to the filter list. */
1138     fl_entry = add_to_filter_list(list_type, name, strval);
1139
1140     /* Update all the filter list widgets, not just the one in
1141        the dialog box in which we clicked on "Copy". */
1142     args.active_filter_l = filter_l;
1143     args.nflp = fl_entry;
1144     g_list_foreach(get_filter_dialog_list(list_type), new_filter_cb, &args);
1145
1146 }
1147
1148 #if GTK_MAJOR_VERSION < 2
1149 static void
1150 chg_list_item_cb(GtkWidget *nl_item, gpointer data)
1151 #else
1152 static gboolean
1153 chg_list_item_cb(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
1154                  gpointer data)
1155 #endif
1156 {
1157     GList      *flp = data;
1158     filter_def *filt = flp->data;
1159 #if GTK_MAJOR_VERSION < 2
1160     GtkLabel   *nl_lb =
1161         GTK_LABEL(OBJECT_GET_DATA(nl_item, E_FILT_LBL_KEY));
1162     GList      *nl_model =
1163         OBJECT_GET_DATA(nl_item, E_FILT_LIST_ITEM_MODEL_KEY);
1164 #else
1165     GList      *nl_model;
1166 #endif
1167
1168 #if GTK_MAJOR_VERSION >= 2
1169     gtk_tree_model_get(model, iter, 1, &nl_model, -1);
1170 #endif
1171     /* Is this the item corresponding to the filter list item in question? */
1172     if (flp == nl_model) {
1173         /* Yes - change the label to correspond to the new name for the
1174          * filter. */
1175 #if GTK_MAJOR_VERSION < 2
1176         gtk_label_set(nl_lb, filt->name);
1177 #else
1178         gtk_list_store_set(GTK_LIST_STORE(model), iter, 0, filt->name, -1);
1179         return TRUE;
1180 #endif
1181     }
1182 #if GTK_MAJOR_VERSION >= 2
1183     return FALSE;
1184 #endif
1185 }
1186
1187 static void
1188 chg_filter_cb(gpointer data, gpointer user_data)
1189 {
1190     GtkWidget  *main_w = data;
1191     GtkWidget  *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1192
1193 #if GTK_MAJOR_VERSION < 2
1194     gtk_container_foreach(GTK_CONTAINER(filter_l), chg_list_item_cb, user_data);
1195 #else
1196     gtk_tree_model_foreach(gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l)),
1197                            chg_list_item_cb, user_data);
1198 #endif
1199 }
1200
1201 static void
1202 filter_name_te_changed_cb(GtkWidget *w, gpointer data)
1203 {
1204     GtkWidget  *main_w = gtk_widget_get_toplevel(w);
1205     GtkWidget  *name_te = OBJECT_GET_DATA(main_w, E_FILT_NAME_TE_KEY);
1206     GtkWidget  *filter_te = OBJECT_GET_DATA(main_w, E_FILT_FILTER_TE_KEY);
1207     GtkWidget  *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1208     filter_def *filt;
1209     GList      *fl_entry;
1210     filter_list_type_t  list_type = *(filter_list_type_t *)data;
1211     const gchar         *name = "";
1212     const gchar         *strval = "";
1213
1214 #if GTK_MAJOR_VERSION < 2
1215     GList      *sl;
1216     GtkObject  *l_item;
1217     GtkLabel   *nl_lb;
1218 #else
1219     GtkTreeSelection  *sel;
1220     GtkTreeModel      *model;
1221     GtkTreeIter        iter;
1222 #endif
1223
1224 #if GTK_MAJOR_VERSION < 2
1225     sl     = GTK_LIST(filter_l)->selection;
1226 #else
1227     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
1228 #endif
1229     name   = gtk_entry_get_text(GTK_ENTRY(name_te));
1230     strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
1231
1232     if (DFILTER_EDITED_LIST == list_type) {
1233         /* colorize filter string entry */
1234         filter_te_syntax_check_cb(filter_te);
1235     }
1236
1237     /* if something was selected */
1238 #if GTK_MAJOR_VERSION < 2
1239     if (sl) {
1240         l_item = GTK_OBJECT(sl->data);
1241         fl_entry = (GList *) OBJECT_GET_DATA(l_item,
1242                                                  E_FILT_LIST_ITEM_MODEL_KEY);
1243         nl_lb = (GtkLabel *) OBJECT_GET_DATA(l_item, E_FILT_LBL_KEY);
1244         if (fl_entry != NULL && nl_lb != NULL) {
1245 #else
1246     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1247         gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
1248         if (fl_entry != NULL) {
1249 #endif
1250             filt = (filter_def *) fl_entry->data;
1251
1252             if (strlen(name) > 0 && strlen(strval) > 0 && filt) {
1253                 g_free(filt->name);
1254                 g_free(filt->strval);
1255                 filt->name   = g_strdup(name);
1256                 filt->strval = g_strdup(strval);
1257
1258                 /* Update all the filter list widgets, not just the one in
1259                    the dialog box in which we clicked on "Copy". */
1260                 g_list_foreach(get_filter_dialog_list(list_type), chg_filter_cb,
1261                                fl_entry);
1262             }
1263         }
1264     }
1265 }
1266
1267 static void
1268 delete_filter_cb(gpointer data, gpointer user_data)
1269 {
1270     GtkWidget    *main_w = data;
1271     GtkWidget    *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1272 #if GTK_MAJOR_VERSION < 2
1273     gint          pos = *(gint *)user_data;
1274 #else
1275     gchar        *pos = (gchar *)user_data;
1276     GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l));
1277     GtkTreeIter   iter;
1278 #endif
1279
1280 #if GTK_MAJOR_VERSION < 2
1281     gtk_list_clear_items(GTK_LIST(filter_l), pos, pos + 1);
1282 #else
1283     gtk_tree_model_get_iter_from_string(model, &iter, pos);
1284     gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
1285 #endif
1286 }
1287
1288 static void
1289 filter_del_bt_clicked_cb(GtkWidget *w, gpointer data)
1290 {
1291     GtkWidget  *main_w = gtk_widget_get_toplevel(w);
1292     GtkWidget  *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1293     filter_list_type_t list_type = *(filter_list_type_t *)data;
1294     GList      *fl_entry;
1295 #if GTK_MAJOR_VERSION < 2
1296     GList      *sl;
1297     GtkObject  *l_item;
1298     gint        pos;
1299 #else
1300     gchar             *pos;
1301     GtkTreeSelection  *sel;
1302     GtkTreeModel      *model;
1303     GtkTreeIter        iter;
1304     GtkTreePath       *path;
1305 #endif
1306
1307 #if GTK_MAJOR_VERSION < 2
1308     sl = GTK_LIST(filter_l)->selection;
1309     if (sl) {  /* Something was selected */
1310         l_item = GTK_OBJECT(sl->data);
1311         pos    = gtk_list_child_position(GTK_LIST(filter_l),
1312                                          GTK_WIDGET(l_item));
1313         fl_entry = (GList *) OBJECT_GET_DATA(l_item, E_FILT_LIST_ITEM_MODEL_KEY);
1314 #else
1315     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
1316     /* If something was selected */
1317     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1318         gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
1319         path = gtk_tree_model_get_path(model, &iter);
1320         pos = gtk_tree_path_to_string(path);
1321         gtk_tree_path_free(path);
1322 #endif
1323         if (fl_entry != NULL) {
1324             /* Remove the entry from the filter list. */
1325             remove_from_filter_list(list_type, fl_entry);
1326
1327             /* Update all the filter list widgets, not just the one in
1328                the dialog box in which we clicked on "Delete". */
1329 #if GTK_MAJOR_VERSION < 2
1330             g_list_foreach(get_filter_dialog_list(list_type), delete_filter_cb,
1331                            &pos);
1332 #else
1333             g_list_foreach(get_filter_dialog_list(list_type), delete_filter_cb, pos);
1334 #endif
1335         }
1336 #if GTK_MAJOR_VERSION >= 2
1337         g_free(pos);
1338 #endif
1339     }
1340 }
1341
1342 void
1343 filter_add_expr_bt_cb(GtkWidget *w _U_, gpointer main_w_arg)
1344 {
1345         GtkWidget  *main_w = GTK_WIDGET(main_w_arg);
1346         GtkWidget  *filter_te;
1347
1348         filter_te = OBJECT_GET_DATA(main_w, E_FILT_FILTER_TE_KEY);
1349         dfilter_expr_dlg_new(filter_te);
1350 }
1351
1352 static void
1353 color_filter_te(GtkWidget *w, guint16 red, guint16 green, guint16 blue)
1354 {
1355     GdkColor    bg;
1356     GtkStyle    *style;
1357
1358     bg.pixel    = 0;
1359     bg.red      = red;
1360     bg.green    = green;
1361     bg.blue     = blue;
1362
1363     style = gtk_style_copy(gtk_widget_get_style(w));
1364     style->base[GTK_STATE_NORMAL] = bg;
1365     gtk_widget_set_style(w, style);
1366     gtk_style_unref(style);
1367 }
1368
1369 void
1370 colorize_filter_te_as_empty(GtkWidget *w)
1371 {
1372     /* white */
1373     color_filter_te(w, 0xFFFF, 0xFFFF, 0xFFFF);
1374 }
1375
1376 void
1377 colorize_filter_te_as_invalid(GtkWidget *w)
1378 {
1379     /* light red */
1380     color_filter_te(w, 0xFFFF, 0xAFFF, 0xAFFF);
1381 }
1382
1383 void
1384 colorize_filter_te_as_valid(GtkWidget *w)
1385 {
1386     /* light green */
1387     color_filter_te(w, 0xAFFF, 0xFFFF, 0xAFFF);
1388 }
1389
1390 void
1391 filter_te_syntax_check_cb(GtkWidget *w)
1392 {
1393     const gchar *strval;
1394     dfilter_t   *dfp;
1395
1396     strval = gtk_entry_get_text(GTK_ENTRY(w));
1397
1398     /* colorize filter string entry */
1399     if (strval && dfilter_compile(strval, &dfp)) {
1400         if (dfp != NULL)
1401           dfilter_free(dfp);
1402         if (strlen(strval) == 0)
1403             colorize_filter_te_as_empty(w);
1404         else
1405             colorize_filter_te_as_valid(w);
1406     } else
1407         colorize_filter_te_as_invalid(w);
1408 }
1409
1410