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