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