2 * Dialog boxes for (display and capture) filter editing
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
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.
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.
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.
33 #include <epan/filesystem.h>
34 #include <epan/prefs.h>
35 #include <epan/proto.h>
37 #include "../filters.h"
38 #include "../simple_dialog.h"
41 #include "gtk/main_statusbar.h"
42 #include "gtk/filter_dlg.h"
43 #include "gtk/dlg_utils.h"
44 #include "gtk/gui_utils.h"
45 #include "gtk/dfilter_expr_dlg.h"
46 #include "gtk/stock_icons.h"
47 #include "gtk/gtkglobals.h"
48 #include "gtk/help_dlg.h"
49 #include "gtk/filter_autocomplete.h"
51 #define E_FILT_DIALOG_PTR_KEY "filter_dialog_ptr"
52 #define E_FILT_BUTTON_PTR_KEY "filter_button_ptr"
53 #define E_FILT_PARENT_FILTER_TE_KEY "filter_parent_filter_te"
54 #define E_FILT_CONSTRUCT_ARGS_KEY "filter_construct_args"
55 #define E_FILT_LIST_ITEM_MODEL_KEY "filter_list_item_model"
56 #define E_FILT_LBL_KEY "filter_label"
57 #define E_FILT_FILTER_L_KEY "filter_filter_l"
58 #define E_FILT_CHG_BT_KEY "filter_chg_bt"
59 #define E_FILT_COPY_BT_KEY "filter_copy_bt"
60 #define E_FILT_DEL_BT_KEY "filter_del_bt"
61 #define E_FILT_NAME_TE_KEY "filter_name_te"
62 #define E_FILT_DBLFUNC_KEY "filter_dblfunc"
63 #define E_FILT_DBLARG_KEY "filter_dblarg"
64 #define E_FILT_DBLACTIVATE_KEY "filter_dblactivate"
66 typedef struct _filter_cb_data {
71 static GtkWidget *filter_dialog_new(GtkWidget *button, GtkWidget *filter_te,
72 filter_list_type_t list_type,
73 construct_args_t *construct_args);
74 static void filter_dlg_dclick(GtkWidget *dummy, gpointer main_w_arg,
76 static void filter_dlg_ok_cb(GtkWidget *ok_bt, gpointer data);
77 static void filter_dlg_apply_cb(GtkWidget *apply_bt, gpointer data);
78 static void filter_apply(GtkWidget *main_w, gboolean destroy);
79 static void filter_dlg_save(filter_list_type_t list_type);
80 static void filter_dlg_save_cb(GtkWidget *save_bt, gpointer parent_w);
81 static void filter_dlg_destroy_cb(GtkWidget *win, gpointer data);
84 filter_dlg_delete_event_cb(GtkWidget *prefs_w, GdkEvent *event, gpointer data);
86 filter_dlg_cancel_cb(GtkWidget *cancel_bt, gpointer data);
88 static gint filter_sel_list_button_cb(GtkWidget *, GdkEventButton *,
90 static void filter_sel_list_cb(GtkTreeSelection *, gpointer);
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);
96 /* Create a filter dialog for constructing a capture filter.
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. */
106 capture_filter_construct_cb(GtkWidget *w, gpointer user_data _U_)
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",
119 /* Has a filter dialog box already been opened for that button? */
120 filter_browse_w = g_object_get_data(G_OBJECT(w), E_FILT_DIALOG_PTR_KEY);
122 if (filter_browse_w != NULL) {
123 /* Yes. Just re-activate that dialog box. */
124 reactivate_window(filter_browse_w);
128 /* No. Get the text entry attached to the button. */
129 parent_filter_te = g_object_get_data(G_OBJECT(w), E_FILT_TE_PTR_KEY);
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);
137 /* Create a filter dialog for constructing a display filter.
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
147 If "wants_apply_button" is non-null, we add an "Apply" button that
148 acts like "OK" but doesn't dismiss this dialog. */
150 display_filter_construct_cb(GtkWidget *w, gpointer construct_args_ptr)
152 construct_args_t *construct_args = construct_args_ptr;
153 GtkWidget *filter_browse_w;
154 GtkWidget *parent_filter_te;
156 /* Has a filter dialog box already been opened for the button? */
157 filter_browse_w = g_object_get_data(G_OBJECT(w), E_FILT_DIALOG_PTR_KEY);
159 if (filter_browse_w != NULL) {
160 /* Yes. Just re-activate that dialog box. */
161 reactivate_window(filter_browse_w);
165 /* No. Get the text entry attached to the button. */
166 parent_filter_te = g_object_get_data(G_OBJECT(w), E_FILT_TE_PTR_KEY);
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);
174 /* Should be called when a button that creates filters is destroyed; it
175 destroys any filter dialog created by that button. */
177 filter_button_destroy_cb(GtkWidget *button, gpointer user_data _U_)
181 /* Is there a filter edit/selection dialog associated with this
183 filter_w = g_object_get_data(G_OBJECT(button), E_FILT_DIALOG_PTR_KEY);
185 if (filter_w != NULL) {
186 /* Yes. Break the association, and destroy the dialog. */
187 g_object_set_data(G_OBJECT(button), E_FILT_DIALOG_PTR_KEY, NULL);
188 window_destroy(filter_w);
193 static GtkWidget *global_cfilter_w;
195 /* Create a filter dialog for editing capture filters; this is to be used
196 as a callback for menu items, toolbars, etc.. */
198 cfilter_dialog_cb(GtkWidget *w _U_)
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",
209 /* Has a filter dialog box already been opened for editing
211 if (global_cfilter_w != NULL) {
212 /* Yes. Just reactivate it. */
213 reactivate_window(global_cfilter_w);
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.
222 global_cfilter_w = filter_dialog_new(NULL, NULL, CFILTER_LIST, &args);
226 /* Create a filter dialog for editing display filters; this is to be used
227 as a callback for menu items, toolbars, etc.. */
229 dfilter_dialog_cb(GtkWidget *w _U_)
231 static construct_args_t args = {
232 "Wireshark: Display Filter",
238 display_filter_construct_cb(g_object_get_data(G_OBJECT(top_level), E_FILT_BT_PTR_KEY), &args);
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;
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;
252 remember_filter_dialog(GtkWidget *main_w, GList **filter_dialogs)
254 *filter_dialogs = g_list_append(*filter_dialogs, main_w);
257 /* Remove a filter dialog from the specified list of filter_dialogs. */
259 forget_filter_dialog(GtkWidget *main_w, filter_list_type_t list_type)
263 case CFILTER_EDITED_LIST:
264 cfilter_dialogs = g_list_remove(cfilter_dialogs, main_w);
267 case DFILTER_EDITED_LIST:
268 dfilter_dialogs = g_list_remove(dfilter_dialogs, main_w);
272 g_assert_not_reached();
277 /* Get the dialog list corresponding to a particular filter list. */
279 get_filter_dialog_list(filter_list_type_t list_type)
283 case CFILTER_EDITED_LIST:
284 return cfilter_dialogs;
286 case DFILTER_EDITED_LIST:
287 return dfilter_dialogs;
290 g_assert_not_reached();
297 fill_list(GtkWidget *main_w, filter_list_type_t list_type, const gchar *filter_te_str)
301 GtkTreeView *filter_l;
304 GtkTreeIter *l_select = NULL;
306 filter_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY));
307 store = GTK_LIST_STORE(gtk_tree_view_get_model(filter_l));
310 fl_entry = get_filter_list_first(list_type);
311 while (fl_entry != NULL) {
312 filt = (filter_def *) fl_entry->data;
313 gtk_list_store_append(store, &iter);
314 gtk_list_store_set(store, &iter, 0, filt->name,
317 if (filter_te_str && filt->strval) {
318 if (strcmp(filter_te_str, filt->strval) == 0) {
320 * XXX - We're assuming that we can just copy a GtkTreeIter
321 * and use it later without any crashes. This may not be a
324 l_select = g_memdup(&iter, sizeof(iter));
328 fl_entry = fl_entry->next;
335 clear_list(GtkWidget *main_w) {
336 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
337 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l));
339 gtk_list_store_clear(GTK_LIST_STORE(model));
344 filter_dialog_new(GtkWidget *button, GtkWidget *parent_filter_te,
345 filter_list_type_t list_type, construct_args_t *construct_args)
347 GtkWidget *main_w, /* main window */
348 *main_vb, /* main container */
349 *bbox, /* button container */
350 *ok_bt, /* "OK" button */
351 *apply_bt, /* "Apply" button */
352 *save_bt, /* "Save" button */
353 *cancel_bt, /* "Cancel" button */
354 *help_bt; /* "Help" button */
355 GtkWidget *filter_vb, /* filter settings box */
374 GtkTooltips *tooltips;
375 static filter_list_type_t cfilter_list_type = CFILTER_EDITED_LIST;
376 static filter_list_type_t dfilter_list_type = DFILTER_EDITED_LIST;
377 filter_list_type_t *filter_list_type_p;
378 GList **filter_dialogs;
379 const gchar *filter_te_str = NULL;
381 GtkCellRenderer *renderer;
382 GtkTreeViewColumn *column;
383 GtkTreeSelection *sel;
384 GtkTreeIter *l_select;
385 gchar *list_name = NULL;
387 /* Get a pointer to a static variable holding the type of filter on
388 which we're working, so we can pass that pointer to callback
393 filter_dialogs = &cfilter_dialogs;
394 filter_list_type_p = &cfilter_list_type;
395 list_type = CFILTER_EDITED_LIST;
396 list_name = "Capture Filter";
400 filter_dialogs = &dfilter_dialogs;
401 filter_list_type_p = &dfilter_list_type;
402 list_type = DFILTER_EDITED_LIST;
403 list_name = "Display Filter";
407 g_assert_not_reached();
408 filter_dialogs = NULL;
409 filter_list_type_p = NULL;
413 tooltips = gtk_tooltips_new ();
415 main_w = dlg_conf_window_new(construct_args->title);
416 gtk_window_set_default_size(GTK_WINDOW(main_w), 400, 400);
417 g_object_set_data(G_OBJECT(main_w), E_FILT_CONSTRUCT_ARGS_KEY, construct_args);
419 main_vb = gtk_vbox_new(FALSE, 0);
420 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
421 gtk_container_add(GTK_CONTAINER(main_w), main_vb);
422 gtk_widget_show(main_vb);
424 /* Make sure everything is set up */
425 if (parent_filter_te)
426 filter_te_str = gtk_entry_get_text(GTK_ENTRY(parent_filter_te));
428 /* Container for each row of widgets */
429 filter_vb = gtk_vbox_new(FALSE, 0);
430 gtk_container_set_border_width(GTK_CONTAINER(filter_vb), 0);
431 gtk_container_add(GTK_CONTAINER(main_vb), filter_vb);
432 gtk_widget_show(filter_vb);
434 /* Top row: Buttons and filter list */
435 top_hb = gtk_hbox_new(FALSE, 0);
436 gtk_container_add(GTK_CONTAINER(filter_vb), top_hb);
437 gtk_widget_show(top_hb);
439 edit_fr = gtk_frame_new("Edit");
440 gtk_box_pack_start(GTK_BOX(top_hb), edit_fr, FALSE, FALSE, 0);
441 gtk_widget_show(edit_fr);
443 list_bb = gtk_vbox_new(TRUE, 0);
444 gtk_container_set_border_width(GTK_CONTAINER(list_bb), 5);
445 gtk_container_add(GTK_CONTAINER(edit_fr), list_bb);
446 gtk_widget_show(list_bb);
448 new_bt = gtk_button_new_from_stock(GTK_STOCK_NEW);
449 g_signal_connect(new_bt, "clicked", G_CALLBACK(filter_new_bt_clicked_cb), filter_list_type_p);
450 gtk_widget_show(new_bt);
451 gtk_box_pack_start (GTK_BOX (list_bb), new_bt, FALSE, FALSE, 0);
452 gtk_tooltips_set_tip (tooltips, new_bt,
453 "Create a new filter at the end of the list (with the current properties)", NULL);
455 del_bt = gtk_button_new_from_stock(GTK_STOCK_DELETE);
456 gtk_widget_set_sensitive(del_bt, FALSE);
457 g_signal_connect(del_bt, "clicked", G_CALLBACK(filter_del_bt_clicked_cb), filter_list_type_p);
458 g_object_set_data(G_OBJECT(main_w), E_FILT_DEL_BT_KEY, del_bt);
459 gtk_widget_show(del_bt);
460 gtk_box_pack_start (GTK_BOX (list_bb), del_bt, FALSE, FALSE, 0);
461 gtk_tooltips_set_tip (tooltips, del_bt, ("Delete the selected filter"), NULL);
463 filter_fr = gtk_frame_new(list_name);
464 gtk_box_pack_start(GTK_BOX(top_hb), filter_fr, TRUE, TRUE, 0);
465 gtk_widget_show(filter_fr);
467 filter_sc = scrolled_window_new(NULL, NULL);
468 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(filter_sc),
471 gtk_container_set_border_width (GTK_CONTAINER (filter_sc), 5);
472 gtk_container_add(GTK_CONTAINER(filter_fr), filter_sc);
473 gtk_widget_show(filter_sc);
475 store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
476 filter_l = tree_view_new(GTK_TREE_MODEL(store));
477 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(filter_l), FALSE);
478 renderer = gtk_cell_renderer_text_new();
479 column = gtk_tree_view_column_new_with_attributes("", renderer, "text",
481 gtk_tree_view_column_set_sort_column_id(column, 0);
482 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_l), column);
483 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
484 gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
485 g_signal_connect(sel, "changed", G_CALLBACK(filter_sel_list_cb), filter_vb);
486 g_signal_connect(filter_l, "button_press_event", G_CALLBACK(filter_sel_list_button_cb),
488 g_object_set_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY, filter_l);
489 gtk_container_add(GTK_CONTAINER(filter_sc), filter_l);
490 gtk_widget_show(filter_l);
492 g_object_set_data(G_OBJECT(filter_l), E_FILT_DBLFUNC_KEY, filter_dlg_dclick);
493 g_object_set_data(G_OBJECT(filter_l), E_FILT_DBLARG_KEY, main_w);
494 /* This is a Boolean, but we make it a non-null pointer for TRUE
495 and a null pointer for FALSE, as object data is a pointer. */
496 g_object_set_data(G_OBJECT(filter_l), E_FILT_DBLACTIVATE_KEY,
497 construct_args->activate_on_ok ? "" : NULL);
500 l_select = fill_list(main_w, list_type, filter_te_str);
502 g_object_unref(G_OBJECT(store));
505 props_fr = gtk_frame_new("Properties");
506 gtk_box_pack_start(GTK_BOX(filter_vb), props_fr, FALSE, FALSE, 0);
507 gtk_widget_show(props_fr);
509 props_vb = gtk_vbox_new(FALSE, 3);
510 gtk_container_set_border_width(GTK_CONTAINER(props_vb), 5);
511 gtk_container_add(GTK_CONTAINER(props_fr), props_vb);
512 gtk_widget_show(props_vb);
514 /* row: Filter name entry */
515 middle_hb = gtk_hbox_new(FALSE, 3);
516 gtk_container_add(GTK_CONTAINER(props_vb), middle_hb);
517 gtk_widget_show(middle_hb);
519 name_lb = gtk_label_new("Filter name:");
520 gtk_box_pack_start(GTK_BOX(middle_hb), name_lb, FALSE, FALSE, 0);
521 gtk_widget_show(name_lb);
523 name_te = gtk_entry_new();
524 gtk_box_pack_start(GTK_BOX(middle_hb), name_te, TRUE, TRUE, 0);
525 g_object_set_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY, name_te);
526 g_signal_connect(name_te, "changed", G_CALLBACK(filter_name_te_changed_cb), filter_list_type_p);
527 gtk_widget_show(name_te);
529 /* row: Filter text entry */
530 bottom_hb = gtk_hbox_new(FALSE, 3);
531 gtk_container_add(GTK_CONTAINER(props_vb), bottom_hb);
532 gtk_widget_show(bottom_hb);
534 filter_lb = gtk_label_new("Filter string:");
535 gtk_box_pack_start(GTK_BOX(bottom_hb), filter_lb, FALSE, FALSE, 0);
536 gtk_widget_show(filter_lb);
538 filter_te = gtk_entry_new();
539 gtk_box_pack_start(GTK_BOX(bottom_hb), filter_te, TRUE, TRUE, 0);
540 g_object_set_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY, filter_te);
541 g_signal_connect(filter_te, "changed", G_CALLBACK(filter_name_te_changed_cb), filter_list_type_p);
542 if (list_type == DFILTER_EDITED_LIST) {
543 colorize_filter_te_as_empty(filter_te);
545 g_object_set_data(G_OBJECT(main_w), E_FILT_AUTOCOMP_PTR_KEY, NULL);
546 g_signal_connect(filter_te, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
547 g_signal_connect(main_w, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
549 gtk_widget_show(filter_te);
551 g_object_set_data(G_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY, parent_filter_te);
553 if (list_type == DFILTER_EDITED_LIST) {
554 gtk_tooltips_set_tip(tooltips, filter_te,
555 "Enter a display filter. "
556 "The background color of this field is changed by a continuous syntax check (green is valid, red is invalid, yellow may have unexpected results).",
559 /* Create the "Add Expression..." button, to pop up a dialog
560 for constructing filter comparison expressions. */
561 add_expression_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_ADD_EXPRESSION);
562 g_signal_connect(add_expression_bt, "clicked", G_CALLBACK(filter_add_expr_bt_cb), main_w);
563 gtk_box_pack_start(GTK_BOX(bottom_hb), add_expression_bt, FALSE, FALSE, 0);
564 gtk_widget_show(add_expression_bt);
565 gtk_tooltips_set_tip (tooltips, add_expression_bt, ("Add an expression to the filter string"), NULL);
569 /* button row (create all possible buttons and hide the unrequired later - it's a lot easier) */
570 bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_SAVE, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
571 gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
572 gtk_widget_show(bbox);
574 ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
575 g_signal_connect(ok_bt, "clicked", G_CALLBACK(filter_dlg_ok_cb), filter_list_type_p);
576 gtk_tooltips_set_tip (tooltips, ok_bt, ("Apply the filters and close this dialog"), NULL);
578 /* Catch the "activate" signal on the filter name and filter
579 expression text entries, so that if the user types Return
580 there, we act as if the "OK" button had been selected, as
581 happens if Return is typed if some widget that *doesn't*
582 handle the Return key has the input focus. */
583 if (parent_filter_te != NULL) {
584 dlg_set_activate(name_te, ok_bt);
585 dlg_set_activate(filter_te, ok_bt);
588 apply_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_APPLY);
589 g_signal_connect(apply_bt, "clicked", G_CALLBACK(filter_dlg_apply_cb), filter_list_type_p);
590 gtk_tooltips_set_tip (tooltips, apply_bt, ("Apply the filters and keep this dialog open"), NULL);
592 save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
593 g_signal_connect(save_bt, "clicked", G_CALLBACK(filter_dlg_save_cb), filter_list_type_p);
594 gtk_tooltips_set_tip (tooltips, save_bt, ("Save the filters permanently and keep this dialog open"), NULL);
596 cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
597 gtk_tooltips_set_tip (tooltips, cancel_bt, ("Cancel the changes"), NULL);
598 g_signal_connect(cancel_bt, "clicked", G_CALLBACK(filter_dlg_cancel_cb), filter_list_type_p);
599 window_set_cancel_button(main_w, cancel_bt, NULL);
601 help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
602 if (list_type == CFILTER_EDITED_LIST) {
603 g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_CAPTURE_FILTERS_DIALOG);
605 g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_DISPLAY_FILTERS_DIALOG);
607 gtk_tooltips_set_tip (tooltips, help_bt, ("Show topic specific help"), NULL);
610 gtk_widget_grab_default(ok_bt);
613 remember_filter_dialog(main_w, filter_dialogs);
615 if (button != NULL) {
616 /* This dialog box was created by a "Filter" button.
617 Set the E_FILT_BUTTON_PTR_KEY for the new dialog to point to
619 g_object_set_data(G_OBJECT(main_w), E_FILT_BUTTON_PTR_KEY, button);
621 /* Set the E_FILT_DIALOG_PTR_KEY for the button to point to us */
622 g_object_set_data(G_OBJECT(button), E_FILT_DIALOG_PTR_KEY, main_w);
625 /* DO SELECTION THINGS *AFTER* SHOWING THE DIALOG! */
626 /* otherwise the updatings can get confused */
628 gtk_tree_selection_select_iter(sel, l_select);
630 } else if (filter_te_str && filter_te_str[0]) {
631 gtk_entry_set_text(GTK_ENTRY(name_te), "New filter");
632 gtk_entry_set_text(GTK_ENTRY(filter_te), filter_te_str);
635 g_signal_connect(main_w, "delete_event", G_CALLBACK(filter_dlg_delete_event_cb), filter_list_type_p);
636 g_signal_connect(main_w, "destroy", G_CALLBACK(filter_dlg_destroy_cb), filter_list_type_p);
638 gtk_widget_show(main_w);
640 if(construct_args->modal_and_transient) {
641 parent = gtk_widget_get_parent_window(parent_filter_te);
642 gdk_window_set_transient_for(main_w->window, parent);
643 gtk_window_set_modal(GTK_WINDOW(main_w), TRUE);
646 /* hide the Ok button, if we don't have to apply it and our caller wants a Save button */
647 if (parent_filter_te == NULL && prefs.gui_use_pref_save) {
648 gtk_widget_hide(ok_bt);
651 /* hide the Apply button, if our caller don't wants one */
652 if (!construct_args->wants_apply_button) {
653 gtk_widget_hide(apply_bt);
656 /* hide the Save button if the user uses implicit save */
657 if (!prefs.gui_use_pref_save) {
658 gtk_widget_hide(save_bt);
661 window_present(main_w);
667 filter_dlg_dclick(GtkWidget *filter_l, gpointer main_w_arg, gpointer activate)
669 GtkWidget *main_w = GTK_WIDGET(main_w_arg);
670 GtkWidget *parent_filter_te =
671 g_object_get_data(G_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY);
674 GtkTreeSelection *sel;
678 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
680 if (parent_filter_te != NULL) {
682 * We have a text entry widget associated with this dialog
683 * box; is one of the filters in the list selected?
685 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
687 * Yes. Is there a filter definition for that filter?
689 gtk_tree_model_get(model, &iter, 1, &flp, -1);
692 * Yes - put it in the text entry widget.
694 filt = (filter_def *) flp->data;
695 gtk_entry_set_text(GTK_ENTRY(parent_filter_te),
699 * Are we supposed to cause the filter we
700 * put there to be applied?
702 if (activate != NULL) {
706 g_signal_emit_by_name(G_OBJECT(parent_filter_te), "activate", NULL);
712 window_destroy(main_w);
716 filter_dlg_ok_cb(GtkWidget *ok_bt, gpointer data)
718 filter_list_type_t list_type = *(filter_list_type_t *)data;
721 * Destroy the dialog box and apply the filter.
723 filter_apply(gtk_widget_get_toplevel(ok_bt), TRUE);
725 /* if we don't have a Save button, just save the settings now */
726 if (!prefs.gui_use_pref_save) {
727 filter_dlg_save(list_type);
732 filter_dlg_apply_cb(GtkWidget *apply_bt, gpointer data)
734 filter_list_type_t list_type = *(filter_list_type_t *)data;
737 * Apply the filter, but don't destroy the dialog box.
739 filter_apply(gtk_widget_get_toplevel(apply_bt), FALSE);
741 /* if we don't have a Save button, just save the settings now */
742 if (!prefs.gui_use_pref_save) {
743 filter_dlg_save(list_type);
748 filter_apply(GtkWidget *main_w, gboolean destroy)
750 construct_args_t *construct_args =
751 g_object_get_data(G_OBJECT(main_w), E_FILT_CONSTRUCT_ARGS_KEY);
752 GtkWidget *parent_filter_te =
753 g_object_get_data(G_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY);
754 GtkWidget *filter_te;
755 const gchar *filter_string;
757 if (parent_filter_te != NULL) {
759 * We have a text entry widget associated with this dialog
760 * box; put the filter in our text entry widget into that
763 filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
765 (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
766 gtk_entry_set_text(GTK_ENTRY(parent_filter_te), filter_string);
772 * Destroy the filter dialog box.
774 window_destroy(main_w);
777 if (parent_filter_te != NULL) {
779 * We have a text entry widget associated with this dialog
780 * box; activate that widget to cause the filter we put
781 * there to be applied if we're supposed to do so.
783 * We do this after dismissing the filter dialog box,
784 * as activating the widget the dialog box to which
785 * it belongs to be dismissed, and that may cause it
786 * to destroy our dialog box if the filter succeeds.
787 * This means that our subsequent attempt to destroy
790 * We don't know whether it'll destroy our dialog box,
791 * so we can't rely on it to do so. Instead, we
792 * destroy it ourselves, which will clear the
793 * E_FILT_DIALOG_PTR_KEY pointer for their dialog box,
794 * meaning they won't think it has one and won't try
797 if (construct_args->activate_on_ok) {
798 g_signal_emit_by_name(G_OBJECT(parent_filter_te), "activate", NULL);
805 filter_dlg_save(filter_list_type_t list_type)
810 const char *filter_type;
814 case CFILTER_EDITED_LIST:
815 filter_type = "capture";
816 list_type = CFILTER_LIST;
817 copy_filter_list(CFILTER_LIST, CFILTER_EDITED_LIST);
820 case DFILTER_EDITED_LIST:
821 filter_type = "display";
822 list_type = DFILTER_LIST;
823 copy_filter_list(DFILTER_LIST, DFILTER_EDITED_LIST);
827 g_assert_not_reached();
832 /* Create the directory that holds personal configuration files,
834 if (create_persconffile_dir(&pf_dir_path) == -1) {
835 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
836 "Can't create directory\n\"%s\"\nfor filter files: %s.",
837 pf_dir_path, strerror(errno));
842 save_filter_list(list_type, &f_path, &f_save_errno);
843 if (f_path != NULL) {
844 /* We had an error saving the filter. */
845 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
846 "Could not save to your %s filter file\n\"%s\": %s.",
847 filter_type, f_path, strerror(f_save_errno));
854 filter_dlg_save_cb(GtkWidget *save_bt _U_, gpointer data)
856 filter_list_type_t list_type = *(filter_list_type_t *)data;
858 filter_dlg_save(list_type);
862 /* update a remaining dialog if another one was cancelled */
863 static void filter_dlg_update_list_cb(gpointer data, gpointer user_data)
865 GtkWidget *main_w = data;
866 filter_list_type_t list_type = *(filter_list_type_t *)user_data;
868 /* refill the list */
870 fill_list(main_w, list_type, NULL);
874 /* cancel button pressed, revert changes and exit dialog */
876 filter_dlg_cancel_cb(GtkWidget *cancel_bt, gpointer data)
878 filter_list_type_t list_type = *(filter_list_type_t *)data;
879 GtkWidget *main_w = gtk_widget_get_toplevel(cancel_bt);
880 static GList *filter_list;
883 window_destroy(GTK_WIDGET(main_w));
885 /* if this was the last open filter dialog, revert the changes made */
886 filter_list = get_filter_dialog_list(list_type);
887 if(g_list_length(filter_list) == 0) {
888 /* revert changes in the edited list */
890 case CFILTER_EDITED_LIST:
891 copy_filter_list(CFILTER_EDITED_LIST, CFILTER_LIST);
893 case DFILTER_EDITED_LIST:
894 copy_filter_list(DFILTER_EDITED_LIST, DFILTER_LIST);
897 g_assert_not_reached();
903 /* update other open filter dialogs */
904 g_list_foreach(get_filter_dialog_list(list_type), filter_dlg_update_list_cb, &list_type);
908 /* Treat this as a cancel, by calling "filter_dlg_cancel_cb()" */
910 filter_dlg_delete_event_cb(GtkWidget *main_w, GdkEvent *event _U_,
913 filter_dlg_cancel_cb(main_w, data);
919 filter_dlg_destroy_cb(GtkWidget *win, gpointer data)
921 filter_list_type_t list_type = *(filter_list_type_t *)data;
924 /* Get the button that requested that we be popped up, if any.
925 (It should arrange to destroy us if it's destroyed, so
926 that we don't get a pointer to a non-existent window here.) */
927 button = g_object_get_data(G_OBJECT(win), E_FILT_BUTTON_PTR_KEY);
929 if (button != NULL) {
930 /* Tell it we no longer exist. */
931 g_object_set_data(G_OBJECT(button), E_FILT_DIALOG_PTR_KEY, NULL);
933 /* This is an editing dialog popped up from, for example,
934 a menu item; note that we no longer have one. */
938 case CFILTER_EDITED_LIST:
939 g_assert(win == global_cfilter_w);
940 global_cfilter_w = NULL;
944 g_assert_not_reached();
949 /* Remove this from the list of filter dialog windows. */
950 forget_filter_dialog(win, list_type);
954 filter_sel_list_button_cb(GtkWidget *list, GdkEventButton *event,
957 void (* func)(GtkWidget *, gpointer, gpointer);
959 gpointer func_activate;
961 if (event->type == GDK_2BUTTON_PRESS) {
962 func = g_object_get_data(G_OBJECT(list), E_FILT_DBLFUNC_KEY);
963 func_arg = g_object_get_data(G_OBJECT(list), E_FILT_DBLARG_KEY);
964 func_activate = g_object_get_data(G_OBJECT(list), E_FILT_DBLACTIVATE_KEY);
967 (*func)(list, func_arg, func_activate);
974 filter_sel_list_cb(GtkTreeSelection *sel, gpointer data _U_)
976 GtkWidget *filter_l = GTK_WIDGET(gtk_tree_selection_get_tree_view(sel));
977 GtkWidget *main_w = gtk_widget_get_toplevel(filter_l);
980 GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY);
981 GtkWidget *filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
982 GtkWidget *chg_bt = g_object_get_data(G_OBJECT(main_w), E_FILT_CHG_BT_KEY);
983 GtkWidget *copy_bt = g_object_get_data(G_OBJECT(main_w), E_FILT_COPY_BT_KEY);
984 GtkWidget *del_bt = g_object_get_data(G_OBJECT(main_w), E_FILT_DEL_BT_KEY);
986 gchar *name = NULL, *strval = NULL;
988 gint sensitivity = FALSE;
990 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
991 gtk_tree_model_get(model, &iter, 1, &flp, -1);
993 filt = (filter_def *) flp->data;
994 name = g_strdup(filt->name);
995 strval = g_strdup(filt->strval);
1001 * Did you know that this function is called when the window is destroyed?
1003 * This means that we have to:
1005 * attach to the top-level window data items containing pointers to
1006 * the widgets we affect here;
1008 * give each of those widgets their own destroy callbacks;
1010 * clear that pointer when the widget is destroyed;
1012 * don't do anything to the widget if the pointer we get back is
1015 * so that if we're called after any of the widgets we'd affect are
1016 * destroyed, we know that we shouldn't do anything to those widgets.
1018 if (name_te != NULL)
1019 gtk_entry_set_text(GTK_ENTRY(name_te), name ? name : "");
1020 if (filter_te != NULL)
1021 gtk_entry_set_text(GTK_ENTRY(filter_te), strval ? strval : "");
1023 gtk_widget_set_sensitive(chg_bt, sensitivity);
1024 if (copy_bt != NULL)
1025 gtk_widget_set_sensitive(copy_bt, sensitivity);
1027 gtk_widget_set_sensitive(del_bt, sensitivity);
1034 /* To do: add input checking to each of these callbacks */
1036 /* Structure containing arguments to be passed to "new_filter_cb()".
1038 "active_filter_l" is the list in the dialog box in which "New" or
1039 "Copy" was clicked; in that dialog box, but not in any other dialog
1040 box, we select the newly created list item.
1042 "nflp" is the GList member in the model (filter list) for the new
1045 GtkWidget *active_filter_l;
1047 } new_filter_cb_args_t;
1050 new_filter_cb(gpointer data, gpointer user_data)
1052 GtkWidget *main_w = data;
1053 GtkTreeView *filter_l;
1054 GtkListStore *store;
1056 new_filter_cb_args_t *args = user_data;
1057 filter_def *nfilt = args->nflp->data;
1059 filter_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY));
1060 store = GTK_LIST_STORE(gtk_tree_view_get_model(filter_l));
1061 gtk_list_store_append(store, &iter);
1062 gtk_list_store_set(store, &iter, 0, nfilt->name, 1, args->nflp, -1);
1063 if (GTK_WIDGET(filter_l) == args->active_filter_l) {
1064 /* Select the item. */
1065 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(filter_l),
1071 filter_new_bt_clicked_cb(GtkWidget *w, gpointer data)
1073 GtkWidget *main_w = gtk_widget_get_toplevel(w);
1074 GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY);
1075 GtkWidget *filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
1076 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1077 filter_list_type_t list_type = *(filter_list_type_t *)data;
1079 const gchar *name, *strval;
1080 new_filter_cb_args_t args;
1082 name = gtk_entry_get_text(GTK_ENTRY(name_te));
1083 strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
1085 /* if the user didn't entered a name, set default one */
1086 if (strlen(name) == 0) {
1090 /* if the user didn't entered a string value, set default one */
1091 if (strlen(strval) == 0) {
1095 /* Add a new entry to the filter list. */
1096 fl_entry = add_to_filter_list(list_type, name, strval);
1098 /* Update all the filter list widgets, not just the one in
1099 the dialog box in which we clicked on "Copy". */
1100 args.active_filter_l = filter_l;
1101 args.nflp = fl_entry;
1102 g_list_foreach(get_filter_dialog_list(list_type), new_filter_cb, &args);
1107 chg_list_item_cb(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
1111 filter_def *filt = flp->data;
1114 gtk_tree_model_get(model, iter, 1, &nl_model, -1);
1115 /* Is this the item corresponding to the filter list item in question? */
1116 if (flp == nl_model) {
1117 /* Yes - change the label to correspond to the new name for the
1119 gtk_list_store_set(GTK_LIST_STORE(model), iter, 0, filt->name, -1);
1126 chg_filter_cb(gpointer data, gpointer user_data)
1128 GtkWidget *main_w = data;
1129 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1131 gtk_tree_model_foreach(gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l)),
1132 chg_list_item_cb, user_data);
1136 filter_name_te_changed_cb(GtkWidget *w, gpointer data)
1138 GtkWidget *main_w = gtk_widget_get_toplevel(w);
1139 GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY);
1140 GtkWidget *filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
1141 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1144 filter_list_type_t list_type = *(filter_list_type_t *)data;
1145 const gchar *name = "";
1146 const gchar *strval = "";
1148 GtkTreeSelection *sel;
1149 GtkTreeModel *model;
1152 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
1153 name = gtk_entry_get_text(GTK_ENTRY(name_te));
1154 strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
1156 if (DFILTER_EDITED_LIST == list_type) {
1157 /* colorize filter string entry */
1158 filter_te_syntax_check_cb(filter_te);
1161 /* if something was selected */
1162 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1163 gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
1164 if (fl_entry != NULL) {
1165 filt = (filter_def *) fl_entry->data;
1167 if (strlen(name) > 0 && strlen(strval) > 0 && filt) {
1169 g_free(filt->strval);
1170 filt->name = g_strdup(name);
1171 filt->strval = g_strdup(strval);
1173 /* Update all the filter list widgets, not just the one in
1174 the dialog box in which we clicked on "Copy". */
1175 g_list_foreach(get_filter_dialog_list(list_type), chg_filter_cb,
1183 delete_filter_cb(gpointer data, gpointer user_data)
1185 GtkWidget *main_w = data;
1186 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1187 gchar *pos = (gchar *)user_data;
1188 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l));
1191 gtk_tree_model_get_iter_from_string(model, &iter, pos);
1192 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
1196 filter_del_bt_clicked_cb(GtkWidget *w, gpointer data)
1198 GtkWidget *main_w = gtk_widget_get_toplevel(w);
1199 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1200 filter_list_type_t list_type = *(filter_list_type_t *)data;
1203 GtkTreeSelection *sel;
1204 GtkTreeModel *model;
1208 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
1209 /* If something was selected */
1210 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1211 gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
1212 path = gtk_tree_model_get_path(model, &iter);
1213 pos = gtk_tree_path_to_string(path);
1214 gtk_tree_path_free(path);
1215 if (fl_entry != NULL) {
1216 /* Remove the entry from the filter list. */
1217 remove_from_filter_list(list_type, fl_entry);
1219 /* Update all the filter list widgets, not just the one in
1220 the dialog box in which we clicked on "Delete". */
1221 g_list_foreach(get_filter_dialog_list(list_type), delete_filter_cb, pos);
1228 filter_add_expr_bt_cb(GtkWidget *w _U_, gpointer main_w_arg)
1230 GtkWidget *main_w = GTK_WIDGET(main_w_arg);
1231 GtkWidget *filter_te, *dfilter_w;
1233 filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
1234 dfilter_w = dfilter_expr_dlg_new(filter_te);
1236 /* If we're opening a series of modal dialogs (such as when going
1237 * through file->open, make the latest dialog modal also so that it
1238 * takes over "control" from the other modal dialogs. Also set
1239 * the transient property of the new dialog so the user doesn't try
1240 * to interact with the previous window when they can't.
1241 * XXX: containing widget might be the Filter Toolbar */
1243 if ( GTK_IS_WINDOW(main_w) && gtk_window_get_modal(GTK_WINDOW(main_w))) {
1244 gtk_window_set_modal(GTK_WINDOW(dfilter_w), TRUE);
1245 gtk_window_set_transient_for(GTK_WINDOW(dfilter_w),
1246 GTK_WINDOW(main_w));
1251 color_filter_te(GtkWidget *w, guint16 red, guint16 green, guint16 blue)
1253 static GdkColor black = { 0, 0, 0, 0 };
1261 gtk_widget_modify_text(w, GTK_STATE_NORMAL, &black);
1262 gtk_widget_modify_base(w, GTK_STATE_NORMAL, &bg);
1263 #if GTK_CHECK_VERSION(2,12,0)
1264 gtk_widget_modify_cursor(w, &black, &black);
1269 colorize_filter_te_as_empty(GtkWidget *w)
1272 gtk_widget_modify_text(w, GTK_STATE_NORMAL, NULL);
1273 gtk_widget_modify_base(w, GTK_STATE_NORMAL, NULL);
1274 #if GTK_CHECK_VERSION(2,12,0)
1275 gtk_widget_modify_cursor(w, NULL, NULL);
1280 colorize_filter_te_as_invalid(GtkWidget *w)
1283 color_filter_te(w, 0xFFFF, 0xAFFF, 0xAFFF);
1287 colorize_filter_te_as_deprecated(GtkWidget *w)
1290 color_filter_te(w, 0xFFFF, 0xFFFF, 0xAFFF);
1294 colorize_filter_te_as_valid(GtkWidget *w)
1297 color_filter_te(w, 0xAFFF, 0xFFFF, 0xAFFF);
1301 filter_te_syntax_check_cb(GtkWidget *w)
1303 const gchar *strval;
1305 GPtrArray *depr = NULL;
1306 gboolean use_statusbar;
1310 strval = gtk_entry_get_text(GTK_ENTRY(w));
1311 use_statusbar = g_object_get_data(G_OBJECT(w), E_FILT_FIELD_USE_STATUSBAR_KEY) ? TRUE : FALSE;
1313 if (use_statusbar) {
1314 statusbar_pop_filter_msg();
1317 /* colorize filter string entry */
1318 if (g_object_get_data(G_OBJECT(w), E_FILT_FIELD_NAME_ONLY_KEY) &&
1319 strval && (c = proto_check_field_name(strval)) != 0)
1321 colorize_filter_te_as_invalid(w);
1322 if (use_statusbar) {
1323 msg = g_strdup_printf(" Illegal character in field name: '%c'", c);
1324 statusbar_push_filter_msg(msg);
1327 } else if (strval && dfilter_compile(strval, &dfp)) {
1329 depr = dfilter_deprecated_tokens(dfp);
1332 if (strlen(strval) == 0) {
1333 colorize_filter_te_as_empty(w);
1335 /* You keep using that word. I do not think it means what you think it means. */
1336 colorize_filter_te_as_deprecated(w);
1337 if (use_statusbar) {
1339 * We're being lazy and only printing the first "problem" token.
1340 * Would it be better to print all of them?
1342 msg = g_strdup_printf(" \"%s\" may have unexpected results (see the User's Guide)",
1343 (const char *) g_ptr_array_index(depr, 0));
1344 statusbar_push_filter_msg(msg);
1348 colorize_filter_te_as_valid(w);
1351 colorize_filter_te_as_invalid(w);
1352 if (use_statusbar) {
1353 statusbar_push_filter_msg(" Invalid filter");