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