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_dialog_new(w, parent_filter_te, CFILTER_LIST, &args);
136 /* Create a filter dialog for constructing a display filter.
138 This is to be used as a callback for a button next to a text entry box,
139 which, when clicked, pops up this dialog to allow you to construct a
140 display filter by browsing the list of saved filters and/or by adding
141 test expressions constructed with another dialog. The "OK" button
142 sets the text entry box to the constructed filter and activates that
143 text entry box, causing the filter to be used; this dialog is then
146 If "wants_apply_button" is non-null, we add an "Apply" button that
147 acts like "OK" but doesn't dismiss this dialog. */
149 display_filter_construct_cb(GtkWidget *w, gpointer construct_args_ptr)
151 construct_args_t *construct_args = construct_args_ptr;
152 GtkWidget *filter_browse_w;
153 GtkWidget *parent_filter_te;
155 /* Has a filter dialog box already been opened for the button? */
156 filter_browse_w = g_object_get_data(G_OBJECT(w), E_FILT_DIALOG_PTR_KEY);
158 if (filter_browse_w != NULL) {
159 /* Yes. Just re-activate that dialog box. */
160 reactivate_window(filter_browse_w);
164 /* No. Get the text entry attached to the button. */
165 parent_filter_te = g_object_get_data(G_OBJECT(w), E_FILT_TE_PTR_KEY);
167 /* Now create a new dialog, possibly with an "Apply" button, and
168 definitely with an "Add Expression..." button. */
169 filter_dialog_new(w, parent_filter_te, DFILTER_LIST, construct_args);
172 /* Should be called when a button that creates filters is destroyed; it
173 destroys any filter dialog created by that button. */
175 filter_button_destroy_cb(GtkWidget *button, gpointer user_data _U_)
179 /* Is there a filter edit/selection dialog associated with this
181 filter_w = g_object_get_data(G_OBJECT(button), E_FILT_DIALOG_PTR_KEY);
183 if (filter_w != NULL) {
184 /* Yes. Break the association, and destroy the dialog. */
185 g_object_set_data(G_OBJECT(button), E_FILT_DIALOG_PTR_KEY, NULL);
186 window_destroy(filter_w);
191 static GtkWidget *global_cfilter_w;
193 /* Create a filter dialog for editing capture filters; this is to be used
194 as a callback for menu items, toolbars, etc.. */
196 cfilter_dialog_cb(GtkWidget *w _U_)
198 /* No Apply button, and there's no text widget to set, much less
199 activate, on "OK". */
200 static construct_args_t args = {
201 "Wireshark: Capture Filter",
207 /* Has a filter dialog box already been opened for editing
209 if (global_cfilter_w != NULL) {
210 /* Yes. Just reactivate it. */
211 reactivate_window(global_cfilter_w);
216 * No. Create one; we didn't pop this up as a result of pressing
217 * a button next to some text entry field, so don't associate it
218 * with a text entry field or button.
220 global_cfilter_w = filter_dialog_new(NULL, NULL, CFILTER_LIST, &args);
224 /* Create a filter dialog for editing display filters; this is to be used
225 as a callback for menu items, toolbars, etc.. */
227 dfilter_dialog_cb(GtkWidget *w _U_)
229 static construct_args_t args = {
230 "Wireshark: Display Filter",
236 display_filter_construct_cb(g_object_get_data(G_OBJECT(top_level), E_FILT_BT_PTR_KEY), &args);
239 /* List of capture filter dialogs, so that if the list of filters changes
240 (the model, if you will), we can update all of their lists displaying
241 the filters (the views). */
242 static GList *cfilter_dialogs;
244 /* List of display filter dialogs, so that if the list of filters changes
245 (the model, if you will), we can update all of their lists displaying
246 the filters (the views). */
247 static GList *dfilter_dialogs;
250 remember_filter_dialog(GtkWidget *main_w, GList **filter_dialogs)
252 *filter_dialogs = g_list_append(*filter_dialogs, main_w);
255 /* Remove a filter dialog from the specified list of filter_dialogs. */
257 forget_filter_dialog(GtkWidget *main_w, filter_list_type_t list_type)
261 case CFILTER_EDITED_LIST:
262 cfilter_dialogs = g_list_remove(cfilter_dialogs, main_w);
265 case DFILTER_EDITED_LIST:
266 dfilter_dialogs = g_list_remove(dfilter_dialogs, main_w);
270 g_assert_not_reached();
275 /* Get the dialog list corresponding to a particular filter list. */
277 get_filter_dialog_list(filter_list_type_t list_type)
281 case CFILTER_EDITED_LIST:
282 return cfilter_dialogs;
284 case DFILTER_EDITED_LIST:
285 return dfilter_dialogs;
288 g_assert_not_reached();
295 fill_list(GtkWidget *main_w, filter_list_type_t list_type, const gchar *filter_te_str)
299 GtkTreeView *filter_l;
302 GtkTreeIter *l_select = NULL;
304 filter_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY));
305 store = GTK_LIST_STORE(gtk_tree_view_get_model(filter_l));
308 fl_entry = get_filter_list_first(list_type);
309 while (fl_entry != NULL) {
310 filt = (filter_def *) fl_entry->data;
311 gtk_list_store_append(store, &iter);
312 gtk_list_store_set(store, &iter, 0, filt->name,
315 if (filter_te_str && filt->strval) {
316 if (strcmp(filter_te_str, filt->strval) == 0) {
318 * XXX - We're assuming that we can just copy a GtkTreeIter
319 * and use it later without any crashes. This may not be a
322 l_select = g_memdup(&iter, sizeof(iter));
326 fl_entry = fl_entry->next;
333 clear_list(GtkWidget *main_w) {
334 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
335 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l));
337 gtk_list_store_clear(GTK_LIST_STORE(model));
342 filter_dialog_new(GtkWidget *button, GtkWidget *parent_filter_te,
343 filter_list_type_t list_type, construct_args_t *construct_args)
345 GtkWidget *main_w, /* main window */
346 *main_vb, /* main container */
347 *bbox, /* button container */
348 *ok_bt, /* "OK" button */
349 *apply_bt, /* "Apply" button */
350 *save_bt, /* "Save" button */
351 *cancel_bt, /* "Cancel" button */
352 *help_bt; /* "Help" button */
353 GtkWidget *filter_vb, /* filter settings box */
372 GtkTooltips *tooltips;
373 static filter_list_type_t cfilter_list_type = CFILTER_EDITED_LIST;
374 static filter_list_type_t dfilter_list_type = DFILTER_EDITED_LIST;
375 filter_list_type_t *filter_list_type_p;
376 GList **filter_dialogs;
377 const gchar *filter_te_str = NULL;
379 GtkCellRenderer *renderer;
380 GtkTreeViewColumn *column;
381 GtkTreeSelection *sel;
382 GtkTreeIter *l_select;
383 gchar *list_name = NULL;
385 /* Get a pointer to a static variable holding the type of filter on
386 which we're working, so we can pass that pointer to callback
391 filter_dialogs = &cfilter_dialogs;
392 filter_list_type_p = &cfilter_list_type;
393 list_type = CFILTER_EDITED_LIST;
394 list_name = "Capture Filter";
398 filter_dialogs = &dfilter_dialogs;
399 filter_list_type_p = &dfilter_list_type;
400 list_type = DFILTER_EDITED_LIST;
401 list_name = "Display Filter";
405 g_assert_not_reached();
406 filter_dialogs = NULL;
407 filter_list_type_p = NULL;
411 tooltips = gtk_tooltips_new ();
413 main_w = dlg_conf_window_new(construct_args->title);
414 gtk_window_set_default_size(GTK_WINDOW(main_w), 400, 400);
415 g_object_set_data(G_OBJECT(main_w), E_FILT_CONSTRUCT_ARGS_KEY, construct_args);
417 main_vb = gtk_vbox_new(FALSE, 0);
418 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
419 gtk_container_add(GTK_CONTAINER(main_w), main_vb);
420 gtk_widget_show(main_vb);
422 /* Make sure everything is set up */
423 if (parent_filter_te)
424 filter_te_str = gtk_entry_get_text(GTK_ENTRY(parent_filter_te));
426 /* Container for each row of widgets */
427 filter_vb = gtk_vbox_new(FALSE, 0);
428 gtk_container_set_border_width(GTK_CONTAINER(filter_vb), 0);
429 gtk_container_add(GTK_CONTAINER(main_vb), filter_vb);
430 gtk_widget_show(filter_vb);
432 /* Top row: Buttons and filter list */
433 top_hb = gtk_hbox_new(FALSE, 0);
434 gtk_container_add(GTK_CONTAINER(filter_vb), top_hb);
435 gtk_widget_show(top_hb);
437 edit_fr = gtk_frame_new("Edit");
438 gtk_box_pack_start(GTK_BOX(top_hb), edit_fr, FALSE, FALSE, 0);
439 gtk_widget_show(edit_fr);
441 list_bb = gtk_vbox_new(TRUE, 0);
442 gtk_container_set_border_width(GTK_CONTAINER(list_bb), 5);
443 gtk_container_add(GTK_CONTAINER(edit_fr), list_bb);
444 gtk_widget_show(list_bb);
446 new_bt = gtk_button_new_from_stock(GTK_STOCK_NEW);
447 g_signal_connect(new_bt, "clicked", G_CALLBACK(filter_new_bt_clicked_cb), filter_list_type_p);
448 gtk_widget_show(new_bt);
449 gtk_box_pack_start (GTK_BOX (list_bb), new_bt, FALSE, FALSE, 0);
450 gtk_tooltips_set_tip (tooltips, new_bt,
451 "Create a new filter at the end of the list (with the current properties)", NULL);
453 del_bt = gtk_button_new_from_stock(GTK_STOCK_DELETE);
454 gtk_widget_set_sensitive(del_bt, FALSE);
455 g_signal_connect(del_bt, "clicked", G_CALLBACK(filter_del_bt_clicked_cb), filter_list_type_p);
456 g_object_set_data(G_OBJECT(main_w), E_FILT_DEL_BT_KEY, del_bt);
457 gtk_widget_show(del_bt);
458 gtk_box_pack_start (GTK_BOX (list_bb), del_bt, FALSE, FALSE, 0);
459 gtk_tooltips_set_tip (tooltips, del_bt, ("Delete the selected filter"), NULL);
461 filter_fr = gtk_frame_new(list_name);
462 gtk_box_pack_start(GTK_BOX(top_hb), filter_fr, TRUE, TRUE, 0);
463 gtk_widget_show(filter_fr);
465 filter_sc = scrolled_window_new(NULL, NULL);
466 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(filter_sc),
469 gtk_container_set_border_width (GTK_CONTAINER (filter_sc), 5);
470 gtk_container_add(GTK_CONTAINER(filter_fr), filter_sc);
471 gtk_widget_show(filter_sc);
473 store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
474 filter_l = tree_view_new(GTK_TREE_MODEL(store));
475 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(filter_l), FALSE);
476 renderer = gtk_cell_renderer_text_new();
477 column = gtk_tree_view_column_new_with_attributes("", renderer, "text",
479 gtk_tree_view_column_set_sort_column_id(column, 0);
480 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_l), column);
481 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
482 gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
483 g_signal_connect(sel, "changed", G_CALLBACK(filter_sel_list_cb), filter_vb);
484 g_signal_connect(filter_l, "button_press_event", G_CALLBACK(filter_sel_list_button_cb),
486 g_object_set_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY, filter_l);
487 gtk_container_add(GTK_CONTAINER(filter_sc), filter_l);
488 gtk_widget_show(filter_l);
490 g_object_set_data(G_OBJECT(filter_l), E_FILT_DBLFUNC_KEY, filter_dlg_dclick);
491 g_object_set_data(G_OBJECT(filter_l), E_FILT_DBLARG_KEY, main_w);
492 /* This is a Boolean, but we make it a non-null pointer for TRUE
493 and a null pointer for FALSE, as object data is a pointer. */
494 g_object_set_data(G_OBJECT(filter_l), E_FILT_DBLACTIVATE_KEY,
495 construct_args->activate_on_ok ? "" : NULL);
498 l_select = fill_list(main_w, list_type, filter_te_str);
500 g_object_unref(G_OBJECT(store));
503 props_fr = gtk_frame_new("Properties");
504 gtk_box_pack_start(GTK_BOX(filter_vb), props_fr, FALSE, FALSE, 0);
505 gtk_widget_show(props_fr);
507 props_vb = gtk_vbox_new(FALSE, 3);
508 gtk_container_set_border_width(GTK_CONTAINER(props_vb), 5);
509 gtk_container_add(GTK_CONTAINER(props_fr), props_vb);
510 gtk_widget_show(props_vb);
512 /* row: Filter name entry */
513 middle_hb = gtk_hbox_new(FALSE, 3);
514 gtk_container_add(GTK_CONTAINER(props_vb), middle_hb);
515 gtk_widget_show(middle_hb);
517 name_lb = gtk_label_new("Filter name:");
518 gtk_box_pack_start(GTK_BOX(middle_hb), name_lb, FALSE, FALSE, 0);
519 gtk_widget_show(name_lb);
521 name_te = gtk_entry_new();
522 gtk_box_pack_start(GTK_BOX(middle_hb), name_te, TRUE, TRUE, 0);
523 g_object_set_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY, name_te);
524 g_signal_connect(name_te, "changed", G_CALLBACK(filter_name_te_changed_cb), filter_list_type_p);
525 gtk_widget_show(name_te);
527 /* row: Filter text entry */
528 bottom_hb = gtk_hbox_new(FALSE, 3);
529 gtk_container_add(GTK_CONTAINER(props_vb), bottom_hb);
530 gtk_widget_show(bottom_hb);
532 filter_lb = gtk_label_new("Filter string:");
533 gtk_box_pack_start(GTK_BOX(bottom_hb), filter_lb, FALSE, FALSE, 0);
534 gtk_widget_show(filter_lb);
536 filter_te = gtk_entry_new();
537 gtk_box_pack_start(GTK_BOX(bottom_hb), filter_te, TRUE, TRUE, 0);
538 g_object_set_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY, filter_te);
539 g_signal_connect(filter_te, "changed", G_CALLBACK(filter_name_te_changed_cb), filter_list_type_p);
540 if (list_type == DFILTER_EDITED_LIST) {
541 colorize_filter_te_as_empty(filter_te);
543 g_object_set_data(G_OBJECT(main_w), E_FILT_AUTOCOMP_PTR_KEY, NULL);
544 g_signal_connect(filter_te, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
545 g_signal_connect(main_w, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
547 gtk_widget_show(filter_te);
549 g_object_set_data(G_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY, parent_filter_te);
551 if (list_type == DFILTER_EDITED_LIST) {
552 gtk_tooltips_set_tip(tooltips, filter_te,
553 "Enter a display filter. "
554 "The background color of this field is changed by a continuous syntax check"
555 " (green is valid, red is invalid, yellow may have unexpected results).",
558 /* Create the "Add Expression..." button, to pop up a dialog
559 for constructing filter comparison expressions. */
560 add_expression_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_ADD_EXPRESSION);
561 g_signal_connect(add_expression_bt, "clicked", G_CALLBACK(filter_add_expr_bt_cb), main_w);
562 gtk_box_pack_start(GTK_BOX(bottom_hb), add_expression_bt, FALSE, FALSE, 0);
563 gtk_widget_show(add_expression_bt);
564 gtk_tooltips_set_tip (tooltips, add_expression_bt, ("Add an expression to the filter string"), NULL);
568 /* button row (create all possible buttons and hide the unrequired later - it's a lot easier) */
569 bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_SAVE, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
570 gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
571 gtk_widget_show(bbox);
573 ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
574 g_signal_connect(ok_bt, "clicked", G_CALLBACK(filter_dlg_ok_cb), filter_list_type_p);
575 gtk_tooltips_set_tip (tooltips, ok_bt, ("Apply the filters and close this dialog"), NULL);
577 /* Catch the "activate" signal on the filter name and filter
578 expression text entries, so that if the user types Return
579 there, we act as if the "OK" button had been selected, as
580 happens if Return is typed if some widget that *doesn't*
581 handle the Return key has the input focus. */
582 if (parent_filter_te != NULL) {
583 dlg_set_activate(name_te, ok_bt);
584 dlg_set_activate(filter_te, ok_bt);
587 apply_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_APPLY);
588 g_signal_connect(apply_bt, "clicked", G_CALLBACK(filter_dlg_apply_cb), filter_list_type_p);
589 gtk_tooltips_set_tip (tooltips, apply_bt, ("Apply the filters and keep this dialog open"), NULL);
591 save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
592 g_signal_connect(save_bt, "clicked", G_CALLBACK(filter_dlg_save_cb), filter_list_type_p);
593 gtk_tooltips_set_tip (tooltips, save_bt, ("Save the filters permanently and keep this dialog open"), NULL);
595 cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
596 gtk_tooltips_set_tip (tooltips, cancel_bt, ("Cancel the changes"), NULL);
597 g_signal_connect(cancel_bt, "clicked", G_CALLBACK(filter_dlg_cancel_cb), filter_list_type_p);
598 window_set_cancel_button(main_w, cancel_bt, NULL);
600 help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
601 if (list_type == CFILTER_EDITED_LIST) {
602 g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_CAPTURE_FILTERS_DIALOG);
604 g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_DISPLAY_FILTERS_DIALOG);
606 gtk_tooltips_set_tip (tooltips, help_bt, ("Show topic specific help"), NULL);
609 gtk_widget_grab_default(ok_bt);
612 remember_filter_dialog(main_w, filter_dialogs);
614 if (button != NULL) {
615 /* This dialog box was created by a "Filter" button.
616 Set the E_FILT_BUTTON_PTR_KEY for the new dialog to point to
618 g_object_set_data(G_OBJECT(main_w), E_FILT_BUTTON_PTR_KEY, button);
620 /* Set the E_FILT_DIALOG_PTR_KEY for the button to point to us */
621 g_object_set_data(G_OBJECT(button), E_FILT_DIALOG_PTR_KEY, main_w);
624 /* DO SELECTION THINGS *AFTER* SHOWING THE DIALOG! */
625 /* otherwise the updatings can get confused */
627 gtk_tree_selection_select_iter(sel, l_select);
629 } else if (filter_te_str && filter_te_str[0]) {
630 gtk_entry_set_text(GTK_ENTRY(name_te), "New filter");
631 gtk_entry_set_text(GTK_ENTRY(filter_te), filter_te_str);
634 g_signal_connect(main_w, "delete_event", G_CALLBACK(filter_dlg_delete_event_cb), filter_list_type_p);
635 g_signal_connect(main_w, "destroy", G_CALLBACK(filter_dlg_destroy_cb), filter_list_type_p);
637 gtk_widget_show(main_w);
639 if(construct_args->modal_and_transient) {
640 parent = gtk_widget_get_parent_window(parent_filter_te);
641 gdk_window_set_transient_for(main_w->window, parent);
642 gtk_window_set_modal(GTK_WINDOW(main_w), TRUE);
645 /* hide the Ok button, if we don't have to apply it and our caller wants a Save button */
646 if (parent_filter_te == NULL && prefs.gui_use_pref_save) {
647 gtk_widget_hide(ok_bt);
650 /* hide the Apply button, if our caller don't wants one */
651 if (!construct_args->wants_apply_button) {
652 gtk_widget_hide(apply_bt);
655 /* hide the Save button if the user uses implicit save */
656 if (!prefs.gui_use_pref_save) {
657 gtk_widget_hide(save_bt);
660 window_present(main_w);
666 filter_dlg_dclick(GtkWidget *filter_l, gpointer main_w_arg, gpointer activate)
668 GtkWidget *main_w = GTK_WIDGET(main_w_arg);
669 GtkWidget *parent_filter_te =
670 g_object_get_data(G_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY);
673 GtkTreeSelection *sel;
677 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
679 if (parent_filter_te != NULL) {
681 * We have a text entry widget associated with this dialog
682 * box; is one of the filters in the list selected?
684 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
686 * Yes. Is there a filter definition for that filter?
688 gtk_tree_model_get(model, &iter, 1, &flp, -1);
691 * Yes - put it in the text entry widget.
693 filt = (filter_def *) flp->data;
694 gtk_entry_set_text(GTK_ENTRY(parent_filter_te),
698 * Are we supposed to cause the filter we
699 * put there to be applied?
701 if (activate != NULL) {
705 g_signal_emit_by_name(G_OBJECT(parent_filter_te), "activate", NULL);
711 window_destroy(main_w);
715 filter_dlg_ok_cb(GtkWidget *ok_bt, gpointer data)
717 filter_list_type_t list_type = *(filter_list_type_t *)data;
720 * Destroy the dialog box and apply the filter.
722 filter_apply(gtk_widget_get_toplevel(ok_bt), TRUE);
724 /* if we don't have a Save button, just save the settings now */
725 if (!prefs.gui_use_pref_save) {
726 filter_dlg_save(list_type);
731 filter_dlg_apply_cb(GtkWidget *apply_bt, gpointer data)
733 filter_list_type_t list_type = *(filter_list_type_t *)data;
736 * Apply the filter, but don't destroy the dialog box.
738 filter_apply(gtk_widget_get_toplevel(apply_bt), FALSE);
740 /* if we don't have a Save button, just save the settings now */
741 if (!prefs.gui_use_pref_save) {
742 filter_dlg_save(list_type);
747 filter_apply(GtkWidget *main_w, gboolean destroy)
749 construct_args_t *construct_args =
750 g_object_get_data(G_OBJECT(main_w), E_FILT_CONSTRUCT_ARGS_KEY);
751 GtkWidget *parent_filter_te =
752 g_object_get_data(G_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY);
753 GtkWidget *filter_te;
754 const gchar *filter_string;
756 if (parent_filter_te != NULL) {
758 * We have a text entry widget associated with this dialog
759 * box; put the filter in our text entry widget into that
762 filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
764 (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
765 gtk_entry_set_text(GTK_ENTRY(parent_filter_te), filter_string);
771 * Destroy the filter dialog box.
773 window_destroy(main_w);
776 if (parent_filter_te != NULL) {
778 * We have a text entry widget associated with this dialog
779 * box; activate that widget to cause the filter we put
780 * there to be applied if we're supposed to do so.
782 * We do this after dismissing the filter dialog box,
783 * as activating the widget the dialog box to which
784 * it belongs to be dismissed, and that may cause it
785 * to destroy our dialog box if the filter succeeds.
786 * This means that our subsequent attempt to destroy
789 * We don't know whether it'll destroy our dialog box,
790 * so we can't rely on it to do so. Instead, we
791 * destroy it ourselves, which will clear the
792 * E_FILT_DIALOG_PTR_KEY pointer for their dialog box,
793 * meaning they won't think it has one and won't try
796 if (construct_args->activate_on_ok) {
797 g_signal_emit_by_name(G_OBJECT(parent_filter_te), "activate", NULL);
804 filter_dlg_save(filter_list_type_t list_type)
809 const char *filter_type;
813 case CFILTER_EDITED_LIST:
814 filter_type = "capture";
815 list_type = CFILTER_LIST;
816 copy_filter_list(CFILTER_LIST, CFILTER_EDITED_LIST);
819 case DFILTER_EDITED_LIST:
820 filter_type = "display";
821 list_type = DFILTER_LIST;
822 copy_filter_list(DFILTER_LIST, DFILTER_EDITED_LIST);
826 g_assert_not_reached();
831 /* Create the directory that holds personal configuration files,
833 if (create_persconffile_dir(&pf_dir_path) == -1) {
834 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
835 "Can't create directory\n\"%s\"\nfor filter files: %s.",
836 pf_dir_path, strerror(errno));
841 save_filter_list(list_type, &f_path, &f_save_errno);
842 if (f_path != NULL) {
843 /* We had an error saving the filter. */
844 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
845 "Could not save to your %s filter file\n\"%s\": %s.",
846 filter_type, f_path, strerror(f_save_errno));
853 filter_dlg_save_cb(GtkWidget *save_bt _U_, gpointer data)
855 filter_list_type_t list_type = *(filter_list_type_t *)data;
857 filter_dlg_save(list_type);
861 /* update a remaining dialog if another one was cancelled */
862 static void filter_dlg_update_list_cb(gpointer data, gpointer user_data)
864 GtkWidget *main_w = data;
865 filter_list_type_t list_type = *(filter_list_type_t *)user_data;
867 /* refill the list */
869 fill_list(main_w, list_type, NULL);
873 /* cancel button pressed, revert changes and exit dialog */
875 filter_dlg_cancel_cb(GtkWidget *cancel_bt, gpointer data)
877 filter_list_type_t list_type = *(filter_list_type_t *)data;
878 GtkWidget *main_w = gtk_widget_get_toplevel(cancel_bt);
879 static GList *filter_list;
882 window_destroy(GTK_WIDGET(main_w));
884 /* if this was the last open filter dialog, revert the changes made */
885 filter_list = get_filter_dialog_list(list_type);
886 if(g_list_length(filter_list) == 0) {
887 /* revert changes in the edited list */
889 case CFILTER_EDITED_LIST:
890 copy_filter_list(CFILTER_EDITED_LIST, CFILTER_LIST);
892 case DFILTER_EDITED_LIST:
893 copy_filter_list(DFILTER_EDITED_LIST, DFILTER_LIST);
896 g_assert_not_reached();
902 /* update other open filter dialogs */
903 g_list_foreach(get_filter_dialog_list(list_type), filter_dlg_update_list_cb, &list_type);
907 /* Treat this as a cancel, by calling "filter_dlg_cancel_cb()" */
909 filter_dlg_delete_event_cb(GtkWidget *main_w, GdkEvent *event _U_,
912 filter_dlg_cancel_cb(main_w, data);
918 filter_dlg_destroy_cb(GtkWidget *win, gpointer data)
920 filter_list_type_t list_type = *(filter_list_type_t *)data;
923 /* Get the button that requested that we be popped up, if any.
924 (It should arrange to destroy us if it's destroyed, so
925 that we don't get a pointer to a non-existent window here.) */
926 button = g_object_get_data(G_OBJECT(win), E_FILT_BUTTON_PTR_KEY);
928 if (button != NULL) {
929 /* Tell it we no longer exist. */
930 g_object_set_data(G_OBJECT(button), E_FILT_DIALOG_PTR_KEY, NULL);
932 /* This is an editing dialog popped up from, for example,
933 a menu item; note that we no longer have one. */
937 case CFILTER_EDITED_LIST:
938 g_assert(win == global_cfilter_w);
939 global_cfilter_w = NULL;
943 g_assert_not_reached();
948 /* Remove this from the list of filter dialog windows. */
949 forget_filter_dialog(win, list_type);
953 filter_sel_list_button_cb(GtkWidget *list, GdkEventButton *event,
956 void (* func)(GtkWidget *, gpointer, gpointer);
958 gpointer func_activate;
960 if (event->type == GDK_2BUTTON_PRESS) {
961 func = g_object_get_data(G_OBJECT(list), E_FILT_DBLFUNC_KEY);
962 func_arg = g_object_get_data(G_OBJECT(list), E_FILT_DBLARG_KEY);
963 func_activate = g_object_get_data(G_OBJECT(list), E_FILT_DBLACTIVATE_KEY);
966 (*func)(list, func_arg, func_activate);
973 filter_sel_list_cb(GtkTreeSelection *sel, gpointer data _U_)
975 GtkWidget *filter_l = GTK_WIDGET(gtk_tree_selection_get_tree_view(sel));
976 GtkWidget *main_w = gtk_widget_get_toplevel(filter_l);
979 GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY);
980 GtkWidget *filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
981 GtkWidget *chg_bt = g_object_get_data(G_OBJECT(main_w), E_FILT_CHG_BT_KEY);
982 GtkWidget *copy_bt = g_object_get_data(G_OBJECT(main_w), E_FILT_COPY_BT_KEY);
983 GtkWidget *del_bt = g_object_get_data(G_OBJECT(main_w), E_FILT_DEL_BT_KEY);
985 gchar *name = NULL, *strval = NULL;
987 gint sensitivity = FALSE;
989 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
990 gtk_tree_model_get(model, &iter, 1, &flp, -1);
992 filt = (filter_def *) flp->data;
993 name = g_strdup(filt->name);
994 strval = g_strdup(filt->strval);
1000 * Did you know that this function is called when the window is destroyed?
1002 * This means that we have to:
1004 * attach to the top-level window data items containing pointers to
1005 * the widgets we affect here;
1007 * give each of those widgets their own destroy callbacks;
1009 * clear that pointer when the widget is destroyed;
1011 * don't do anything to the widget if the pointer we get back is
1014 * so that if we're called after any of the widgets we'd affect are
1015 * destroyed, we know that we shouldn't do anything to those widgets.
1017 if (name_te != NULL)
1018 gtk_entry_set_text(GTK_ENTRY(name_te), name ? name : "");
1019 if (filter_te != NULL)
1020 gtk_entry_set_text(GTK_ENTRY(filter_te), strval ? strval : "");
1022 gtk_widget_set_sensitive(chg_bt, sensitivity);
1023 if (copy_bt != NULL)
1024 gtk_widget_set_sensitive(copy_bt, sensitivity);
1026 gtk_widget_set_sensitive(del_bt, sensitivity);
1031 /* To do: add input checking to each of these callbacks */
1033 /* Structure containing arguments to be passed to "new_filter_cb()".
1035 "active_filter_l" is the list in the dialog box in which "New" or
1036 "Copy" was clicked; in that dialog box, but not in any other dialog
1037 box, we select the newly created list item.
1039 "nflp" is the GList member in the model (filter list) for the new
1042 GtkWidget *active_filter_l;
1044 } new_filter_cb_args_t;
1047 new_filter_cb(gpointer data, gpointer user_data)
1049 GtkWidget *main_w = data;
1050 GtkTreeView *filter_l;
1051 GtkListStore *store;
1053 new_filter_cb_args_t *args = user_data;
1054 filter_def *nfilt = args->nflp->data;
1056 filter_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY));
1057 store = GTK_LIST_STORE(gtk_tree_view_get_model(filter_l));
1058 gtk_list_store_append(store, &iter);
1059 gtk_list_store_set(store, &iter, 0, nfilt->name, 1, args->nflp, -1);
1060 if (GTK_WIDGET(filter_l) == args->active_filter_l) {
1061 /* Select the item. */
1062 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(filter_l),
1068 filter_new_bt_clicked_cb(GtkWidget *w, gpointer data)
1070 GtkWidget *main_w = gtk_widget_get_toplevel(w);
1071 GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY);
1072 GtkWidget *filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
1073 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1074 filter_list_type_t list_type = *(filter_list_type_t *)data;
1076 const gchar *name, *strval;
1077 new_filter_cb_args_t args;
1079 name = gtk_entry_get_text(GTK_ENTRY(name_te));
1080 strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
1082 /* if the user didn't entered a name, set default one */
1083 if (strlen(name) == 0) {
1087 /* if the user didn't entered a string value, set default one */
1088 if (strlen(strval) == 0) {
1092 /* Add a new entry to the filter list. */
1093 fl_entry = add_to_filter_list(list_type, name, strval);
1095 /* Update all the filter list widgets, not just the one in
1096 the dialog box in which we clicked on "Copy". */
1097 args.active_filter_l = filter_l;
1098 args.nflp = fl_entry;
1099 g_list_foreach(get_filter_dialog_list(list_type), new_filter_cb, &args);
1104 chg_list_item_cb(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
1108 filter_def *filt = flp->data;
1111 gtk_tree_model_get(model, iter, 1, &nl_model, -1);
1112 /* Is this the item corresponding to the filter list item in question? */
1113 if (flp == nl_model) {
1114 /* Yes - change the label to correspond to the new name for the
1116 gtk_list_store_set(GTK_LIST_STORE(model), iter, 0, filt->name, -1);
1123 chg_filter_cb(gpointer data, gpointer user_data)
1125 GtkWidget *main_w = data;
1126 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1128 gtk_tree_model_foreach(gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l)),
1129 chg_list_item_cb, user_data);
1133 filter_name_te_changed_cb(GtkWidget *w, gpointer data)
1135 GtkWidget *main_w = gtk_widget_get_toplevel(w);
1136 GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY);
1137 GtkWidget *filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
1138 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1141 filter_list_type_t list_type = *(filter_list_type_t *)data;
1142 const gchar *name = "";
1143 const gchar *strval = "";
1145 GtkTreeSelection *sel;
1146 GtkTreeModel *model;
1149 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
1150 name = gtk_entry_get_text(GTK_ENTRY(name_te));
1151 strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
1153 if (DFILTER_EDITED_LIST == list_type) {
1154 /* colorize filter string entry */
1155 filter_te_syntax_check_cb(filter_te);
1158 /* if something was selected */
1159 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1160 gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
1161 if (fl_entry != NULL) {
1162 filt = (filter_def *) fl_entry->data;
1164 if (strlen(name) > 0 && strlen(strval) > 0 && filt) {
1166 g_free(filt->strval);
1167 filt->name = g_strdup(name);
1168 filt->strval = g_strdup(strval);
1170 /* Update all the filter list widgets, not just the one in
1171 the dialog box in which we clicked on "Copy". */
1172 g_list_foreach(get_filter_dialog_list(list_type), chg_filter_cb,
1180 delete_filter_cb(gpointer data, gpointer user_data)
1182 GtkWidget *main_w = data;
1183 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1184 gchar *pos = (gchar *)user_data;
1185 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l));
1188 gtk_tree_model_get_iter_from_string(model, &iter, pos);
1189 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
1193 filter_del_bt_clicked_cb(GtkWidget *w, gpointer data)
1195 GtkWidget *main_w = gtk_widget_get_toplevel(w);
1196 GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
1197 filter_list_type_t list_type = *(filter_list_type_t *)data;
1200 GtkTreeSelection *sel;
1201 GtkTreeModel *model;
1205 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
1206 /* If something was selected */
1207 if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
1208 gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
1209 path = gtk_tree_model_get_path(model, &iter);
1210 pos = gtk_tree_path_to_string(path);
1211 gtk_tree_path_free(path);
1212 if (fl_entry != NULL) {
1213 /* Remove the entry from the filter list. */
1214 remove_from_filter_list(list_type, fl_entry);
1216 /* Update all the filter list widgets, not just the one in
1217 the dialog box in which we clicked on "Delete". */
1218 g_list_foreach(get_filter_dialog_list(list_type), delete_filter_cb, pos);
1225 filter_add_expr_bt_cb(GtkWidget *w _U_, gpointer main_w_arg)
1227 GtkWidget *main_w = GTK_WIDGET(main_w_arg);
1228 GtkWidget *filter_te, *dfilter_w;
1230 filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
1231 dfilter_w = dfilter_expr_dlg_new(filter_te);
1233 /* If we're opening a series of modal dialogs (such as when going
1234 * through file->open, make the latest dialog modal also so that it
1235 * takes over "control" from the other modal dialogs. Also set
1236 * the transient property of the new dialog so the user doesn't try
1237 * to interact with the previous window when they can't.
1238 * XXX: containing widget might be the Filter Toolbar */
1240 if ( GTK_IS_WINDOW(main_w) && gtk_window_get_modal(GTK_WINDOW(main_w))) {
1241 gtk_window_set_modal(GTK_WINDOW(dfilter_w), TRUE);
1242 gtk_window_set_transient_for(GTK_WINDOW(dfilter_w),
1243 GTK_WINDOW(main_w));
1248 color_filter_te(GtkWidget *w, guint16 red, guint16 green, guint16 blue)
1250 static GdkColor black = { 0, 0, 0, 0 };
1258 gtk_widget_modify_text(w, GTK_STATE_NORMAL, &black);
1259 gtk_widget_modify_base(w, GTK_STATE_NORMAL, &bg);
1260 #if GTK_CHECK_VERSION(2,12,0)
1261 gtk_widget_modify_cursor(w, &black, &black);
1266 colorize_filter_te_as_empty(GtkWidget *w)
1269 gtk_widget_modify_text(w, GTK_STATE_NORMAL, NULL);
1270 gtk_widget_modify_base(w, GTK_STATE_NORMAL, NULL);
1271 #if GTK_CHECK_VERSION(2,12,0)
1272 gtk_widget_modify_cursor(w, NULL, NULL);
1277 colorize_filter_te_as_invalid(GtkWidget *w)
1280 color_filter_te(w, 0xFFFF, 0xAFFF, 0xAFFF);
1284 colorize_filter_te_as_deprecated(GtkWidget *w)
1287 color_filter_te(w, 0xFFFF, 0xFFFF, 0xAFFF);
1291 colorize_filter_te_as_valid(GtkWidget *w)
1294 color_filter_te(w, 0xAFFF, 0xFFFF, 0xAFFF);
1298 * XXX This calls dfilter_compile, which might call get_host_ipaddr or
1299 * get_host_ipaddr6. Either of of these will freeze the UI if the host
1300 * name resolution takes a long time to complete. We need to work
1301 * around this, either by disabling host name resolution or by doing
1302 * the resolution asynchronously.
1304 * We could use a separate thread but we have be careful to only call
1305 * GTK+/GDK routines from the main thread. From the GDK threads
1308 * "With the Win32 backend, GDK calls should not be attempted from
1309 * multiple threads at all."
1313 filter_te_syntax_check_cb(GtkWidget *w)
1315 const gchar *strval;
1317 GPtrArray *depr = NULL;
1318 gboolean use_statusbar;
1322 strval = gtk_entry_get_text(GTK_ENTRY(w));
1323 use_statusbar = g_object_get_data(G_OBJECT(w), E_FILT_FIELD_USE_STATUSBAR_KEY) ? TRUE : FALSE;
1325 if (use_statusbar) {
1326 statusbar_pop_filter_msg();
1329 /* colorize filter string entry */
1330 if (g_object_get_data(G_OBJECT(w), E_FILT_FIELD_NAME_ONLY_KEY) &&
1331 strval && (c = proto_check_field_name(strval)) != 0)
1333 colorize_filter_te_as_invalid(w);
1334 if (use_statusbar) {
1335 msg = g_strdup_printf(" Illegal character in field name: '%c'", c);
1336 statusbar_push_filter_msg(msg);
1339 } else if (strval && dfilter_compile(strval, &dfp)) {
1341 depr = dfilter_deprecated_tokens(dfp);
1343 if (strlen(strval) == 0) {
1344 colorize_filter_te_as_empty(w);
1346 /* You keep using that word. I do not think it means what you think it means. */
1347 colorize_filter_te_as_deprecated(w);
1348 if (use_statusbar) {
1350 * We're being lazy and only printing the first "problem" token.
1351 * Would it be better to print all of them?
1353 msg = g_strdup_printf(" \"%s\" may have unexpected results (see the User's Guide)",
1354 (const char *) g_ptr_array_index(depr, 0));
1355 statusbar_push_temporary_msg(msg);
1359 colorize_filter_te_as_valid(w);
1363 colorize_filter_te_as_invalid(w);
1364 if (use_statusbar) {
1365 if (dfilter_error_msg) {
1366 msg = g_strdup_printf(" Invalid filter: %s", dfilter_error_msg);
1367 statusbar_push_filter_msg(msg);
1370 statusbar_push_filter_msg(" Invalid filter");
1382 * indent-tabs-mode: nil
1385 * ex: set shiftwidth=4 tabstop=8 expandtab
1386 * :indentSize=4:tabSize=8:noTabs=true: