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"
557 " (green is valid, red is invalid, yellow may have unexpected results).",
560 /* Create the "Add Expression..." button, to pop up a dialog
561 for constructing filter comparison expressions. */
562 add_expression_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_ADD_EXPRESSION);
563 g_signal_connect(add_expression_bt, "clicked", G_CALLBACK(filter_add_expr_bt_cb), main_w);
564 gtk_box_pack_start(GTK_BOX(bottom_hb), add_expression_bt, FALSE, FALSE, 0);
565 gtk_widget_show(add_expression_bt);
566 gtk_tooltips_set_tip (tooltips, add_expression_bt, ("Add an expression to the filter string"), NULL);
570 /* button row (create all possible buttons and hide the unrequired later - it's a lot easier) */
571 bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_SAVE, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
572 gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
573 gtk_widget_show(bbox);
575 ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
576 g_signal_connect(ok_bt, "clicked", G_CALLBACK(filter_dlg_ok_cb), filter_list_type_p);
577 gtk_tooltips_set_tip (tooltips, ok_bt, ("Apply the filters and close this dialog"), NULL);
579 /* Catch the "activate" signal on the filter name and filter
580 expression text entries, so that if the user types Return
581 there, we act as if the "OK" button had been selected, as
582 happens if Return is typed if some widget that *doesn't*
583 handle the Return key has the input focus. */
584 if (parent_filter_te != NULL) {
585 dlg_set_activate(name_te, ok_bt);
586 dlg_set_activate(filter_te, ok_bt);
589 apply_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_APPLY);
590 g_signal_connect(apply_bt, "clicked", G_CALLBACK(filter_dlg_apply_cb), filter_list_type_p);
591 gtk_tooltips_set_tip (tooltips, apply_bt, ("Apply the filters and keep this dialog open"), NULL);
593 save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
594 g_signal_connect(save_bt, "clicked", G_CALLBACK(filter_dlg_save_cb), filter_list_type_p);
595 gtk_tooltips_set_tip (tooltips, save_bt, ("Save the filters permanently and keep this dialog open"), NULL);
597 cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
598 gtk_tooltips_set_tip (tooltips, cancel_bt, ("Cancel the changes"), NULL);
599 g_signal_connect(cancel_bt, "clicked", G_CALLBACK(filter_dlg_cancel_cb), filter_list_type_p);
600 window_set_cancel_button(main_w, cancel_bt, NULL);
602 help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
603 if (list_type == CFILTER_EDITED_LIST) {
604 g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_CAPTURE_FILTERS_DIALOG);
606 g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_DISPLAY_FILTERS_DIALOG);
608 gtk_tooltips_set_tip (tooltips, help_bt, ("Show topic specific help"), NULL);
611 gtk_widget_grab_default(ok_bt);
614 remember_filter_dialog(main_w, filter_dialogs);
616 if (button != NULL) {
617 /* This dialog box was created by a "Filter" button.
618 Set the E_FILT_BUTTON_PTR_KEY for the new dialog to point to
620 g_object_set_data(G_OBJECT(main_w), E_FILT_BUTTON_PTR_KEY, button);
622 /* Set the E_FILT_DIALOG_PTR_KEY for the button to point to us */
623 g_object_set_data(G_OBJECT(button), E_FILT_DIALOG_PTR_KEY, main_w);
626 /* DO SELECTION THINGS *AFTER* SHOWING THE DIALOG! */
627 /* otherwise the updatings can get confused */
629 gtk_tree_selection_select_iter(sel, l_select);
631 } else if (filter_te_str && filter_te_str[0]) {
632 gtk_entry_set_text(GTK_ENTRY(name_te), "New filter");
633 gtk_entry_set_text(GTK_ENTRY(filter_te), filter_te_str);
636 g_signal_connect(main_w, "delete_event", G_CALLBACK(filter_dlg_delete_event_cb), filter_list_type_p);
637 g_signal_connect(main_w, "destroy", G_CALLBACK(filter_dlg_destroy_cb), filter_list_type_p);
639 gtk_widget_show(main_w);
641 if(construct_args->modal_and_transient) {
642 parent = gtk_widget_get_parent_window(parent_filter_te);
643 gdk_window_set_transient_for(main_w->window, parent);
644 gtk_window_set_modal(GTK_WINDOW(main_w), TRUE);
647 /* hide the Ok button, if we don't have to apply it and our caller wants a Save button */
648 if (parent_filter_te == NULL && prefs.gui_use_pref_save) {
649 gtk_widget_hide(ok_bt);
652 /* hide the Apply button, if our caller don't wants one */
653 if (!construct_args->wants_apply_button) {
654 gtk_widget_hide(apply_bt);
657 /* hide the Save button if the user uses implicit save */
658 if (!prefs.gui_use_pref_save) {
659 gtk_widget_hide(save_bt);
662 window_present(main_w);
668 filter_dlg_dclick(GtkWidget *filter_l, gpointer main_w_arg, gpointer activate)
670 GtkWidget *main_w = GTK_WIDGET(main_w_arg);
671 GtkWidget *parent_filter_te =
672 g_object_get_data(G_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY);
675 GtkTreeSelection *sel;
679 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
681 if (parent_filter_te != NULL) {
683 * We have a text entry widget associated with this dialog
684 * box; is one of the filters in the list selected?
686 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
688 * Yes. Is there a filter definition for that filter?
690 gtk_tree_model_get(model, &iter, 1, &flp, -1);
693 * Yes - put it in the text entry widget.
695 filt = (filter_def *) flp->data;
696 gtk_entry_set_text(GTK_ENTRY(parent_filter_te),
700 * Are we supposed to cause the filter we
701 * put there to be applied?
703 if (activate != NULL) {
707 g_signal_emit_by_name(G_OBJECT(parent_filter_te), "activate", NULL);
713 window_destroy(main_w);
717 filter_dlg_ok_cb(GtkWidget *ok_bt, gpointer data)
719 filter_list_type_t list_type = *(filter_list_type_t *)data;
722 * Destroy the dialog box and apply the filter.
724 filter_apply(gtk_widget_get_toplevel(ok_bt), TRUE);
726 /* if we don't have a Save button, just save the settings now */
727 if (!prefs.gui_use_pref_save) {
728 filter_dlg_save(list_type);
733 filter_dlg_apply_cb(GtkWidget *apply_bt, gpointer data)
735 filter_list_type_t list_type = *(filter_list_type_t *)data;
738 * Apply the filter, but don't destroy the dialog box.
740 filter_apply(gtk_widget_get_toplevel(apply_bt), FALSE);
742 /* if we don't have a Save button, just save the settings now */
743 if (!prefs.gui_use_pref_save) {
744 filter_dlg_save(list_type);
749 filter_apply(GtkWidget *main_w, gboolean destroy)
751 construct_args_t *construct_args =
752 g_object_get_data(G_OBJECT(main_w), E_FILT_CONSTRUCT_ARGS_KEY);
753 GtkWidget *parent_filter_te =
754 g_object_get_data(G_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY);
755 GtkWidget *filter_te;
756 const gchar *filter_string;
758 if (parent_filter_te != NULL) {
760 * We have a text entry widget associated with this dialog
761 * box; put the filter in our text entry widget into that
764 filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
766 (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
767 gtk_entry_set_text(GTK_ENTRY(parent_filter_te), filter_string);
773 * Destroy the filter dialog box.
775 window_destroy(main_w);
778 if (parent_filter_te != NULL) {
780 * We have a text entry widget associated with this dialog
781 * box; activate that widget to cause the filter we put
782 * there to be applied if we're supposed to do so.
784 * We do this after dismissing the filter dialog box,
785 * as activating the widget the dialog box to which
786 * it belongs to be dismissed, and that may cause it
787 * to destroy our dialog box if the filter succeeds.
788 * This means that our subsequent attempt to destroy
791 * We don't know whether it'll destroy our dialog box,
792 * so we can't rely on it to do so. Instead, we
793 * destroy it ourselves, which will clear the
794 * E_FILT_DIALOG_PTR_KEY pointer for their dialog box,
795 * meaning they won't think it has one and won't try
798 if (construct_args->activate_on_ok) {
799 g_signal_emit_by_name(G_OBJECT(parent_filter_te), "activate", NULL);
806 filter_dlg_save(filter_list_type_t list_type)
811 const char *filter_type;
815 case CFILTER_EDITED_LIST:
816 filter_type = "capture";
817 list_type = CFILTER_LIST;
818 copy_filter_list(CFILTER_LIST, CFILTER_EDITED_LIST);
821 case DFILTER_EDITED_LIST:
822 filter_type = "display";
823 list_type = DFILTER_LIST;
824 copy_filter_list(DFILTER_LIST, DFILTER_EDITED_LIST);
828 g_assert_not_reached();
833 /* Create the directory that holds personal configuration files,
835 if (create_persconffile_dir(&pf_dir_path) == -1) {
836 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
837 "Can't create directory\n\"%s\"\nfor filter files: %s.",
838 pf_dir_path, strerror(errno));
843 save_filter_list(list_type, &f_path, &f_save_errno);
844 if (f_path != NULL) {
845 /* We had an error saving the filter. */
846 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
847 "Could not save to your %s filter file\n\"%s\": %s.",
848 filter_type, f_path, strerror(f_save_errno));
855 filter_dlg_save_cb(GtkWidget *save_bt _U_, gpointer data)
857 filter_list_type_t list_type = *(filter_list_type_t *)data;
859 filter_dlg_save(list_type);
863 /* update a remaining dialog if another one was cancelled */
864 static void filter_dlg_update_list_cb(gpointer data, gpointer user_data)
866 GtkWidget *main_w = data;
867 filter_list_type_t list_type = *(filter_list_type_t *)user_data;
869 /* refill the list */
871 fill_list(main_w, list_type, NULL);
875 /* cancel button pressed, revert changes and exit dialog */
877 filter_dlg_cancel_cb(GtkWidget *cancel_bt, gpointer data)
879 filter_list_type_t list_type = *(filter_list_type_t *)data;
880 GtkWidget *main_w = gtk_widget_get_toplevel(cancel_bt);
881 static GList *filter_list;
884 window_destroy(GTK_WIDGET(main_w));
886 /* if this was the last open filter dialog, revert the changes made */
887 filter_list = get_filter_dialog_list(list_type);
888 if(g_list_length(filter_list) == 0) {
889 /* revert changes in the edited list */
891 case CFILTER_EDITED_LIST:
892 copy_filter_list(CFILTER_EDITED_LIST, CFILTER_LIST);
894 case DFILTER_EDITED_LIST:
895 copy_filter_list(DFILTER_EDITED_LIST, DFILTER_LIST);
898 g_assert_not_reached();
904 /* update other open filter dialogs */
905 g_list_foreach(get_filter_dialog_list(list_type), filter_dlg_update_list_cb, &list_type);
909 /* Treat this as a cancel, by calling "filter_dlg_cancel_cb()" */
911 filter_dlg_delete_event_cb(GtkWidget *main_w, GdkEvent *event _U_,
914 filter_dlg_cancel_cb(main_w, data);
920 filter_dlg_destroy_cb(GtkWidget *win, gpointer data)
922 filter_list_type_t list_type = *(filter_list_type_t *)data;
925 /* Get the button that requested that we be popped up, if any.
926 (It should arrange to destroy us if it's destroyed, so
927 that we don't get a pointer to a non-existent window here.) */
928 button = g_object_get_data(G_OBJECT(win), E_FILT_BUTTON_PTR_KEY);
930 if (button != NULL) {
931 /* Tell it we no longer exist. */
932 g_object_set_data(G_OBJECT(button), E_FILT_DIALOG_PTR_KEY, NULL);
934 /* This is an editing dialog popped up from, for example,
935 a menu item; note that we no longer have one. */
939 case CFILTER_EDITED_LIST:
940 g_assert(win == global_cfilter_w);
941 global_cfilter_w = NULL;
945 g_assert_not_reached();
950 /* Remove this from the list of filter dialog windows. */
951 forget_filter_dialog(win, list_type);
955 filter_sel_list_button_cb(GtkWidget *list, GdkEventButton *event,
958 void (* func)(GtkWidget *, gpointer, gpointer);
960 gpointer func_activate;
962 if (event->type == GDK_2BUTTON_PRESS) {
963 func = g_object_get_data(G_OBJECT(list), E_FILT_DBLFUNC_KEY);
964 func_arg = g_object_get_data(G_OBJECT(list), E_FILT_DBLARG_KEY);
965 func_activate = g_object_get_data(G_OBJECT(list), E_FILT_DBLACTIVATE_KEY);
968 (*func)(list, func_arg, func_activate);
975 filter_sel_list_cb(GtkTreeSelection *sel, gpointer data _U_)
977 GtkWidget *filter_l = GTK_WIDGET(gtk_tree_selection_get_tree_view(sel));
978 GtkWidget *main_w = gtk_widget_get_toplevel(filter_l);
981 GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY);
982 GtkWidget *filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
983 GtkWidget *chg_bt = g_object_get_data(G_OBJECT(main_w), E_FILT_CHG_BT_KEY);
984 GtkWidget *copy_bt = g_object_get_data(G_OBJECT(main_w), E_FILT_COPY_BT_KEY);
985 GtkWidget *del_bt = g_object_get_data(G_OBJECT(main_w), E_FILT_DEL_BT_KEY);
987 gchar *name = NULL, *strval = NULL;
989 gint sensitivity = FALSE;
991 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
992 gtk_tree_model_get(model, &iter, 1, &flp, -1);
994 filt = (filter_def *) flp->data;
995 name = g_strdup(filt->name);
996 strval = g_strdup(filt->strval);
1002 * Did you know that this function is called when the window is destroyed?
1004 * This means that we have to:
1006 * attach to the top-level window data items containing pointers to
1007 * the widgets we affect here;
1009 * give each of those widgets their own destroy callbacks;
1011 * clear that pointer when the widget is destroyed;
1013 * don't do anything to the widget if the pointer we get back is
1016 * so that if we're called after any of the widgets we'd affect are
1017 * destroyed, we know that we shouldn't do anything to those widgets.
1019 if (name_te != NULL)
1020 gtk_entry_set_text(GTK_ENTRY(name_te), name ? name : "");
1021 if (filter_te != NULL)
1022 gtk_entry_set_text(GTK_ENTRY(filter_te), strval ? strval : "");
1024 gtk_widget_set_sensitive(chg_bt, sensitivity);
1025 if (copy_bt != NULL)
1026 gtk_widget_set_sensitive(copy_bt, sensitivity);
1028 gtk_widget_set_sensitive(del_bt, sensitivity);
1033 /* To do: add input checking to each of these callbacks */
1035 /* Structure containing arguments to be passed to "new_filter_cb()".
1037 "active_filter_l" is the list in the dialog box in which "New" or
1038 "Copy" was clicked; in that dialog box, but not in any other dialog
1039 box, we select the newly created list item.
1041 "nflp" is the GList member in the model (filter list) for the new
1044 GtkWidget *active_filter_l;
1046 } new_filter_cb_args_t;
1049 new_filter_cb(gpointer data, gpointer user_data)
1051 GtkWidget *main_w = data;
1052 GtkTreeView *filter_l;
1053 GtkListStore *store;
1055 new_filter_cb_args_t *args = user_data;
1056 filter_def *nfilt = args->nflp->data;
1058 filter_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY));
1059 store = GTK_LIST_STORE(gtk_tree_view_get_model(filter_l));
1060 gtk_list_store_append(store, &iter);
1061 gtk_list_store_set(store, &iter, 0, nfilt->name, 1, args->nflp, -1);
1062 if (GTK_WIDGET(filter_l) == args->active_filter_l) {
1063 /* Select the item. */
1064 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(filter_l),
1070 filter_new_bt_clicked_cb(GtkWidget *w, gpointer data)
1072 GtkWidget *main_w = gtk_widget_get_toplevel(w);
1073 GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY);
1074 GtkWidget *filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
1075 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1076 filter_list_type_t list_type = *(filter_list_type_t *)data;
1078 const gchar *name, *strval;
1079 new_filter_cb_args_t args;
1081 name = gtk_entry_get_text(GTK_ENTRY(name_te));
1082 strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
1084 /* if the user didn't entered a name, set default one */
1085 if (strlen(name) == 0) {
1089 /* if the user didn't entered a string value, set default one */
1090 if (strlen(strval) == 0) {
1094 /* Add a new entry to the filter list. */
1095 fl_entry = add_to_filter_list(list_type, name, strval);
1097 /* Update all the filter list widgets, not just the one in
1098 the dialog box in which we clicked on "Copy". */
1099 args.active_filter_l = filter_l;
1100 args.nflp = fl_entry;
1101 g_list_foreach(get_filter_dialog_list(list_type), new_filter_cb, &args);
1106 chg_list_item_cb(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
1110 filter_def *filt = flp->data;
1113 gtk_tree_model_get(model, iter, 1, &nl_model, -1);
1114 /* Is this the item corresponding to the filter list item in question? */
1115 if (flp == nl_model) {
1116 /* Yes - change the label to correspond to the new name for the
1118 gtk_list_store_set(GTK_LIST_STORE(model), iter, 0, filt->name, -1);
1125 chg_filter_cb(gpointer data, gpointer user_data)
1127 GtkWidget *main_w = data;
1128 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1130 gtk_tree_model_foreach(gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l)),
1131 chg_list_item_cb, user_data);
1135 filter_name_te_changed_cb(GtkWidget *w, gpointer data)
1137 GtkWidget *main_w = gtk_widget_get_toplevel(w);
1138 GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY);
1139 GtkWidget *filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
1140 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1143 filter_list_type_t list_type = *(filter_list_type_t *)data;
1144 const gchar *name = "";
1145 const gchar *strval = "";
1147 GtkTreeSelection *sel;
1148 GtkTreeModel *model;
1151 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
1152 name = gtk_entry_get_text(GTK_ENTRY(name_te));
1153 strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
1155 if (DFILTER_EDITED_LIST == list_type) {
1156 /* colorize filter string entry */
1157 filter_te_syntax_check_cb(filter_te);
1160 /* if something was selected */
1161 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1162 gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
1163 if (fl_entry != NULL) {
1164 filt = (filter_def *) fl_entry->data;
1166 if (strlen(name) > 0 && strlen(strval) > 0 && filt) {
1168 g_free(filt->strval);
1169 filt->name = g_strdup(name);
1170 filt->strval = g_strdup(strval);
1172 /* Update all the filter list widgets, not just the one in
1173 the dialog box in which we clicked on "Copy". */
1174 g_list_foreach(get_filter_dialog_list(list_type), chg_filter_cb,
1182 delete_filter_cb(gpointer data, gpointer user_data)
1184 GtkWidget *main_w = data;
1185 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1186 gchar *pos = (gchar *)user_data;
1187 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l));
1190 gtk_tree_model_get_iter_from_string(model, &iter, pos);
1191 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
1195 filter_del_bt_clicked_cb(GtkWidget *w, gpointer data)
1197 GtkWidget *main_w = gtk_widget_get_toplevel(w);
1198 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1199 filter_list_type_t list_type = *(filter_list_type_t *)data;
1202 GtkTreeSelection *sel;
1203 GtkTreeModel *model;
1207 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
1208 /* If something was selected */
1209 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1210 gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
1211 path = gtk_tree_model_get_path(model, &iter);
1212 pos = gtk_tree_path_to_string(path);
1213 gtk_tree_path_free(path);
1214 if (fl_entry != NULL) {
1215 /* Remove the entry from the filter list. */
1216 remove_from_filter_list(list_type, fl_entry);
1218 /* Update all the filter list widgets, not just the one in
1219 the dialog box in which we clicked on "Delete". */
1220 g_list_foreach(get_filter_dialog_list(list_type), delete_filter_cb, pos);
1227 filter_add_expr_bt_cb(GtkWidget *w _U_, gpointer main_w_arg)
1229 GtkWidget *main_w = GTK_WIDGET(main_w_arg);
1230 GtkWidget *filter_te, *dfilter_w;
1232 filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
1233 dfilter_w = dfilter_expr_dlg_new(filter_te);
1235 /* If we're opening a series of modal dialogs (such as when going
1236 * through file->open, make the latest dialog modal also so that it
1237 * takes over "control" from the other modal dialogs. Also set
1238 * the transient property of the new dialog so the user doesn't try
1239 * to interact with the previous window when they can't.
1240 * XXX: containing widget might be the Filter Toolbar */
1242 if ( GTK_IS_WINDOW(main_w) && gtk_window_get_modal(GTK_WINDOW(main_w))) {
1243 gtk_window_set_modal(GTK_WINDOW(dfilter_w), TRUE);
1244 gtk_window_set_transient_for(GTK_WINDOW(dfilter_w),
1245 GTK_WINDOW(main_w));
1250 color_filter_te(GtkWidget *w, guint16 red, guint16 green, guint16 blue)
1252 static GdkColor black = { 0, 0, 0, 0 };
1260 gtk_widget_modify_text(w, GTK_STATE_NORMAL, &black);
1261 gtk_widget_modify_base(w, GTK_STATE_NORMAL, &bg);
1262 #if GTK_CHECK_VERSION(2,12,0)
1263 gtk_widget_modify_cursor(w, &black, &black);
1268 colorize_filter_te_as_empty(GtkWidget *w)
1271 gtk_widget_modify_text(w, GTK_STATE_NORMAL, NULL);
1272 gtk_widget_modify_base(w, GTK_STATE_NORMAL, NULL);
1273 #if GTK_CHECK_VERSION(2,12,0)
1274 gtk_widget_modify_cursor(w, NULL, NULL);
1279 colorize_filter_te_as_invalid(GtkWidget *w)
1282 color_filter_te(w, 0xFFFF, 0xAFFF, 0xAFFF);
1286 colorize_filter_te_as_deprecated(GtkWidget *w)
1289 color_filter_te(w, 0xFFFF, 0xFFFF, 0xAFFF);
1293 colorize_filter_te_as_valid(GtkWidget *w)
1296 color_filter_te(w, 0xAFFF, 0xFFFF, 0xAFFF);
1300 filter_te_syntax_check_cb(GtkWidget *w)
1302 const gchar *strval;
1304 GPtrArray *depr = NULL;
1305 gboolean use_statusbar;
1309 strval = gtk_entry_get_text(GTK_ENTRY(w));
1310 use_statusbar = g_object_get_data(G_OBJECT(w), E_FILT_FIELD_USE_STATUSBAR_KEY) ? TRUE : FALSE;
1312 if (use_statusbar) {
1313 statusbar_pop_filter_msg();
1316 /* colorize filter string entry */
1317 if (g_object_get_data(G_OBJECT(w), E_FILT_FIELD_NAME_ONLY_KEY) &&
1318 strval && (c = proto_check_field_name(strval)) != 0)
1320 colorize_filter_te_as_invalid(w);
1321 if (use_statusbar) {
1322 msg = g_strdup_printf(" Illegal character in field name: '%c'", c);
1323 statusbar_push_filter_msg(msg);
1326 } else if (strval && dfilter_compile(strval, &dfp)) {
1328 depr = dfilter_deprecated_tokens(dfp);
1330 if (strlen(strval) == 0) {
1331 colorize_filter_te_as_empty(w);
1333 /* You keep using that word. I do not think it means what you think it means. */
1334 colorize_filter_te_as_deprecated(w);
1335 if (use_statusbar) {
1337 * We're being lazy and only printing the first "problem" token.
1338 * Would it be better to print all of them?
1340 msg = g_strdup_printf(" \"%s\" may have unexpected results (see the User's Guide)",
1341 (const char *) g_ptr_array_index(depr, 0));
1342 statusbar_push_temporary_msg(msg);
1346 colorize_filter_te_as_valid(w);
1350 colorize_filter_te_as_invalid(w);
1351 if (use_statusbar) {
1352 if (dfilter_error_msg) {
1353 msg = g_strdup_printf(" Invalid filter: %s", dfilter_error_msg);
1354 statusbar_push_filter_msg(msg);
1357 statusbar_push_filter_msg(" Invalid filter");
1369 * indent-tabs-mode: nil
1372 * ex: set shiftwidth=4 tabstop=8 expandtab
1373 * :indentSize=4:tabSize=8:noTabs=true: