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