now: Don't have a Save button in the following settings dialogs:
[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 dummy);
73 static void filter_dlg_apply_cb(GtkWidget *apply_bt, gpointer dummy);
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, NULL);
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 dummy _U_)
802 {
803         /*
804          * Apply the filter, but don't destroy the dialog box.
805          */
806         filter_apply(gtk_widget_get_toplevel(apply_bt), FALSE);
807 }
808
809 static void
810 filter_apply(GtkWidget *main_w, gboolean destroy)
811 {
812         construct_args_t *construct_args =
813             OBJECT_GET_DATA(main_w, E_FILT_CONSTRUCT_ARGS_KEY);
814         GtkWidget        *parent_filter_te =
815             OBJECT_GET_DATA(main_w, E_FILT_PARENT_FILTER_TE_KEY);
816         GtkWidget        *filter_te;
817         const gchar      *filter_string;
818
819         if (parent_filter_te != NULL) {
820                 /*
821                  * We have a text entry widget associated with this dialog
822                  * box; put the filter in our text entry widget into that
823                  * text entry widget.
824                  */
825                 filter_te = OBJECT_GET_DATA(main_w, E_FILT_FILTER_TE_KEY);
826                 filter_string =
827                     (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
828                 gtk_entry_set_text(GTK_ENTRY(parent_filter_te), filter_string);
829
830         }
831
832         if (destroy) {
833                 /*
834                  * Destroy the filter dialog box.
835                  */
836                 window_destroy(main_w);
837         }
838
839         if (parent_filter_te != NULL) {
840                 /*
841                  * We have a text entry widget associated with this dialog
842                  * box; activate that widget to cause the filter we put
843                  * there to be applied if we're supposed to do so.
844                  *
845                  * We do this after dismissing the filter dialog box,
846                  * as activating the widget the dialog box to which
847                  * it belongs to be dismissed, and that may cause it
848                  * to destroy our dialog box if the filter succeeds.
849                  * This means that our subsequent attempt to destroy
850                  * it will fail.
851                  *
852                  * We don't know whether it'll destroy our dialog box,
853                  * so we can't rely on it to do so.  Instead, we
854                  * destroy it ourselves, which will clear the
855                  * E_FILT_DIALOG_PTR_KEY pointer for their dialog box,
856                  * meaning they won't think it has one and won't try
857                  * to destroy it.
858                  */
859                 if (construct_args->activate_on_ok) {
860                         SIGNAL_EMIT_BY_NAME(parent_filter_te, "activate", NULL);
861                 }
862         }
863 }
864
865
866 static void
867 filter_dlg_save(filter_list_type_t list_type)
868 {
869         char *pf_dir_path;
870         char *f_path;
871         int f_save_errno;
872         const char *filter_type;
873
874         switch (list_type) {
875
876         case CFILTER_EDITED_LIST:
877                 filter_type = "capture";
878                 list_type = CFILTER_LIST;
879                 copy_filter_list(CFILTER_LIST, CFILTER_EDITED_LIST);
880                 break;
881
882         case DFILTER_EDITED_LIST:
883                 filter_type = "display";
884                 list_type = DFILTER_LIST;
885                 copy_filter_list(DFILTER_LIST, DFILTER_EDITED_LIST);
886                 break;
887
888         default:
889                 g_assert_not_reached();
890                 filter_type = NULL;
891                 break;
892         }
893
894         /* Create the directory that holds personal configuration files,
895            if necessary.  */
896         if (create_persconffile_dir(&pf_dir_path) == -1) {
897                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
898                     "Can't create directory\n\"%s\"\nfor filter files: %s.",
899                     pf_dir_path, strerror(errno));
900                 g_free(pf_dir_path);
901                 return;
902         }
903
904         save_filter_list(list_type, &f_path, &f_save_errno);
905         if (f_path != NULL) {
906                 /* We had an error saving the filter. */
907                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
908                     "Could not save to your %s filter file\n\"%s\": %s.",
909                     filter_type, f_path, strerror(f_save_errno));
910                 g_free(f_path);
911         }
912 }
913
914
915 static void
916 filter_dlg_save_cb(GtkWidget *save_bt _U_, gpointer data)
917 {
918         filter_list_type_t list_type = *(filter_list_type_t *)data;
919
920         filter_dlg_save(list_type);
921 }
922
923 #if 0
924 /* update a remaining dialog if another one was cancelled */
925 static void filter_dlg_update_list_cb(gpointer data, gpointer user_data)
926 {
927     GtkWidget  *main_w = data;
928     filter_list_type_t list_type = *(filter_list_type_t *)user_data;
929
930     /* refill the list */
931     clear_list(main_w);
932     fill_list(main_w, list_type);
933 }
934 #endif
935
936 /* cancel button pressed, revert changes and exit dialog */
937 static void
938 filter_dlg_cancel_cb(GtkWidget *cancel_bt, gpointer data)
939 {
940     filter_list_type_t list_type = *(filter_list_type_t *)data;
941     GtkWidget  *main_w = gtk_widget_get_toplevel(cancel_bt);
942     static GList *filter_list;
943
944
945     window_destroy(GTK_WIDGET(main_w));
946
947     /* if this was the last open filter dialog, revert the changes made */
948     filter_list = get_filter_dialog_list(list_type);
949     if(g_list_length(filter_list) == 0) {
950         /* revert changes in the edited list */
951         switch (list_type) {
952         case CFILTER_EDITED_LIST:
953                 copy_filter_list(CFILTER_EDITED_LIST, CFILTER_LIST);
954                 break;
955         case DFILTER_EDITED_LIST:
956                 copy_filter_list(DFILTER_EDITED_LIST, DFILTER_LIST);
957                 break;
958         default:
959                 g_assert_not_reached();
960                 break;
961         }
962     }
963
964 #if 0
965     /* update other open filter dialogs */
966     g_list_foreach(get_filter_dialog_list(list_type), filter_dlg_update_list_cb, &list_type);
967 #endif
968 }
969
970 /* Treat this as a cancel, by calling "filter_dlg_cancel_cb()" */
971 static gboolean
972 filter_dlg_delete_event_cb(GtkWidget *main_w, GdkEvent *event _U_,
973                            gpointer data)
974 {
975   filter_dlg_cancel_cb(main_w, data);
976   return FALSE;
977 }
978
979
980 static void
981 filter_dlg_destroy_cb(GtkWidget *win, gpointer data)
982 {
983         filter_list_type_t list_type = *(filter_list_type_t *)data;
984         GtkWidget *button;
985
986         /* Get the button that requested that we be popped up, if any.
987            (It should arrange to destroy us if it's destroyed, so
988            that we don't get a pointer to a non-existent window here.) */
989         button = OBJECT_GET_DATA(win, E_FILT_BUTTON_PTR_KEY);
990
991         if (button != NULL) {
992                 /* Tell it we no longer exist. */
993                 OBJECT_SET_DATA(button, E_FILT_DIALOG_PTR_KEY, NULL);
994         } else {
995                 /* This is an editing dialog popped up from, for example,
996                    a menu item; note that we no longer have one. */
997                 switch (list_type) {
998
999 #ifdef HAVE_LIBPCAP
1000                 case CFILTER_EDITED_LIST:
1001                         g_assert(win == global_cfilter_w);
1002                         global_cfilter_w = NULL;
1003                         break;
1004 #endif
1005                 default:
1006                         g_assert_not_reached();
1007                         break;
1008                 }
1009         }
1010
1011         /* Remove this from the list of filter dialog windows. */
1012         forget_filter_dialog(win, list_type);
1013 }
1014
1015 #if GTK_MAJOR_VERSION < 2
1016 static gint
1017 filter_sel_list_button_cb(GtkWidget *widget, GdkEventButton *event,
1018                           gpointer func_data)
1019 #else
1020 static gint
1021 filter_sel_list_button_cb(GtkWidget *list, GdkEventButton *event,
1022                           gpointer data _U_)
1023 #endif
1024 {
1025 #if GTK_MAJOR_VERSION < 2
1026     GtkWidget *list = func_data;
1027 #endif
1028     void (* func)(GtkWidget *, gpointer, gpointer);
1029     gpointer func_arg;
1030     gpointer func_activate;
1031
1032 #if GTK_MAJOR_VERSION < 2
1033     if (!GTK_IS_LIST_ITEM(widget)) return FALSE;
1034 #endif
1035     if (event->type == GDK_2BUTTON_PRESS) {
1036         func = OBJECT_GET_DATA(list, E_FILT_DBLFUNC_KEY);
1037         func_arg = OBJECT_GET_DATA(list, E_FILT_DBLARG_KEY);
1038         func_activate = OBJECT_GET_DATA(list, E_FILT_DBLACTIVATE_KEY);
1039
1040         if (func)
1041             (*func)(list, func_arg, func_activate);
1042     }
1043
1044     return FALSE;
1045 }
1046
1047 #if GTK_MAJOR_VERSION < 2
1048 static void
1049 filter_sel_list_cb(GtkWidget *l, gpointer data _U_)
1050 #else
1051 static void
1052 filter_sel_list_cb(GtkTreeSelection *sel, gpointer data _U_)
1053 #endif
1054 {
1055 #if GTK_MAJOR_VERSION < 2
1056     GtkWidget    *main_w = gtk_widget_get_toplevel(l);
1057     GList        *sl;
1058     GtkObject    *l_item;
1059 #else
1060     GtkWidget    *filter_l = GTK_WIDGET(gtk_tree_selection_get_tree_view(sel));
1061     GtkWidget    *main_w = gtk_widget_get_toplevel(filter_l);
1062     GtkTreeModel *model;
1063     GtkTreeIter   iter;
1064 #endif
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    *chg_bt = OBJECT_GET_DATA(main_w, E_FILT_CHG_BT_KEY);
1068     GtkWidget    *copy_bt = OBJECT_GET_DATA(main_w, E_FILT_COPY_BT_KEY);
1069     GtkWidget    *del_bt = OBJECT_GET_DATA(main_w, E_FILT_DEL_BT_KEY);
1070     filter_def   *filt;
1071     gchar        *name = NULL, *strval = NULL;
1072     GList        *flp;
1073     gint          sensitivity = FALSE;
1074
1075 #if GTK_MAJOR_VERSION < 2
1076     if (l)
1077         sl = GTK_LIST(l)->selection;
1078     else
1079         sl = NULL;
1080
1081     if (sl) {  /* Something was selected */
1082         l_item = GTK_OBJECT(sl->data);
1083         flp    = (GList *) OBJECT_GET_DATA(l_item, E_FILT_LIST_ITEM_MODEL_KEY);
1084 #else
1085     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1086         gtk_tree_model_get(model, &iter, 1, &flp, -1);
1087 #endif
1088         if (flp) {
1089             filt   = (filter_def *) flp->data;
1090             name   = g_strdup(filt->name);
1091             strval = g_strdup(filt->strval);
1092             sensitivity = TRUE;
1093         }
1094     }
1095
1096     /*
1097      * Did you know that this function is called when the window is destroyed?
1098      * Funny, that.
1099      * This means that we have to:
1100      *
1101      *  attach to the top-level window data items containing pointers to
1102      *  the widgets we affect here;
1103      *
1104      *  give each of those widgets their own destroy callbacks;
1105      *
1106      *  clear that pointer when the widget is destroyed;
1107      *
1108      *  don't do anything to the widget if the pointer we get back is
1109      *  null;
1110      *
1111      * so that if we're called after any of the widgets we'd affect are
1112      * destroyed, we know that we shouldn't do anything to those widgets.
1113      */
1114     if (name_te != NULL)
1115         gtk_entry_set_text(GTK_ENTRY(name_te), name ? name : "");
1116     if (filter_te != NULL)
1117         gtk_entry_set_text(GTK_ENTRY(filter_te), strval ? strval : "");
1118     if (chg_bt != NULL)
1119         gtk_widget_set_sensitive(chg_bt, sensitivity);
1120     if (copy_bt != NULL)
1121         gtk_widget_set_sensitive(copy_bt, sensitivity);
1122     if (del_bt != NULL)
1123         gtk_widget_set_sensitive(del_bt, sensitivity);
1124     if (name != NULL)
1125         g_free(name);
1126     if (strval != NULL)
1127         g_free(strval);
1128 }
1129
1130 /* To do: add input checking to each of these callbacks */
1131
1132 /* Structure containing arguments to be passed to "new_filter_cb()".
1133
1134    "active_filter_l" is the list in the dialog box in which "New" or
1135    "Copy" was clicked; in that dialog box, but not in any other dialog
1136    box, we select the newly created list item.
1137
1138    "nflp" is the GList member in the model (filter list) for the new
1139    filter. */
1140 typedef struct {
1141         GtkWidget *active_filter_l;
1142         GList     *nflp;
1143 } new_filter_cb_args_t;
1144
1145 static void
1146 new_filter_cb(gpointer data, gpointer user_data)
1147 {
1148     GtkWidget    *main_w = data;
1149 #if GTK_MAJOR_VERSION < 2
1150     GtkWidget    *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1151     GtkWidget    *nl_lb, *nl_item;
1152 #else
1153     GtkTreeView  *filter_l;
1154     GtkListStore *store;
1155     GtkTreeIter   iter;
1156 #endif
1157     new_filter_cb_args_t *args = user_data;
1158     filter_def *nfilt = args->nflp->data;
1159
1160 #if GTK_MAJOR_VERSION < 2
1161     nl_lb        = gtk_label_new(nfilt->name);
1162     nl_item      = gtk_list_item_new();
1163     gtk_misc_set_alignment(GTK_MISC(nl_lb), 0.0, 0.5);
1164     gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
1165     gtk_widget_show(nl_lb);
1166     gtk_container_add(GTK_CONTAINER(filter_l), nl_item);
1167     gtk_widget_show(nl_item);
1168     OBJECT_SET_DATA(nl_item, E_FILT_LBL_KEY, nl_lb);
1169     OBJECT_SET_DATA(GTK_OBJECT(nl_item), E_FILT_LIST_ITEM_MODEL_KEY,
1170                         args->nflp);
1171     if (filter_l == args->active_filter_l) {
1172         /* Select the item. */
1173         gtk_list_select_child(GTK_LIST(filter_l), nl_item);
1174     }
1175 #else
1176     filter_l = GTK_TREE_VIEW(OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY));
1177     store = GTK_LIST_STORE(gtk_tree_view_get_model(filter_l));
1178     gtk_list_store_append(store, &iter);
1179     gtk_list_store_set(store, &iter, 0, nfilt->name, 1, args->nflp, -1);
1180     if (GTK_WIDGET(filter_l) == args->active_filter_l) {
1181         /* Select the item. */
1182         gtk_tree_selection_select_iter(gtk_tree_view_get_selection(filter_l),
1183                                        &iter);
1184     }
1185 #endif
1186 }
1187
1188 static void
1189 filter_new_bt_clicked_cb(GtkWidget *w, gpointer data)
1190 {
1191   GtkWidget  *main_w = gtk_widget_get_toplevel(w);
1192   GtkWidget  *name_te = OBJECT_GET_DATA(main_w, E_FILT_NAME_TE_KEY);
1193   GtkWidget  *filter_te = OBJECT_GET_DATA(main_w, E_FILT_FILTER_TE_KEY);
1194   GtkWidget  *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1195   filter_list_type_t list_type = *(filter_list_type_t *)data;
1196   GList      *fl_entry;
1197   const gchar *name, *strval;
1198   new_filter_cb_args_t args;
1199
1200   name   = gtk_entry_get_text(GTK_ENTRY(name_te));
1201   strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
1202
1203   /* if the user didn't entered a name, set default one */
1204   if (strlen(name) == 0) {
1205     name = "new";
1206   }
1207
1208   /* if the user didn't entered a string value, set default one */
1209   if (strlen(strval) == 0) {
1210     strval = "new";
1211   }
1212
1213     /* Add a new entry to the filter list. */
1214     fl_entry = add_to_filter_list(list_type, name, strval);
1215
1216     /* Update all the filter list widgets, not just the one in
1217        the dialog box in which we clicked on "Copy". */
1218     args.active_filter_l = filter_l;
1219     args.nflp = fl_entry;
1220     g_list_foreach(get_filter_dialog_list(list_type), new_filter_cb, &args);
1221
1222 }
1223
1224 #if GTK_MAJOR_VERSION < 2
1225 static void
1226 chg_list_item_cb(GtkWidget *nl_item, gpointer data)
1227 #else
1228 static gboolean
1229 chg_list_item_cb(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
1230                  gpointer data)
1231 #endif
1232 {
1233     GList      *flp = data;
1234     filter_def *filt = flp->data;
1235 #if GTK_MAJOR_VERSION < 2
1236     GtkLabel   *nl_lb =
1237         GTK_LABEL(OBJECT_GET_DATA(nl_item, E_FILT_LBL_KEY));
1238     GList      *nl_model =
1239         OBJECT_GET_DATA(nl_item, E_FILT_LIST_ITEM_MODEL_KEY);
1240 #else
1241     GList      *nl_model;
1242 #endif
1243
1244 #if GTK_MAJOR_VERSION >= 2
1245     gtk_tree_model_get(model, iter, 1, &nl_model, -1);
1246 #endif
1247     /* Is this the item corresponding to the filter list item in question? */
1248     if (flp == nl_model) {
1249         /* Yes - change the label to correspond to the new name for the
1250          * filter. */
1251 #if GTK_MAJOR_VERSION < 2
1252         gtk_label_set(nl_lb, filt->name);
1253 #else
1254         gtk_list_store_set(GTK_LIST_STORE(model), iter, 0, filt->name, -1);
1255         return TRUE;
1256 #endif
1257     }
1258 #if GTK_MAJOR_VERSION >= 2
1259     return FALSE;
1260 #endif
1261 }
1262
1263 static void
1264 chg_filter_cb(gpointer data, gpointer user_data)
1265 {
1266     GtkWidget  *main_w = data;
1267     GtkWidget  *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1268
1269 #if GTK_MAJOR_VERSION < 2
1270     gtk_container_foreach(GTK_CONTAINER(filter_l), chg_list_item_cb, user_data);
1271 #else
1272     gtk_tree_model_foreach(gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l)),
1273                            chg_list_item_cb, user_data);
1274 #endif
1275 }
1276
1277 static void
1278 filter_name_te_changed_cb(GtkWidget *w, gpointer data)
1279 {
1280     GtkWidget  *main_w = gtk_widget_get_toplevel(w);
1281     GtkWidget  *name_te = OBJECT_GET_DATA(main_w, E_FILT_NAME_TE_KEY);
1282     GtkWidget  *filter_te = OBJECT_GET_DATA(main_w, E_FILT_FILTER_TE_KEY);
1283     GtkWidget  *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1284     filter_def *filt;
1285     GList      *fl_entry;
1286     filter_list_type_t  list_type = *(filter_list_type_t *)data;
1287     const gchar         *name = "";
1288     const gchar         *strval = "";
1289
1290 #if GTK_MAJOR_VERSION < 2
1291     GList      *sl;
1292     GtkObject  *l_item;
1293     GtkLabel   *nl_lb;
1294 #else
1295     GtkTreeSelection  *sel;
1296     GtkTreeModel      *model;
1297     GtkTreeIter        iter;
1298 #endif
1299
1300 #if GTK_MAJOR_VERSION < 2
1301     sl     = GTK_LIST(filter_l)->selection;
1302 #else
1303     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
1304 #endif
1305     name   = gtk_entry_get_text(GTK_ENTRY(name_te));
1306     strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
1307
1308     if (DFILTER_EDITED_LIST == list_type) {
1309         /* colorize filter string entry */
1310         filter_te_syntax_check_cb(filter_te);
1311     }
1312
1313     /* if something was selected */
1314 #if GTK_MAJOR_VERSION < 2
1315     if (sl) {
1316         l_item = GTK_OBJECT(sl->data);
1317         fl_entry = (GList *) OBJECT_GET_DATA(l_item,
1318                                                  E_FILT_LIST_ITEM_MODEL_KEY);
1319         nl_lb = (GtkLabel *) OBJECT_GET_DATA(l_item, E_FILT_LBL_KEY);
1320         if (fl_entry != NULL && nl_lb != NULL) {
1321 #else
1322     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1323         gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
1324         if (fl_entry != NULL) {
1325 #endif
1326             filt = (filter_def *) fl_entry->data;
1327
1328             if (strlen(name) > 0 && strlen(strval) > 0 && filt) {
1329                 g_free(filt->name);
1330                 g_free(filt->strval);
1331                 filt->name   = g_strdup(name);
1332                 filt->strval = g_strdup(strval);
1333
1334                 /* Update all the filter list widgets, not just the one in
1335                    the dialog box in which we clicked on "Copy". */
1336                 g_list_foreach(get_filter_dialog_list(list_type), chg_filter_cb,
1337                                fl_entry);
1338             }
1339         }
1340     }
1341 }
1342
1343 static void
1344 delete_filter_cb(gpointer data, gpointer user_data)
1345 {
1346     GtkWidget    *main_w = data;
1347     GtkWidget    *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1348 #if GTK_MAJOR_VERSION < 2
1349     gint          pos = *(gint *)user_data;
1350 #else
1351     gchar        *pos = (gchar *)user_data;
1352     GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l));
1353     GtkTreeIter   iter;
1354 #endif
1355
1356 #if GTK_MAJOR_VERSION < 2
1357     gtk_list_clear_items(GTK_LIST(filter_l), pos, pos + 1);
1358 #else
1359     gtk_tree_model_get_iter_from_string(model, &iter, pos);
1360     gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
1361 #endif
1362 }
1363
1364 static void
1365 filter_del_bt_clicked_cb(GtkWidget *w, gpointer data)
1366 {
1367     GtkWidget  *main_w = gtk_widget_get_toplevel(w);
1368     GtkWidget  *filter_l = OBJECT_GET_DATA(main_w, E_FILT_FILTER_L_KEY);
1369     filter_list_type_t list_type = *(filter_list_type_t *)data;
1370     GList      *fl_entry;
1371 #if GTK_MAJOR_VERSION < 2
1372     GList      *sl;
1373     GtkObject  *l_item;
1374     gint        pos;
1375 #else
1376     gchar             *pos;
1377     GtkTreeSelection  *sel;
1378     GtkTreeModel      *model;
1379     GtkTreeIter        iter;
1380     GtkTreePath       *path;
1381 #endif
1382
1383 #if GTK_MAJOR_VERSION < 2
1384     sl = GTK_LIST(filter_l)->selection;
1385     if (sl) {  /* Something was selected */
1386         l_item = GTK_OBJECT(sl->data);
1387         pos    = gtk_list_child_position(GTK_LIST(filter_l),
1388                                          GTK_WIDGET(l_item));
1389         fl_entry = (GList *) OBJECT_GET_DATA(l_item, E_FILT_LIST_ITEM_MODEL_KEY);
1390 #else
1391     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
1392     /* If something was selected */
1393     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1394         gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
1395         path = gtk_tree_model_get_path(model, &iter);
1396         pos = gtk_tree_path_to_string(path);
1397         gtk_tree_path_free(path);
1398 #endif
1399         if (fl_entry != NULL) {
1400             /* Remove the entry from the filter list. */
1401             remove_from_filter_list(list_type, fl_entry);
1402
1403             /* Update all the filter list widgets, not just the one in
1404                the dialog box in which we clicked on "Delete". */
1405 #if GTK_MAJOR_VERSION < 2
1406             g_list_foreach(get_filter_dialog_list(list_type), delete_filter_cb,
1407                            &pos);
1408 #else
1409             g_list_foreach(get_filter_dialog_list(list_type), delete_filter_cb, pos);
1410 #endif
1411         }
1412 #if GTK_MAJOR_VERSION >= 2
1413         g_free(pos);
1414 #endif
1415     }
1416 }
1417
1418 void
1419 filter_add_expr_bt_cb(GtkWidget *w _U_, gpointer main_w_arg)
1420 {
1421         GtkWidget  *main_w = GTK_WIDGET(main_w_arg);
1422         GtkWidget  *filter_te;
1423
1424         filter_te = OBJECT_GET_DATA(main_w, E_FILT_FILTER_TE_KEY);
1425         dfilter_expr_dlg_new(filter_te);
1426 }
1427
1428 static void
1429 color_filter_te(GtkWidget *w, guint16 red, guint16 green, guint16 blue)
1430 {
1431     GdkColor    bg;
1432     GtkStyle    *style;
1433
1434     bg.pixel    = 0;
1435     bg.red      = red;
1436     bg.green    = green;
1437     bg.blue     = blue;
1438
1439     style = gtk_style_copy(gtk_widget_get_style(w));
1440     style->base[GTK_STATE_NORMAL] = bg;
1441     gtk_widget_set_style(w, style);
1442     gtk_style_unref(style);
1443 }
1444
1445 void
1446 colorize_filter_te_as_empty(GtkWidget *w)
1447 {
1448     /* white */
1449     color_filter_te(w, 0xFFFF, 0xFFFF, 0xFFFF);
1450 }
1451
1452 void
1453 colorize_filter_te_as_invalid(GtkWidget *w)
1454 {
1455     /* light red */
1456     color_filter_te(w, 0xFFFF, 0xAFFF, 0xAFFF);
1457 }
1458
1459 void
1460 colorize_filter_te_as_valid(GtkWidget *w)
1461 {
1462     /* light green */
1463     color_filter_te(w, 0xAFFF, 0xFFFF, 0xAFFF);
1464 }
1465
1466 void
1467 filter_te_syntax_check_cb(GtkWidget *w)
1468 {
1469     const gchar *strval;
1470     dfilter_t   *dfp;
1471
1472     strval = gtk_entry_get_text(GTK_ENTRY(w));
1473
1474     /* colorize filter string entry */
1475     if (strval && dfilter_compile(strval, &dfp)) {
1476         if (dfp != NULL)
1477           dfilter_free(dfp);
1478         if (strlen(strval) == 0)
1479             colorize_filter_te_as_empty(w);
1480         else
1481             colorize_filter_te_as_valid(w);
1482     } else
1483         colorize_filter_te_as_invalid(w);
1484 }
1485
1486