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