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